diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 1963ad10a5..bfbad56359 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1605,6 +1605,8 @@ namespace MediaBrowser.Api.Playback { state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } + + state.AllMediaStreams = mediaStreams; } private async Task GetChannelMediaInfo(string id, diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 10543351b9..53cc41501e 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -5,6 +5,8 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using ServiceStack; using System; @@ -18,20 +20,20 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Playback.Hls { - [Route("/Videos/{Id}/master.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] + [Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")] public class GetMasterHlsVideoStream : VideoStreamRequest { public bool EnableAdaptiveBitrateStreaming { get; set; } + public SubtitleDeliveryMethod SubtitleMethod { get; set; } + public GetMasterHlsVideoStream() { EnableAdaptiveBitrateStreaming = true; } } - [Route("/Videos/{Id}/main.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] + [Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")] public class GetMainHlsVideoStream : VideoStreamRequest { } @@ -359,7 +361,17 @@ namespace MediaBrowser.Api.Playback.Hls var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8"; playlistUrl += queryString; - AppendPlaylist(builder, playlistUrl, totalBitrate); + var request = (GetMasterHlsVideoStream) state.Request; + + var subtitleStreams = state.AllMediaStreams + .Where(i => i.IsTextSubtitleStream) + .ToList(); + + var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ? + "subs" : + null; + + AppendPlaylist(builder, playlistUrl, totalBitrate, subtitleGroup); if (EnableAdaptiveBitrateStreaming(state)) { @@ -369,16 +381,52 @@ namespace MediaBrowser.Api.Playback.Hls var variation = GetBitrateVariation(totalBitrate); var newBitrate = totalBitrate - variation; - AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate); + AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate, subtitleGroup); variation *= 2; newBitrate = totalBitrate - variation; - AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate); + AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate, subtitleGroup); + } + + if (!string.IsNullOrWhiteSpace(subtitleGroup)) + { + AddSubtitles(state, subtitleStreams, builder); } return builder.ToString(); } + private void AddSubtitles(StreamState state, IEnumerable subtitles, StringBuilder builder) + { + var selectedIndex = state.SubtitleStream == null ? (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}\""; + + var name = stream.Language; + + var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index; + var isForced = stream.IsForced; + + if (string.IsNullOrWhiteSpace(name)) name = stream.Codec ?? "Unknown"; + + var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}", + state.Request.MediaSourceId, + stream.Index.ToString(UsCulture), + 30.ToString(UsCulture)); + + var line = string.Format(format, + name, + isDefault ? "YES" : "NO", + isForced ? "YES" : "NO", + url, + stream.Language ?? "Unknown"); + + builder.AppendLine(line); + } + } + private bool EnableAdaptiveBitrateStreaming(StreamState state) { var request = state.Request as GetMasterHlsVideoStream; @@ -397,9 +445,16 @@ namespace MediaBrowser.Api.Playback.Hls return state.VideoRequest.VideoBitRate.HasValue; } - private void AppendPlaylist(StringBuilder builder, string url, int bitrate) + private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup) { - builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture)); + var header = "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture); + + if (!string.IsNullOrWhiteSpace(subtitleGroup)) + { + header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup); + } + + builder.AppendLine(header); builder.AppendLine(url); } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index c6f4544477..1d3ff939af 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -38,6 +38,8 @@ namespace MediaBrowser.Api.Playback public string InputContainer { get; set; } + public List AllMediaStreams { get; set; } + public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } public MediaStream SubtitleStream { get; set; } @@ -78,6 +80,7 @@ namespace MediaBrowser.Api.Playback SupportedAudioCodecs = new List(); PlayableStreamFileNames = new List(); RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + AllMediaStreams = new List(); } public string InputAudioSync { get; set; } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index f7696e9639..dc5799239a 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -9,8 +9,10 @@ using MediaBrowser.Model.Providers; using ServiceStack; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -91,6 +93,29 @@ namespace MediaBrowser.Api.Subtitles [ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public long StartPositionTicks { get; set; } + + [ApiMember(Name = "EndPositionTicks", Description = "EndPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public long? EndPositionTicks { get; set; } + } + + [Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/subtitles.m3u8", "GET", Summary = "Gets an HLS subtitle playlist.")] + public class GetSubtitlePlaylist + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + + [ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string MediaSourceId { get; set; } + + [ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] + public int Index { get; set; } + + [ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] + public int SegmentLength { get; set; } } public class SubtitleService : BaseApiService @@ -106,6 +131,53 @@ namespace MediaBrowser.Api.Subtitles _subtitleEncoder = subtitleEncoder; } + public object Get(GetSubtitlePlaylist request) + { + var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); + + var mediaSource = item.GetMediaSources(false) + .First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id)); + + var builder = new StringBuilder(); + + var runtime = mediaSource.RunTimeTicks ?? -1; + + if (runtime <= 0) + { + throw new ArgumentException("HLS Subtitles are not supported for this media."); + } + + builder.AppendLine("#EXTM3U"); + builder.AppendLine("#EXT-X-TARGETDURATION:" + request.SegmentLength.ToString(CultureInfo.InvariantCulture)); + builder.AppendLine("#EXT-X-VERSION:3"); + builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); + + long positionTicks = 0; + var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks; + + while (positionTicks < runtime) + { + var remaining = runtime - positionTicks; + var lengthTicks = Math.Min(remaining, segmentLengthTicks); + + builder.AppendLine("#EXTINF:" + TimeSpan.FromTicks(lengthTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture)); + + var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks); + + var url = string.Format("stream.srt?StartPositionTicks={0}&EndPositionTicks={1}", + positionTicks.ToString(CultureInfo.InvariantCulture), + endPositionTicks.ToString(CultureInfo.InvariantCulture)); + + builder.AppendLine(url); + + positionTicks += segmentLengthTicks; + } + + builder.AppendLine("#EXT-X-ENDLIST"); + + return ResultFactory.GetResult(builder.ToString(), Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary()); + } + public object Get(GetSubtitle request) { if (string.IsNullOrEmpty(request.Format)) @@ -133,7 +205,7 @@ namespace MediaBrowser.Api.Subtitles request.Index, request.Format, request.StartPositionTicks, - null, + request.EndPositionTicks, CancellationToken.None).ConfigureAwait(false); } diff --git a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs index d12b3598ca..533f4ecf49 100644 --- a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs @@ -193,11 +193,12 @@ namespace MediaBrowser.Dlna.Profiles } }; - SoftSubtitleProfiles = new[] + SubtitleProfiles = new[] { new SubtitleProfile { - Format = "srt" + Format = "srt", + Method = SubtitleDeliveryMethod.External } }; } diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs index b90c906fbc..3dc1967dd9 100644 --- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -339,11 +339,12 @@ namespace MediaBrowser.Dlna.Profiles } }; - SoftSubtitleProfiles = new[] + SubtitleProfiles = new[] { new SubtitleProfile { - Format = "smi" + Format = "smi", + Method = SubtitleDeliveryMethod.External } }; } diff --git a/MediaBrowser.Dlna/Profiles/Windows81Profile.cs b/MediaBrowser.Dlna/Profiles/Windows81Profile.cs index 029fefc645..20d0834e1e 100644 --- a/MediaBrowser.Dlna/Profiles/Windows81Profile.cs +++ b/MediaBrowser.Dlna/Profiles/Windows81Profile.cs @@ -155,11 +155,12 @@ namespace MediaBrowser.Dlna.Profiles } }; - ExternalSubtitleProfiles = new[] + SubtitleProfiles = new[] { new SubtitleProfile { - Format = "ttml" + Format = "ttml", + Method = SubtitleDeliveryMethod.External } }; } diff --git a/MediaBrowser.Dlna/Profiles/Xml/Android.xml b/MediaBrowser.Dlna/Profiles/Xml/Android.xml index b7a6cc19c3..efab21021d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Android.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Android.xml @@ -65,6 +65,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index 6aafbe86e0..164ea943df 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -35,6 +35,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index 28fe6e0c9d..9a4322b68f 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -39,6 +39,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml index f0cf1e96c9..ee18a5efa2 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml @@ -73,6 +73,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index 775c7e4661..43516a378d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -39,6 +39,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml index 1461c2255c..1d63d38859 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml @@ -45,6 +45,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml index 5859b2439e..2ae5524faa 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -68,8 +68,7 @@ - - - - + + + \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml index 28f170bc02..b8edd8ff51 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -106,8 +106,7 @@ - - - - + + + \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml index 2e32b77c69..c3c13cdc3d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml @@ -71,6 +71,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index 87ba6e33bf..d48fc34b18 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml @@ -99,6 +99,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml index 698bb44b10..319936e0a5 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml @@ -107,6 +107,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml index f07536fcb5..541ecf8286 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -110,6 +110,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml index a99e4fa1e1..a99dc6b4e6 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -93,6 +93,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml index 3d4661621b..40e55e98ad 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -93,6 +93,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml index 55f89e3eb9..4499b905df 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml @@ -93,6 +93,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 5d12b65c81..4e0231a535 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -78,6 +78,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml b/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml index 1847d844f8..dec3d02ef3 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml @@ -62,8 +62,7 @@ - - - - + + + \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml b/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml index 12b7fe9c93..7c3414b25d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml @@ -76,6 +76,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index f3e1531300..b629fa5183 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -100,6 +100,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index 7e057a3f9d..5f86a94a9f 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -90,6 +90,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index d099113081..40ad2bc78a 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -45,6 +45,4 @@ - - \ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 630e7fa604..5366a538c0 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -90,8 +90,7 @@ namespace MediaBrowser.Model.Dlna public CodecProfile[] CodecProfiles { get; set; } public ResponseProfile[] ResponseProfiles { get; set; } - public SubtitleProfile[] SoftSubtitleProfiles { get; set; } - public SubtitleProfile[] ExternalSubtitleProfiles { get; set; } + public SubtitleProfile[] SubtitleProfiles { get; set; } public DeviceProfile() { @@ -100,9 +99,6 @@ namespace MediaBrowser.Model.Dlna ResponseProfiles = new ResponseProfile[] { }; CodecProfiles = new CodecProfile[] { }; ContainerProfiles = new ContainerProfile[] { }; - - SoftSubtitleProfiles = new SubtitleProfile[] { }; - ExternalSubtitleProfiles = new SubtitleProfile[] { }; XmlRootAttributes = new XmlAttribute[] { }; diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 22e817de15..72bf88b70d 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -518,7 +518,7 @@ namespace MediaBrowser.Model.Dlna { // See if the device can retrieve the subtitles externally bool supportsSubsExternally = options.Context == EncodingContext.Streaming && - ContainsSubtitleFormat(options.Profile.ExternalSubtitleProfiles, _serverTextSubtitleOutputs); + ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.External, _serverTextSubtitleOutputs); if (supportsSubsExternally) { @@ -526,7 +526,7 @@ namespace MediaBrowser.Model.Dlna } // See if the device can retrieve the subtitles externally - bool supportsEmbedded = ContainsSubtitleFormat(options.Profile.SoftSubtitleProfiles, _serverTextSubtitleOutputs); + bool supportsEmbedded = ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.Embed, _serverTextSubtitleOutputs); if (supportsEmbedded) { @@ -547,11 +547,11 @@ namespace MediaBrowser.Model.Dlna return codec; } - private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, string[] formats) + private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, SubtitleDeliveryMethod method, string[] formats) { foreach (SubtitleProfile profile in profiles) { - if (ListHelper.ContainsIgnoreCase(formats, profile.Format)) + if (method == profile.Method && ListHelper.ContainsIgnoreCase(formats, profile.Format)) { return true; } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 4f02d64508..f299a30c9b 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -476,6 +476,10 @@ namespace MediaBrowser.Model.Dlna /// /// The external /// - External = 2 + External = 2, + /// + /// The HLS + /// + Hls = 3 } } diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs index 7aa5e17713..1895158357 100644 --- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs +++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs @@ -9,5 +9,8 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("protocol")] public string Protocol { get; set; } + + [XmlAttribute("method")] + public SubtitleDeliveryMethod Method { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 8ffe8aa632..ad2b503659 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -550,24 +550,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } - if (!string.IsNullOrEmpty(info.Path)) - { - item.Path = info.Path; - } - else if (!string.IsNullOrEmpty(info.Url)) - { - item.Path = info.Url; - } - isNew = true; } item.RecordingInfo = info; item.ServiceName = serviceName; + var originalPath = item.Path; + + if (!string.IsNullOrEmpty(info.Path)) + { + item.Path = info.Path; + } + else if (!string.IsNullOrEmpty(info.Url)) + { + item.Path = info.Url; + } + + var pathChanged = !string.Equals(originalPath, item.Path); + await item.RefreshMetadata(new MetadataRefreshOptions { - ForceSave = isNew + ForceSave = isNew || pathChanged }, cancellationToken);