From ca0c0bdcb921cd662b2871f0f788bbec77f822d9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 19 Mar 2015 12:16:33 -0400 Subject: [PATCH] use stream id's to close streams --- MediaBrowser.Api/ApiEntryPoint.cs | 27 ++++-- .../Playback/BaseStreamingService.cs | 5 +- .../Playback/Dash/MpegDashService.cs | 7 +- .../Playback/Hls/DynamicHlsService.cs | 2 +- .../Playback/Hls/HlsSegmentService.cs | 5 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 3 +- MediaBrowser.Api/Playback/StreamRequest.cs | 1 + .../LiveTv/LiveTvManager.cs | 84 ++++++++++++------- 8 files changed, 89 insertions(+), 45 deletions(-) diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index ef415ec57c..281f764b5f 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -132,6 +132,7 @@ namespace MediaBrowser.Api /// Called when [transcode beginning]. /// /// The path. + /// The stream identifier. /// The transcoding job identifier. /// The type. /// The process. @@ -140,6 +141,7 @@ namespace MediaBrowser.Api /// The cancellation token source. /// TranscodingJob. public TranscodingJob OnTranscodeBeginning(string path, + string streamId, string transcodingJobId, TranscodingJobType type, Process process, @@ -157,7 +159,8 @@ namespace MediaBrowser.Api ActiveRequestCount = 1, DeviceId = deviceId, CancellationTokenSource = cancellationTokenSource, - Id = transcodingJobId + Id = transcodingJobId, + StreamId = streamId }; _activeTranscodingJobs.Add(job); @@ -316,17 +319,26 @@ namespace MediaBrowser.Api /// Kills the single transcoding job. /// /// The device id. + /// The stream identifier. /// The delete files. /// Task. - /// deviceId - internal void KillTranscodingJobs(string deviceId, Func deleteFiles) + internal void KillTranscodingJobs(string deviceId, string streamId, Func deleteFiles) { if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException("deviceId"); } - KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles); + KillTranscodingJobs(j => + { + if (string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)) + { + return string.IsNullOrWhiteSpace(streamId) || string.Equals(streamId, j.StreamId, StringComparison.OrdinalIgnoreCase); + } + + return false; + + }, deleteFiles); } /// @@ -335,7 +347,7 @@ namespace MediaBrowser.Api /// The kill job. /// The delete files. /// Task. - internal void KillTranscodingJobs(Func killJob, Func deleteFiles) + private void KillTranscodingJobs(Func killJob, Func deleteFiles) { var jobs = new List(); @@ -516,6 +528,11 @@ namespace MediaBrowser.Api /// public class TranscodingJob { + /// + /// Gets or sets the stream identifier. + /// + /// The stream identifier. + public string StreamId { get; set; } /// /// Gets or sets the path. /// diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 3a10881821..7115ddffdb 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false); data += "-" + (state.Request.DeviceId ?? string.Empty); - data += "-" + (state.Request.ClientTime ?? string.Empty); + data += "-" + (state.Request.StreamId ?? state.Request.ClientTime ?? string.Empty); var dataHash = data.GetMD5().ToString("N"); @@ -1054,6 +1054,7 @@ namespace MediaBrowser.Api.Playback } var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, + state.Request.StreamId ?? state.Request.ClientTime, transcodingId, TranscodingJobType, process, @@ -1523,7 +1524,7 @@ namespace MediaBrowser.Api.Playback } else if (i == 16) { - request.ClientTime = val; + request.StreamId = val; } else if (i == 17) { diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs index 2655ff8ad1..38b0d35d11 100644 --- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs @@ -162,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Dash // If the playlist doesn't already exist, startup ffmpeg try { - KillTranscodingJobs(request.DeviceId, playlistPath); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, p => false); if (currentTranscodingIndex.HasValue) { @@ -204,11 +204,6 @@ namespace MediaBrowser.Api.Playback.Dash return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType), cancellationToken).ConfigureAwait(false); } - private void KillTranscodingJobs(string deviceId, string playlistPath) - { - ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase)); - } - private long GetPositionTicks(StreamState state, int requestedIndex) { if (requestedIndex <= 0) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 32bbda8dbc..43a9db1312 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -136,7 +136,7 @@ namespace MediaBrowser.Api.Playback.Hls // If the playlist doesn't already exist, startup ffmpeg try { - ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase)); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId ?? request.ClientTime, p => false); if (currentTranscodingIndex.HasValue) { diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 9f80fcd0ad..da4ffb55dc 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -47,6 +47,9 @@ namespace MediaBrowser.Api.Playback.Hls { [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] public string DeviceId { get; set; } + + [ApiMember(Name = "StreamId", Description = "The stream id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] + public string StreamId { get; set; } } public class HlsSegmentService : BaseApiService @@ -69,7 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls public void Delete(StopEncodingProcess request) { - ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, path => true); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, path => true); } /// diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 5adb76cc58..943d9fe481 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Library; +using System; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 907b17b244..b52260b506 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -71,6 +71,7 @@ namespace MediaBrowser.Api.Playback public string Params { get; set; } public string ClientTime { get; set; } + public string StreamId { get; set; } public string TranscodingJobId { get; set; } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 41d8604de1..b383bbb87b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; using MediaBrowser.Common.Progress; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Channels; @@ -24,7 +23,6 @@ using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -747,8 +745,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv programs = programs.Where(p => p.IsMovie == query.IsMovie.Value); } - var serviceName = ActiveService.Name; - var programList = programs.ToList(); var genres = programList.SelectMany(i => i.Genres) @@ -757,7 +753,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); programs = programList.OrderBy(i => i.HasImage(ImageType.Primary) ? 0 : 1) - .ThenByDescending(i => GetRecommendationScore(i, user.Id, serviceName, genres)) + .ThenByDescending(i => GetRecommendationScore(i, user.Id, genres)) .ThenBy(i => i.StartDate); if (query.Limit.HasValue) @@ -807,7 +803,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv return result; } - private int GetRecommendationScore(LiveTvProgram program, Guid userId, string serviceName, Dictionary genres) + private int GetRecommendationScore(LiveTvProgram program, Guid userId, Dictionary genres) { var score = 0; @@ -821,7 +817,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv score++; } - var internalChannelId = _tvDtoService.GetInternalChannelId(serviceName, program.ExternalChannelId); + var internalChannelId = _tvDtoService.GetInternalChannelId(program.ServiceName, program.ExternalChannelId); var channel = GetInternalChannel(internalChannelId); var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey()); @@ -879,11 +875,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv private async Task AddRecordingInfo(IEnumerable programs, CancellationToken cancellationToken) { - var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false); - var timerList = timers.ToList(); + var timers = new Dictionary>(); foreach (var program in programs) { + List timerList; + if (!timers.TryGetValue(program.ServiceName, out timerList)) + { + var tempTimers = await GetService(program.ServiceName).GetTimersAsync(cancellationToken).ConfigureAwait(false); + timers[program.ServiceName] = timerList = tempTimers.ToList(); + } + var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, program.ExternalId, StringComparison.OrdinalIgnoreCase)); if (timer != null) @@ -922,15 +924,43 @@ namespace MediaBrowser.Server.Implementations.LiveTv private async Task RefreshChannelsInternal(IProgress progress, CancellationToken cancellationToken) { - // Avoid implicitly captured closure - var service = ActiveService; + var numComplete = 0; + double progressPerService = _services.Count == 0 + ? 0 + : 1 / _services.Count; - if (service == null) + foreach (var service in _services) { - progress.Report(100); - return; + cancellationToken.ThrowIfCancellationRequested(); + + try + { + var innerProgress = new ActionableProgress(); + innerProgress.RegisterAction(p => progress.Report(p * progressPerService)); + + await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + _logger.ErrorException("Error refreshing channels for service", ex); + } + + numComplete++; + double percent = numComplete; + percent /= _services.Count; + + progress.Report(100 * percent); } + progress.Report(100); + } + + private async Task RefreshChannelsInternal(ILiveTvService service, IProgress progress, CancellationToken cancellationToken) + { progress.Report(10); var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false); @@ -1192,16 +1222,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv public async Task> GetRecordings(RecordingQuery query, CancellationToken cancellationToken) { - var service = ActiveService; - - if (service == null) - { - return new QueryResult - { - Items = new RecordingInfoDto[] { } - }; - } - var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false); @@ -1209,6 +1229,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv var returnArray = internalResult.Items.Cast() .Select(i => { + var service = GetService(i); + var channel = string.IsNullOrEmpty(i.RecordingInfo.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, i.RecordingInfo.ChannelId)); return _tvDtoService.GetRecordingInfoDto(i, channel, service, user); }) @@ -1413,8 +1435,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv return program; } - private async Task GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null) + private async Task> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null) { + var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ? + GetService(program) : + _services.FirstOrDefault(); + ProgramInfo programInfo = null; if (program != null) @@ -1448,18 +1474,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } - var info = await ActiveService.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); + var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); info.Id = null; - return info; + return new Tuple(info, service); } public async Task GetNewTimerDefaults(CancellationToken cancellationToken) { var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false); - var obj = _tvDtoService.GetSeriesTimerInfoDto(info, ActiveService, null); + var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null); return obj; } @@ -1470,7 +1496,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false); var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false); - var info = _tvDtoService.GetSeriesTimerInfoDto(defaults, ActiveService, null); + var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null); info.Days = new List {