diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 444977e352..08ac5671d5 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -296,7 +296,7 @@ namespace MediaBrowser.Api // TODO: Lower this hls timeout var timerDuration = job.Type == TranscodingJobType.Progressive ? 1000 : - 14400000; + 7200000; if (job.KillTimer == null) { diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index b833dd7350..6eba195453 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -272,6 +272,11 @@ namespace MediaBrowser.Api.Playback // Set this back to what it was mediaSource.SupportsDirectStream = supportsDirectStream; + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } } if (mediaSource.SupportsDirectStream) @@ -285,6 +290,11 @@ namespace MediaBrowser.Api.Playback { mediaSource.SupportsDirectStream = false; } + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } } if (mediaSource.SupportsTranscoding) @@ -297,10 +307,36 @@ namespace MediaBrowser.Api.Playback if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode) { streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).Substring(1); + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; } + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + } + + private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken) + { + var profiles = info.GetSubtitleProfiles(false, "-", accessToken); + + foreach (var profile in profiles) + { + foreach (var stream in mediaSource.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Index == profile.Index) + { + stream.DeliveryMethod = profile.DeliveryMethod; + + if (profile.DeliveryMethod == SubtitleDeliveryMethod.External) + { + stream.DeliveryUrl = profile.Url.TrimStart('-'); + } + } + } } } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 7f696f300b..b364414d10 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -167,9 +167,12 @@ namespace MediaBrowser.Dlna.Didl AddVideoResource(container, video, deviceId, filter, contentFeature, streamInfo); } - foreach (var subtitle in streamInfo.GetExternalSubtitles(_serverAddress, _accessToken, false)) + foreach (var subtitle in streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken)) { - AddSubtitleElement(container, subtitle); + if (subtitle.DeliveryMethod == SubtitleDeliveryMethod.External) + { + AddSubtitleElement(container, subtitle); + } } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index feee2d765c..9bfa684be4 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.Model.Dlna public int? MaxVideoBitDepth { get; set; } public int? MaxRefFrames { get; set; } - + public float? MaxFramerate { get; set; } public DeviceProfile DeviceProfile { get; set; } @@ -81,7 +81,8 @@ namespace MediaBrowser.Model.Dlna public bool IsDirectStream { - get { + get + { return PlayMethod == PlayMethod.DirectStream || PlayMethod == PlayMethod.DirectPlay; } @@ -175,7 +176,7 @@ namespace MediaBrowser.Model.Dlna { list.Add(pair.Value); } - + return string.Format("Params={0}", string.Join(";", list.ToArray())); } @@ -199,7 +200,7 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(item.StartPositionTicks))); list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty)); - + list.Add(new NameValuePair("ClientTime", item.IsDirectStream ? string.Empty : DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture))); list.Add(new NameValuePair("MaxRefFrames", item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty)); list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty)); @@ -216,53 +217,25 @@ namespace MediaBrowser.Model.Dlna return list; } - public List GetExternalSubtitles(bool includeSelectedTrackOnly) + public List GetExternalSubtitles(bool includeSelectedTrackOnly, string baseUrl, string accessToken) { - List list = new List(); + List list = GetSubtitleProfiles(includeSelectedTrackOnly, baseUrl, accessToken); + List newList = new List(); // First add the selected track - if (SubtitleStreamIndex.HasValue) + foreach (SubtitleStreamInfo stream in list) { - foreach (MediaStream stream in MediaSource.MediaStreams) + if (stream.DeliveryMethod == SubtitleDeliveryMethod.External) { - if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) - { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); - - if (info != null) - { - list.Add(info); - } - } + newList.Add(stream); } } - if (!includeSelectedTrackOnly) - { - foreach (MediaStream stream in MediaSource.MediaStreams) - { - if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) - { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); - - if (info != null) - { - list.Add(info); - } - } - } - } - - return list; + return newList; } - public List GetExternalSubtitles(string baseUrl, string accessToken, bool includeSelectedTrackOnly) + public List GetSubtitleProfiles(bool includeSelectedTrackOnly, string baseUrl, string accessToken) { - if (string.IsNullOrEmpty(baseUrl)) - { - throw new ArgumentNullException(baseUrl); - } - List list = new List(); // HLS will preserve timestamps so we can just grab the full subtitle stream @@ -279,10 +252,7 @@ namespace MediaBrowser.Model.Dlna { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); - if (info != null) - { - list.Add(info); - } + list.Add(info); } } } @@ -295,14 +265,11 @@ namespace MediaBrowser.Model.Dlna { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); - if (info != null) - { - list.Add(info); - } + list.Add(info); } } } - + return list; } @@ -310,15 +277,22 @@ namespace MediaBrowser.Model.Dlna { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); - if (info != null) + if (info.DeliveryMethod == SubtitleDeliveryMethod.External) { - info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", - baseUrl, - ItemId, - MediaSourceId, - StringHelper.ToStringCultureInvariant(stream.Index), - StringHelper.ToStringCultureInvariant(startPositionTicks), - SubtitleFormat); + if (MediaSource.Protocol == MediaProtocol.Http) + { + info.Url = stream.Path; + } + else if (!string.IsNullOrEmpty(baseUrl)) + { + info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", + baseUrl, + ItemId, + MediaSourceId, + StringHelper.ToStringCultureInvariant(stream.Index), + StringHelper.ToStringCultureInvariant(startPositionTicks), + SubtitleFormat); + } } return info; @@ -328,18 +302,14 @@ namespace MediaBrowser.Model.Dlna { SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile.SubtitleProfiles, Context); - if (subtitleProfile.Method != SubtitleDeliveryMethod.External) - { - return null; - } - return new SubtitleStreamInfo { IsForced = stream.IsForced, Language = stream.Language, Name = stream.Language ?? "Unknown", Format = SubtitleFormat, - Index = stream.Index + Index = stream.Index, + DeliveryMethod = subtitleProfile.Method }; } diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs index a7a8da3ba2..602858ccc9 100644 --- a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs +++ b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs @@ -8,5 +8,6 @@ namespace MediaBrowser.Model.Dlna public bool IsForced { get; set; } public string Format { get; set; } public int Index { get; set; } + public SubtitleDeliveryMethod DeliveryMethod { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index fa075490a5..760829ebff 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Extensions; using System.Diagnostics; namespace MediaBrowser.Model.Entities @@ -135,6 +136,17 @@ namespace MediaBrowser.Model.Entities /// true if this instance is external; otherwise, false. public bool IsExternal { get; set; } + /// + /// Gets or sets the method. + /// + /// The method. + public SubtitleDeliveryMethod? DeliveryMethod { get; set; } + /// + /// Gets or sets the delivery URL. + /// + /// The delivery URL. + public string DeliveryUrl { get; set; } + public bool IsTextSubtitleStream { get diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index c3e8cf9444..43fb10df00 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -227,6 +227,11 @@ namespace MediaBrowser.Server.Implementations.Sync { Format = "srt", Method = SubtitleDeliveryMethod.External + }, + new SubtitleProfile + { + Format = "vtt", + Method = SubtitleDeliveryMethod.External } }; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index b73e0e85f2..7eb015ae7c 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -495,7 +495,7 @@ namespace MediaBrowser.Server.Implementations.Sync // No sense creating external subs if we're already burning one into the video var externalSubs = streamInfo.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ? new List() : - streamInfo.GetExternalSubtitles(false); + streamInfo.GetExternalSubtitles(false, null, null); // Mark as requiring conversion if transcoding the video, or if any subtitles need to be extracted var requiresVideoTranscoding = streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 5e5a0d2fc7..3acc79088d 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -677,7 +677,6 @@ namespace MediaBrowser.Server.Implementations.Sync syncedItem.Item.MediaSources = new List(); syncedItem.OriginalFileName = Path.GetFileName(libraryItem.Path); - if (string.IsNullOrWhiteSpace(syncedItem.OriginalFileName)) { syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path); @@ -686,6 +685,7 @@ namespace MediaBrowser.Server.Implementations.Sync // This will be null for items that are not audio/video if (mediaSource != null) { + syncedItem.OriginalFileName = Path.ChangeExtension(syncedItem.OriginalFileName, Path.GetExtension(mediaSource.Path)); syncedItem.Item.MediaSources.Add(mediaSource); }