diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4db70df9c0..9d2c8c4448 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -214,7 +214,7 @@ namespace MediaBrowser.Api.Playback args += " -map -0:a"; } - if (state.SubtitleStream == null) + if (state.SubtitleStream == null || state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Hls) { args += " -map -0:s"; } @@ -477,7 +477,7 @@ namespace MediaBrowser.Api.Playback var pts = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && !state.VideoRequest.CopyTimestamps) + if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode && !state.VideoRequest.CopyTimestamps) { var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds; @@ -575,7 +575,7 @@ namespace MediaBrowser.Api.Playback var output = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream) + if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode) { var subParam = GetTextSubtitleParam(state); @@ -865,7 +865,7 @@ namespace MediaBrowser.Api.Playback { var arg = string.Format("-i {0}", GetInputPathArgument(state)); - if (state.SubtitleStream != null) + if (state.SubtitleStream != null && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode) { if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index e3f6e15e20..000c022567 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -529,6 +529,11 @@ namespace MediaBrowser.Api.Playback.Hls "subs" : null; + if (!string.IsNullOrWhiteSpace(subtitleGroup)) + { + AddSubtitles(state, subtitleStreams, builder); + } + AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); if (EnableAdaptiveBitrateStreaming(state, isLiveStream)) @@ -548,11 +553,6 @@ namespace MediaBrowser.Api.Playback.Hls AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); } - if (!string.IsNullOrWhiteSpace(subtitleGroup)) - { - AddSubtitles(state, subtitleStreams, builder); - } - return builder.ToString(); } @@ -566,11 +566,11 @@ namespace MediaBrowser.Api.Playback.Hls private void AddSubtitles(StreamState state, IEnumerable subtitles, StringBuilder builder) { - var selectedIndex = state.SubtitleStream == null ? (int?)null : state.SubtitleStream.Index; + var selectedIndex = state.SubtitleStream == null || state.VideoRequest.SubtitleMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index; foreach (var stream in subtitles) { - const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},URI=\"{3}\",LANGUAGE=\"{4}\""; + const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\""; var name = stream.Language; @@ -579,10 +579,11 @@ namespace MediaBrowser.Api.Playback.Hls if (string.IsNullOrWhiteSpace(name)) name = stream.Codec ?? "Unknown"; - var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}", + var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}", state.Request.MediaSourceId, stream.Index.ToString(UsCulture), - 30.ToString(UsCulture)); + 30.ToString(UsCulture), + AuthorizationContext.GetAuthorizationInfo(Request).Token); var line = string.Format(format, name, @@ -827,7 +828,7 @@ namespace MediaBrowser.Api.Playback.Hls var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"", state.SegmentLength.ToString(UsCulture)); - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode; args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg; @@ -853,7 +854,7 @@ namespace MediaBrowser.Api.Playback.Hls private bool EnableCopyTs(StreamState state) { - return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream; + return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode; } protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 22c38009a6..f8adbdc227 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -9,6 +9,7 @@ using MediaBrowser.Model.Serialization; using ServiceStack; using System; using CommonIO; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.Api.Playback.Hls { @@ -104,7 +105,7 @@ namespace MediaBrowser.Api.Playback.Hls var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"", state.SegmentLength.ToString(UsCulture)); - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode; args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index b7e180eca0..1353d8fe73 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -13,6 +13,7 @@ using System; using System.Globalization; using System.IO; using CommonIO; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.Api.Playback.Progressive { @@ -161,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Progressive args += keyFrameArg; - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode; var hasCopyTs = false; // Add resolution params, if specified diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 37034751d8..ff17e988ea 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -164,6 +164,8 @@ namespace MediaBrowser.Api.Subtitles long positionTicks = 0; var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks; + var accessToken = AuthorizationContext.GetAuthorizationInfo(Request).Token; + while (positionTicks < runtime) { var remaining = runtime - positionTicks; @@ -173,9 +175,10 @@ namespace MediaBrowser.Api.Subtitles var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks); - var url = string.Format("stream.srt?StartPositionTicks={0}&EndPositionTicks={1}", + var url = string.Format("stream.vtt?StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}", positionTicks.ToString(CultureInfo.InvariantCulture), - endPositionTicks.ToString(CultureInfo.InvariantCulture)); + endPositionTicks.ToString(CultureInfo.InvariantCulture), + accessToken); builder.AppendLine(url); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3dfbdec569..bd4ede4668 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1835,8 +1835,8 @@ namespace MediaBrowser.Controller.Entities ProviderIds = ProviderIds, IndexNumber = IndexNumber, ParentIndexNumber = ParentIndexNumber, - Year = ProductionYear, - PremiereDate = PremiereDate + Year = ProductionYear, + PremiereDate = PremiereDate }; } @@ -1985,5 +1985,14 @@ namespace MediaBrowser.Controller.Entities { return LibraryManager.DeleteItem(this, options); } + + public virtual Task OnFileDeleted() + { + // Remove from database + return Delete(new DeleteOptions + { + DeleteFileLocation = false + }); + } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 501e48a74b..5dffb5e9b2 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -390,5 +390,12 @@ namespace MediaBrowser.Controller.LiveTv /// The options. /// The user. void AddChannelInfo(BaseItemDto dto, LiveTvChannel channel, DtoOptions options, User user); + + /// + /// Called when [recording file deleted]. + /// + /// The recording. + /// Task. + Task OnRecordingFileDeleted(ILiveTvRecording recording); } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index c3d843f85a..6f72ae1fb3 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -151,5 +151,10 @@ namespace MediaBrowser.Controller.LiveTv { return LiveTvManager.DeleteRecording(this); } + + public override Task OnFileDeleted() + { + return LiveTvManager.OnRecordingFileDeleted(this); + } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 5492a29f30..ce0c69dee3 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -166,5 +166,10 @@ namespace MediaBrowser.Controller.LiveTv { return LiveTvManager.DeleteRecording(this); } + + public override Task OnFileDeleted() + { + return LiveTvManager.OnRecordingFileDeleted(this); + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index f8f4e9ec9f..ddaf7ff6d2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -90,8 +90,7 @@ namespace MediaBrowser.Controller.MediaEncoding Cabac = info.Cabac; Context = info.Context; - if (info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode || - info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed) + if (info.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External) { SubtitleStreamIndex = info.SubtitleStreamIndex; } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index aef206f135..1df7ffab3e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -20,6 +20,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.MediaEncoding.Encoder { @@ -456,7 +457,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var arg = string.Format("-i {0}", GetInputPathArgument(state)); - if (state.SubtitleStream != null) + if (state.SubtitleStream != null && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode) { if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { @@ -826,7 +827,7 @@ namespace MediaBrowser.MediaEncoding.Encoder args += " -map -0:a"; } - if (state.SubtitleStream == null) + if (state.SubtitleStream == null || state.Options.SubtitleMethod == SubtitleDeliveryMethod.Hls) { args += " -map -0:s"; } @@ -933,7 +934,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var output = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream) + if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode) { var subParam = GetTextSubtitleParam(state); @@ -1018,7 +1019,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var pts = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream) + if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode && !state.Options.CopyTimestamps) { var seconds = TimeSpan.FromTicks(state.Options.StartTimeTicks ?? 0).TotalSeconds; diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 5756566fe9..d1afcc7205 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -88,7 +88,7 @@ namespace MediaBrowser.MediaEncoding.Encoder args += keyFrameArg; - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode; // Add resolution params, if specified if (!hasGraphicalSubs) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 665da030d4..b04f1b0fb4 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -783,6 +783,11 @@ namespace MediaBrowser.Model.Dlna continue; } + if (profile.Method == SubtitleDeliveryMethod.Hls && playMethod != PlayMethod.Transcode) + { + continue; + } + if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; @@ -799,12 +804,6 @@ namespace MediaBrowser.Model.Dlna { return profile; } - - // For sync we can handle the longer extraction times - if (context == EncodingContext.Static && subtitleStream.IsTextSubtitleStream) - { - return profile; - } } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index bb522082aa..6b848d0f65 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -926,16 +926,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv var queryResult = _libraryManager.QueryItems(internalQuery); - var returnArray = queryResult.Items - .Cast() - .Select(i => new Tuple(_dtoService.GetBaseItemDto(i, options, user), i.ServiceName, i.ExternalId)) - .ToArray(); - - await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false); + var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user).ToArray(); var result = new QueryResult { - Items = returnArray.Select(i => i.Item1).ToArray(), + Items = returnArray, TotalRecordCount = queryResult.TotalRecordCount }; @@ -1006,15 +1001,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv var user = _userManager.GetUserById(query.UserId); - var returnArray = internalResult.Items - .Select(i => new Tuple(_dtoService.GetBaseItemDto(i, options, user), i.ServiceName, i.ExternalId)) - .ToArray(); - - await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false); + var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ToArray(); var result = new QueryResult { - Items = returnArray.Select(i => i.Item1).ToArray(), + Items = returnArray, TotalRecordCount = internalResult.TotalRecordCount }; @@ -1635,18 +1626,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false); - var tuples = internalResult.Items - .Select(i => new Tuple(i, _dtoService.GetBaseItemDto(i, options, user))) - .ToArray(); - - if (user != null) - { - _dtoService.FillSyncInfo(tuples, new DtoOptions(), user); - } + var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ToArray(); return new QueryResult { - Items = tuples.Select(i => i.Item2).ToArray(), + Items = returnArray, TotalRecordCount = internalResult.TotalRecordCount }; } @@ -1707,6 +1691,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } + public Task OnRecordingFileDeleted(ILiveTvRecording recording) + { + var service = GetService(recording); + + if (service is EmbyTV.EmbyTV) + { + // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says + return service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None); + } + + return Task.FromResult(true); + } + public async Task DeleteRecording(string recordingId) { var recording = await GetInternalRecording(recordingId, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs index 5f1bf0216a..7ff81e5c18 100644 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs @@ -239,11 +239,7 @@ namespace MediaBrowser.Server.Implementations.Persistence typeof(Year).Name, typeof(Channel).Name, typeof(AggregateFolder).Name, - typeof(CollectionFolder).Name, - - // LiveTVManager handles recordings - typeof(LiveTvAudioRecording).Name, - typeof(LiveTvVideoRecording).Name + typeof(CollectionFolder).Name } }); @@ -279,11 +275,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItem.Path ?? string.Empty); - await libraryItem.Delete(new DeleteOptions - { - DeleteFileLocation = false - - }).ConfigureAwait(false); + await libraryItem.OnFileDeleted().ConfigureAwait(false); } catch (OperationCanceledException) {