diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 1e056f670a..761b1eb4ec 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -150,9 +150,10 @@ namespace MediaBrowser.Api.Playback.Hls { var text = reader.ReadToEnd(); + text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT"); + var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture); - // ffmpeg pads the reported length by a full second text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase); return text; diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d6ccdd1fda..109aa85de4 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -73,6 +73,10 @@ namespace MediaBrowser.Api.Playback { get { + if (!RunTimeTicks.HasValue) + { + return 6; + } if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var userAgent = UserAgent ?? string.Empty; @@ -88,13 +92,13 @@ namespace MediaBrowser.Api.Playback return 10; } - if (!RunTimeTicks.HasValue) - { - return 10; - } return 6; } + if (!RunTimeTicks.HasValue) + { + return 6; + } return 3; } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2a49168ed7..6f358dfb44 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -254,6 +254,16 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public string ExternalSeriesId + { + get { return this.GetProviderId("ProviderExternalSeriesId"); } + set + { + this.SetProviderId("ProviderExternalSeriesId", value); + } + } + /// /// Gets or sets the etag. /// diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3d7b4abef4..b508110cf9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -763,7 +763,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new ApplicationException("Tuner not found."); } - private async Task> GetChannelStreamInternal(string channelId, CancellationToken cancellationToken) + private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -771,7 +771,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { - var result = await hostInstance.GetChannelStream(channelId, null, cancellationToken).ConfigureAwait(false); + var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); return new Tuple(result.Item1, hostInstance, result.Item2); } @@ -994,7 +994,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - var result = await GetChannelStreamInternal(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); + var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false); isResourceOpen = true; semaphore = result.Item3; var mediaStreamInfo = result.Item1; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 65d023e889..df1ef39624 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -668,6 +668,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.EpisodeTitle = info.EpisodeTitle; item.ExternalId = info.Id; + item.ExternalSeriesId = info.SeriesId; item.Genres = info.Genres; item.IsHD = info.IsHD; item.IsKids = info.IsKids; @@ -903,8 +904,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user); - var list = new List>(); - list.Add(new Tuple(dto, program.ServiceName, program.ExternalId)); + var list = new List>(); + list.Add(new Tuple(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesId)); await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false); @@ -1092,15 +1093,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv return score; } - private async Task AddRecordingInfo(IEnumerable> programs, CancellationToken cancellationToken) + private async Task AddRecordingInfo(IEnumerable> programs, CancellationToken cancellationToken) { var timers = new Dictionary>(); + var seriesTimers = new Dictionary>(); foreach (var programTuple in programs) { var program = programTuple.Item1; var serviceName = programTuple.Item2; var externalProgramId = programTuple.Item3; + string externalSeriesId = programTuple.Item4; if (string.IsNullOrWhiteSpace(serviceName)) { @@ -1123,6 +1126,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase)); + var foundSeriesTimer = false; if (timer != null) { @@ -1133,8 +1137,38 @@ namespace MediaBrowser.Server.Implementations.LiveTv { program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, timer.SeriesTimerId) .ToString("N"); + + foundSeriesTimer = true; } } + + if (foundSeriesTimer || string.IsNullOrWhiteSpace(externalSeriesId)) + { + continue; + } + + List seriesTimerList; + if (!seriesTimers.TryGetValue(serviceName, out seriesTimerList)) + { + try + { + var tempTimers = await GetService(serviceName).GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); + seriesTimers[serviceName] = seriesTimerList = tempTimers.ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting series timer infos", ex); + seriesTimers[serviceName] = seriesTimerList = new List(); + } + } + + var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase)); + + if (seriesTimer != null) + { + program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, seriesTimer.Id) + .ToString("N"); + } } } @@ -1659,7 +1693,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv public async Task AddInfoToProgramDto(List> tuples, List fields, User user = null) { - var recordingTuples = new List>(); + var recordingTuples = new List>(); foreach (var tuple in tuples) { @@ -1727,7 +1761,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.ServiceName = serviceName; } - recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId)); + recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId, program.ExternalSeriesId)); } await AddRecordingInfo(recordingTuples, CancellationToken.None).ConfigureAwait(false);