From bd2ea703e31522d505407a33089b95f997f6b062 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 28 Mar 2015 16:22:27 -0400 Subject: implement modular media sources --- .../Library/MediaSourceManager.cs | 148 ++++++++++++++++++--- 1 file changed, 126 insertions(+), 22 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 39219b541..40cf240d7 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Channels; +using System.Collections.Concurrent; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -13,25 +14,24 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Server.Implementations.LiveTv; namespace MediaBrowser.Server.Implementations.Library { - public class MediaSourceManager : IMediaSourceManager + public class MediaSourceManager : IMediaSourceManager, IDisposable { private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; private IMediaSourceProvider[] _providers; private readonly ILogger _logger; - public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, IChannelManager channelManager, ILogger logger) + public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger) { _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; - _channelManager = channelManager; _logger = logger; } @@ -133,24 +133,15 @@ namespace MediaBrowser.Server.Implementations.Library IEnumerable mediaSources; var hasMediaSources = (IHasMediaSources)item; - var channelItem = item as IChannelMediaItem; - if (channelItem != null) + if (string.IsNullOrWhiteSpace(userId)) { - mediaSources = await _channelManager.GetChannelItemMediaSources(id, true, cancellationToken) - .ConfigureAwait(false); + mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution); } else { - if (string.IsNullOrWhiteSpace(userId)) - { - mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution); - } - else - { - var user = _userManager.GetUserById(userId); - mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user); - } + var user = _userManager.GetUserById(userId); + mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user); } var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false); @@ -161,11 +152,16 @@ namespace MediaBrowser.Server.Implementations.Library foreach (var source in dynamicMediaSources) { - source.SupportsTranscoding = false; - if (source.Protocol == MediaProtocol.File) { source.SupportsDirectStream = File.Exists(source.Path); + + // TODO: Path substitution + } + else if (source.Protocol == MediaProtocol.Http) + { + // TODO: Allow this when the source is plain http, e.g. not HLS or Mpeg Dash + source.SupportsDirectStream = false; } else { @@ -175,7 +171,7 @@ namespace MediaBrowser.Server.Implementations.Library list.Add(source); } - return SortMediaSources(list); + return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder); } private async Task> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken) @@ -190,7 +186,15 @@ namespace MediaBrowser.Server.Implementations.Library { try { - return await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false); + var sources = await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false); + var list = sources.ToList(); + + foreach (var mediaSource in list) + { + SetKeyProperties(provider, mediaSource); + } + + return list; } catch (Exception ex) { @@ -199,6 +203,21 @@ namespace MediaBrowser.Server.Implementations.Library } } + private void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) + { + var prefix = provider.GetType().FullName.GetMD5().ToString("N") + "|"; + + if (!string.IsNullOrWhiteSpace(mediaSource.OpenKey) && !mediaSource.OpenKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + mediaSource.OpenKey = prefix + mediaSource.OpenKey; + } + + if (!string.IsNullOrWhiteSpace(mediaSource.CloseKey) && !mediaSource.CloseKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + mediaSource.CloseKey = prefix + mediaSource.CloseKey; + } + } + public Task> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken) { return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken); @@ -294,5 +313,90 @@ namespace MediaBrowser.Server.Implementations.Library { return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } + + private readonly ConcurrentDictionary _openStreams = + new ConcurrentDictionary(); + private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); + public async Task OpenMediaSource(string openKey, CancellationToken cancellationToken) + { + await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var tuple = GetProvider(openKey); + var provider = tuple.Item1; + + var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + + SetKeyProperties(provider, mediaSource); + + _openStreams.AddOrUpdate(mediaSource.CloseKey, mediaSource.CloseKey, (key, i) => mediaSource.CloseKey); + + return mediaSource; + } + finally + { + _liveStreamSemaphore.Release(); + } + } + + public async Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + { + await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var tuple = GetProvider(closeKey); + + await tuple.Item1.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + + string removedKey; + _openStreams.TryRemove(closeKey, out removedKey); + } + finally + { + _liveStreamSemaphore.Release(); + } + } + + private Tuple GetProvider(string key) + { + var keys = key.Split(new[] { '|' }, 2); + + var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase)); + + return new Tuple(provider, keys[1]); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + private readonly object _disposeLock = new object(); + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + lock (_disposeLock) + { + foreach (var key in _openStreams.Keys.ToList()) + { + var task = CloseMediaSource(key, CancellationToken.None); + + Task.WaitAll(task); + } + + _openStreams.Clear(); + } + } + } } } -- cgit v1.2.3 From 578dec0c71361be17eed68f82f20840807a9c9f4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 00:56:39 -0400 Subject: update stream generation --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 31 ++-- MediaBrowser.Api/Playback/MediaInfoService.cs | 160 +++++++++++++-------- MediaBrowser.Api/Playback/StreamRequest.cs | 3 +- MediaBrowser.Api/Playback/StreamState.cs | 4 +- .../Library/IMediaSourceManager.cs | 25 +++- .../Library/IMediaSourceProvider.cs | 8 +- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 16 +++ MediaBrowser.Model/Dto/MediaSourceInfo.cs | 7 +- .../Channels/ChannelDynamicMediaSourceProvider.cs | 4 +- .../Library/MediaSourceManager.cs | 155 +++++++++++++++++--- .../LiveTv/LiveTvManager.cs | 27 +++- .../LiveTv/LiveTvMediaSourceProvider.cs | 51 +++++-- .../Sync/SyncedMediaSourceProvider.cs | 8 +- 13 files changed, 377 insertions(+), 122 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 435bda2c4..2e7c9a5a7 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; @@ -937,7 +936,7 @@ namespace MediaBrowser.Api.Playback if (state.MediaSource.RequiresOpening) { - var mediaSource = await MediaSourceManager.OpenMediaSource(state.MediaSource.OpenKey, cancellationTokenSource.Token) + var mediaSource = await MediaSourceManager.OpenLiveStream(state.MediaSource.OpenToken, false, cancellationTokenSource.Token) .ConfigureAwait(false); AttachMediaSourceInfo(state, mediaSource, state.VideoRequest, state.RequestedUrl); @@ -946,9 +945,11 @@ namespace MediaBrowser.Api.Playback { TryStreamCopy(state, state.VideoRequest); } + } - // TODO: This is only needed for live tv - await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); + if (state.MediaSource.BufferMs.HasValue) + { + await Task.Delay(state.MediaSource.BufferMs.Value, cancellationTokenSource.Token).ConfigureAwait(false); } } @@ -1616,12 +1617,20 @@ namespace MediaBrowser.Api.Playback var archivable = item as IArchivable; state.IsInputArchive = archivable != null && archivable.IsArchive; - var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false); + MediaSourceInfo mediaSource = null; + if (string.IsNullOrWhiteSpace(request.LiveStreamId)) + { + var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false); + + mediaSource = string.IsNullOrEmpty(request.MediaSourceId) + ? mediaSources.First() + : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); + } + else + { + mediaSource = await MediaSourceManager.GetLiveStream(request.LiveStreamId, cancellationToken).ConfigureAwait(false); + } - var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) - ? mediaSources.First() - : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - var videoRequest = request as VideoStreamRequest; AttachMediaSourceInfo(state, mediaSource, videoRequest, url); @@ -1699,7 +1708,7 @@ namespace MediaBrowser.Api.Playback state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; state.RunTimeTicks = mediaSource.RunTimeTicks; state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - + if (mediaSource.VideoType.HasValue) { state.VideoType = mediaSource.VideoType.Value; @@ -1713,7 +1722,7 @@ namespace MediaBrowser.Api.Playback { state.InputTimestamp = mediaSource.Timestamp.Value; } - + state.InputProtocol = mediaSource.Protocol; state.MediaPath = mediaSource.Path; state.RunTimeTicks = mediaSource.RunTimeTicks; diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index cef8a34e5..d954c5b19 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -45,6 +45,9 @@ namespace MediaBrowser.Api.Playback [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string UserId { get; set; } + [ApiMember(Name = "MaxStreamingBitrate", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] + public int? MaxStreamingBitrate { get; set; } + [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] public long? StartTimeTicks { get; set; } @@ -58,6 +61,20 @@ namespace MediaBrowser.Api.Playback public string MediaSourceId { get; set; } } + [Route("/MediaSources/Open", "POST", Summary = "Opens a media source")] + public class OpenMediaSource : IReturn + { + [ApiMember(Name = "OpenToken", Description = "OpenToken", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string OpenToken { get; set; } + } + + [Route("/MediaSources/Close", "POST", Summary = "Closes a media source")] + public class CloseMediaSource : IReturnVoid + { + [ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string LiveStreamId { get; set; } + } + [Authenticated] public class MediaInfoService : BaseApiService { @@ -84,6 +101,18 @@ namespace MediaBrowser.Api.Playback return ToOptimizedResult(result); } + public async Task Post(OpenMediaSource request) + { + var result = await _mediaSourceManager.OpenLiveStream(request.OpenToken, false, CancellationToken.None).ConfigureAwait(false); + return ToOptimizedResult(result); + } + + public void Post(CloseMediaSource request) + { + var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None); + Task.WaitAll(task); + } + public async Task Post(GetPostedPlaybackInfo request) { var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false); @@ -102,7 +131,7 @@ namespace MediaBrowser.Api.Playback if (profile != null) { var mediaSourceId = request.MediaSourceId; - SetDeviceSpecificData(request.Id, info, profile, authInfo, null, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); + SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); } return ToOptimizedResult(info); @@ -158,81 +187,94 @@ namespace MediaBrowser.Api.Playback int? audioStreamIndex, int? subtitleStreamIndex) { - var streamBuilder = new StreamBuilder(); - var item = _libraryManager.GetItemById(itemId); foreach (var mediaSource in result.MediaSources) { - var options = new VideoOptions - { - MediaSources = new List { mediaSource }, - Context = EncodingContext.Streaming, - DeviceId = auth.DeviceId, - ItemId = item.Id.ToString("N"), - Profile = profile, - MaxBitrate = maxBitrate - }; - - if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase)) - { - options.MediaSourceId = mediaSourceId; - options.AudioStreamIndex = audioStreamIndex; - options.SubtitleStreamIndex = subtitleStreamIndex; - } + SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex); + } - if (mediaSource.SupportsDirectPlay) - { - var supportsDirectStream = mediaSource.SupportsDirectStream; + SortMediaSources(result); + } - // Dummy this up to fool StreamBuilder - mediaSource.SupportsDirectStream = true; + private void SetDeviceSpecificData(BaseItem item, + MediaSourceInfo mediaSource, + DeviceProfile profile, + AuthorizationInfo auth, + int? maxBitrate, + long startTimeTicks, + string mediaSourceId, + int? audioStreamIndex, + int? subtitleStreamIndex) + { + var streamBuilder = new StreamBuilder(); - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); + var options = new VideoOptions + { + MediaSources = new List { mediaSource }, + Context = EncodingContext.Streaming, + DeviceId = auth.DeviceId, + ItemId = item.Id.ToString("N"), + Profile = profile, + MaxBitrate = maxBitrate + }; + + if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase)) + { + options.MediaSourceId = mediaSourceId; + options.AudioStreamIndex = audioStreamIndex; + options.SubtitleStreamIndex = subtitleStreamIndex; + } - if (streamInfo == null || !streamInfo.IsDirectStream) - { - mediaSource.SupportsDirectPlay = false; - } + if (mediaSource.SupportsDirectPlay) + { + var supportsDirectStream = mediaSource.SupportsDirectStream; - // Set this back to what it was - mediaSource.SupportsDirectStream = supportsDirectStream; - } + // Dummy this up to fool StreamBuilder + mediaSource.SupportsDirectStream = true; - if (mediaSource.SupportsDirectStream) + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); + + if (streamInfo == null || !streamInfo.IsDirectStream) { - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); - - if (streamInfo == null || !streamInfo.IsDirectStream) - { - mediaSource.SupportsDirectStream = false; - } + mediaSource.SupportsDirectPlay = false; } - if (mediaSource.SupportsTranscoding) + // Set this back to what it was + mediaSource.SupportsDirectStream = supportsDirectStream; + } + + if (mediaSource.SupportsDirectStream) + { + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); + + if (streamInfo == null || !streamInfo.IsDirectStream) { - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); - - if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode) - { - streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).Substring(1); - mediaSource.TranscodingContainer = streamInfo.Container; - mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - } + mediaSource.SupportsDirectStream = false; } } - SortMediaSources(result); + if (mediaSource.SupportsTranscoding) + { + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); + + if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode) + { + streamInfo.StartPositionTicks = startTimeTicks; + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).Substring(1); + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + } + } } private void SortMediaSources(PlaybackInfoResponse result) diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index b52260b50..7ed4fcd96 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -72,8 +72,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; } + public string LiveStreamId { get; set; } } public class VideoStreamRequest : StreamRequest diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 37f2c7702..b097f3b6a 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -182,11 +182,11 @@ namespace MediaBrowser.Api.Playback private async void DisposeLiveStream() { - if (MediaSource.RequiresClosing) + if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Request.LiveStreamId)) { try { - await _mediaSourceManager.CloseMediaSource(MediaSource.CloseKey, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index fda17aa27..292205c03 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -84,17 +84,34 @@ namespace MediaBrowser.Controller.Library /// /// Opens the media source. /// - /// The open key. + /// The open token. + /// if set to true [enable automatic close]. /// The cancellation token. /// Task<MediaSourceInfo>. - Task OpenMediaSource(string openKey, CancellationToken cancellationToken); + Task OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken); + + /// + /// Gets the live stream. + /// + /// The identifier. + /// The cancellation token. + /// Task<MediaSourceInfo>. + Task GetLiveStream(string id, CancellationToken cancellationToken); + + /// + /// Pings the media source. + /// + /// The live stream identifier. + /// The cancellation token. + /// Task. + Task PingLiveStream(string id, CancellationToken cancellationToken); /// /// Closes the media source. /// - /// The close key. + /// The live stream identifier. /// The cancellation token. /// Task. - Task CloseMediaSource(string closeKey, CancellationToken cancellationToken); + Task CloseLiveStream(string id, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs index c5f5b5401..5b033af4a 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs @@ -19,17 +19,17 @@ namespace MediaBrowser.Controller.Library /// /// Opens the media source. /// - /// The open key. + /// The open token. /// The cancellation token. /// Task<MediaSourceInfo>. - Task OpenMediaSource(string openKey, CancellationToken cancellationToken); + Task OpenMediaSource(string openToken, CancellationToken cancellationToken); /// /// Closes the media source. /// - /// The close key. + /// The live stream identifier. /// The cancellation token. /// Task. - Task CloseMediaSource(string closeKey, CancellationToken cancellationToken); + Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 0b58a9232..d5b5d92a6 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -301,5 +301,21 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<QueryResult<BaseItem>>. Task> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken); + + /// + /// Gets the recording media sources. + /// + /// The identifier. + /// The cancellation token. + /// Task<IEnumerable<MediaSourceInfo>>. + Task> GetRecordingMediaSources(string id, CancellationToken cancellationToken); + + /// + /// Gets the channel media sources. + /// + /// The identifier. + /// The cancellation token. + /// Task<IEnumerable<MediaSourceInfo>>. + Task> GetChannelMediaSources(string id, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 92af8d671..3b4513724 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -27,10 +27,11 @@ namespace MediaBrowser.Model.Dto public bool SupportsDirectPlay { get; set; } public bool RequiresOpening { get; set; } - public string OpenKey { get; set; } + public string OpenToken { get; set; } public bool RequiresClosing { get; set; } - public string CloseKey { get; set; } - + public string LiveStreamId { get; set; } + public int? BufferMs { get; set; } + public VideoType? VideoType { get; set; } public IsoType? IsoType { get; set; } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index 6a7163bb3..dac3a80f2 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -30,12 +30,12 @@ namespace MediaBrowser.Server.Implementations.Channels return Task.FromResult>(new List()); } - public Task OpenMediaSource(string openKey, CancellationToken cancellationToken) + public Task OpenMediaSource(string openToken, CancellationToken cancellationToken) { throw new NotImplementedException(); } - public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 40cf240d7..3dbcf4aad 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -207,14 +207,14 @@ namespace MediaBrowser.Server.Implementations.Library { var prefix = provider.GetType().FullName.GetMD5().ToString("N") + "|"; - if (!string.IsNullOrWhiteSpace(mediaSource.OpenKey) && !mediaSource.OpenKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - mediaSource.OpenKey = prefix + mediaSource.OpenKey; + mediaSource.OpenToken = prefix + mediaSource.OpenToken; } - if (!string.IsNullOrWhiteSpace(mediaSource.CloseKey) && !mediaSource.CloseKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(mediaSource.LiveStreamId) && !mediaSource.LiveStreamId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - mediaSource.CloseKey = prefix + mediaSource.CloseKey; + mediaSource.LiveStreamId = prefix + mediaSource.LiveStreamId; } } @@ -314,24 +314,41 @@ namespace MediaBrowser.Server.Implementations.Library return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } - private readonly ConcurrentDictionary _openStreams = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); - public async Task OpenMediaSource(string openKey, CancellationToken cancellationToken) + + public async Task OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken) { await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { - var tuple = GetProvider(openKey); + var tuple = GetProvider(openToken); var provider = tuple.Item1; var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); SetKeyProperties(provider, mediaSource); - _openStreams.AddOrUpdate(mediaSource.CloseKey, mediaSource.CloseKey, (key, i) => mediaSource.CloseKey); - + var info = new LiveStreamInfo + { + Date = DateTime.UtcNow, + EnableCloseTimer = enableAutoClose, + Id = mediaSource.LiveStreamId, + MediaSource = mediaSource + }; + _openStreams.AddOrUpdate(mediaSource.LiveStreamId, info, (key, i) => info); + + if (enableAutoClose) + { + StartCloseTimer(); + } + + if (!string.IsNullOrWhiteSpace(mediaSource.TranscodingUrl)) + { + mediaSource.TranscodingUrl += "&LiveStreamId=" + mediaSource.LiveStreamId; + } + return mediaSource; } finally @@ -340,18 +357,70 @@ namespace MediaBrowser.Server.Implementations.Library } } - public async Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + public async Task GetLiveStream(string id, CancellationToken cancellationToken) + { + await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + LiveStreamInfo info; + if (_openStreams.TryGetValue(id, out info)) + { + return info.MediaSource; + } + else + { + throw new ResourceNotFoundException(); + } + } + finally + { + _liveStreamSemaphore.Release(); + } + } + + public async Task PingLiveStream(string id, CancellationToken cancellationToken) + { + await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + LiveStreamInfo info; + if (_openStreams.TryGetValue(id, out info)) + { + info.Date = DateTime.UtcNow; + } + else + { + _logger.Error("Failed to update MediaSource timestamp for {0}", id); + } + } + finally + { + _liveStreamSemaphore.Release(); + } + } + + public async Task CloseLiveStream(string id, CancellationToken cancellationToken) { await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { - var tuple = GetProvider(closeKey); + var tuple = GetProvider(id); - await tuple.Item1.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); - string removedKey; - _openStreams.TryRemove(closeKey, out removedKey); + LiveStreamInfo removed; + if (_openStreams.TryRemove(id, out removed)) + { + removed.Closed = true; + } + + if (_openStreams.Count == 0) + { + StopCloseTimer(); + } } finally { @@ -368,11 +437,56 @@ namespace MediaBrowser.Server.Implementations.Library return new Tuple(provider, keys[1]); } + private Timer _closeTimer; + private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(40); + + private void StartCloseTimer() + { + StopCloseTimer(); + + _closeTimer = new Timer(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge); + } + + private void StopCloseTimer() + { + var timer = _closeTimer; + + if (timer != null) + { + _closeTimer = null; + timer.Dispose(); + } + } + + private async void CloseTimerCallback(object state) + { + var infos = _openStreams + .Values + .Where(i => i.EnableCloseTimer && (DateTime.UtcNow - i.Date) > _openStreamMaxAge) + .ToList(); + + foreach (var info in infos) + { + if (!info.Closed) + { + try + { + await CloseLiveStream(info.Id, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error closing media source", ex); + } + } + } + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { + StopCloseTimer(); Dispose(true); } @@ -389,7 +503,7 @@ namespace MediaBrowser.Server.Implementations.Library { foreach (var key in _openStreams.Keys.ToList()) { - var task = CloseMediaSource(key, CancellationToken.None); + var task = CloseLiveStream(key, CancellationToken.None); Task.WaitAll(task); } @@ -398,5 +512,14 @@ namespace MediaBrowser.Server.Implementations.Library } } } + + private class LiveStreamInfo + { + public DateTime Date; + public bool EnableCloseTimer; + public string Id; + public bool Closed; + public MediaSourceInfo MediaSource; + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 202a051e3..86b31e0e5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -313,6 +313,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv return await GetLiveStream(id, true, cancellationToken).ConfigureAwait(false); } + public async Task> GetRecordingMediaSources(string id, CancellationToken cancellationToken) + { + var item = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false); + var service = GetService(item); + + return await service.GetRecordingStreamMediaSources(id, cancellationToken).ConfigureAwait(false); + } + + public async Task> GetChannelMediaSources(string id, CancellationToken cancellationToken) + { + var item = GetInternalChannel(id); + var service = GetService(item); + + return await service.GetChannelStreamMediaSources(id, cancellationToken).ConfigureAwait(false); + } + private ILiveTvService GetService(ILiveTvItem item) { return GetService(item.ServiceName); @@ -330,7 +346,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv try { MediaSourceInfo info; - var isVideo = true; + bool isVideo; if (isChannel) { @@ -340,7 +356,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId); info = await service.GetChannelStream(channel.ExternalId, null, cancellationToken).ConfigureAwait(false); info.RequiresClosing = true; - info.CloseKey = info.Id; + info.LiveStreamId = info.Id; } else { @@ -351,7 +367,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.RecordingInfo.Id); info = await service.GetRecordingStream(recording.RecordingInfo.Id, null, cancellationToken).ConfigureAwait(false); info.RequiresClosing = true; - info.CloseKey = info.Id; + info.LiveStreamId = info.Id; } _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); @@ -393,7 +409,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv { Type = MediaStreamType.Video, // Set the index to -1 because we don't know the exact index of the video stream within the container - Index = -1 + Index = -1, + + // Set to true if unknown to enable deinterlacing + IsInterlaced = true }, new MediaStream { diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 186bc499d..5de4cf499 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -2,6 +2,8 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Linq; @@ -13,10 +15,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv public class LiveTvMediaSourceProvider : IMediaSourceProvider { private readonly ILiveTvManager _liveTvManager; + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; - public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager) + public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager) { _liveTvManager = liveTvManager; + _jsonSerializer = jsonSerializer; + _logger = logManager.GetLogger(GetType().Name); } public Task> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken) @@ -38,28 +44,51 @@ namespace MediaBrowser.Server.Implementations.LiveTv private async Task> GetMediaSourcesInternal(ILiveTvItem item, CancellationToken cancellationToken) { - var hasMediaSources = (IHasMediaSources)item; + IEnumerable sources; - var sources = hasMediaSources.GetMediaSources(false) - .ToList(); + try + { + if (item is ILiveTvRecording) + { + sources = await _liveTvManager.GetRecordingMediaSources(item.Id.ToString("N"), cancellationToken) + .ConfigureAwait(false); + } + else + { + sources = await _liveTvManager.GetChannelMediaSources(item.Id.ToString("N"), cancellationToken) + .ConfigureAwait(false); + } + } + catch (NotImplementedException) + { + var hasMediaSources = (IHasMediaSources)item; + + sources = hasMediaSources.GetMediaSources(false) + .ToList(); + } - foreach (var source in sources) + var list = sources.ToList(); + + foreach (var source in list) { source.Type = MediaSourceType.Default; source.RequiresOpening = true; + source.BufferMs = source.BufferMs ?? 1500; var openKeys = new List(); openKeys.Add(item.GetType().Name); openKeys.Add(item.Id.ToString("N")); - source.OpenKey = string.Join("|", openKeys.ToArray()); + source.OpenToken = string.Join("|", openKeys.ToArray()); } - return sources; + _logger.Debug("MediaSources: {0}", _jsonSerializer.SerializeToString(list)); + + return list; } - public async Task OpenMediaSource(string openKey, CancellationToken cancellationToken) + public async Task OpenMediaSource(string openToken, CancellationToken cancellationToken) { - var keys = openKey.Split(new[] { '|' }, 2); + var keys = openToken.Split(new[] { '|' }, 2); if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase)) { @@ -69,9 +98,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv return await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false); } - public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken) { - return _liveTvManager.CloseLiveStream(closeKey, cancellationToken); + return _liveTvManager.CloseLiveStream(liveStreamId, cancellationToken); } } } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index 25a52fb95..1c17b9993 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -90,13 +90,13 @@ namespace MediaBrowser.Server.Implementations.Sync keyList.Add(provider.GetType().FullName.GetMD5().ToString("N")); keyList.Add(target.Id.GetMD5().ToString("N")); keyList.Add(item.Id); - mediaSource.OpenKey = string.Join("|", keyList.ToArray()); + mediaSource.OpenToken = string.Join("|", keyList.ToArray()); } } - public async Task OpenMediaSource(string openKey, CancellationToken cancellationToken) + public async Task OpenMediaSource(string openToken, CancellationToken cancellationToken) { - var openKeys = openKey.Split(new[] { '|' }, 3); + var openKeys = openToken.Split(new[] { '|' }, 3); var provider = _syncManager.ServerSyncProviders .FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase)); @@ -133,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.Sync mediaSource.SupportsTranscoding = false; } - public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken) { throw new NotImplementedException(); } -- cgit v1.2.3 From a79962b7ebfe59d837970581ac1b5f184b0aa42d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 12:45:16 -0400 Subject: update live stream generation --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 9 ++-- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 4 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 45 ++++++++++++----- .../Library/IMediaSourceManager.cs | 5 +- .../MediaBrowser.Model.Portable.csproj | 6 +++ .../MediaBrowser.Model.net35.csproj | 6 +++ MediaBrowser.Model/ApiClient/IConnectionManager.cs | 6 +++ MediaBrowser.Model/MediaBrowser.Model.csproj | 2 + MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs | 16 ++++++ MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs | 9 ++++ .../Library/MediaSourceManager.cs | 27 ++++++---- .../Session/SessionManager.cs | 57 ++++++++++++++-------- .../ApplicationHost.cs | 2 +- 13 files changed, 145 insertions(+), 49 deletions(-) create mode 100644 MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs create mode 100644 MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 2e7c9a5a7..5a4cdaaa9 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -936,10 +936,13 @@ namespace MediaBrowser.Api.Playback if (state.MediaSource.RequiresOpening) { - var mediaSource = await MediaSourceManager.OpenLiveStream(state.MediaSource.OpenToken, false, cancellationTokenSource.Token) - .ConfigureAwait(false); + var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest + { + OpenToken = state.MediaSource.OpenToken + + }, false, cancellationTokenSource.Token).ConfigureAwait(false); - AttachMediaSourceInfo(state, mediaSource, state.VideoRequest, state.RequestedUrl); + AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.VideoRequest, state.RequestedUrl); if (state.VideoRequest != null) { diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index b166bc319..4f7c0444b 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -698,7 +698,7 @@ namespace MediaBrowser.Api.Playback.Hls { var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d.ts"; - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -712,7 +712,7 @@ namespace MediaBrowser.Api.Playback.Hls ).Trim(); } - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index d954c5b19..6f8c2cc50 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -61,14 +61,12 @@ namespace MediaBrowser.Api.Playback public string MediaSourceId { get; set; } } - [Route("/MediaSources/Open", "POST", Summary = "Opens a media source")] - public class OpenMediaSource : IReturn + [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")] + public class OpenMediaSource : LiveStreamRequest, IReturn { - [ApiMember(Name = "OpenToken", Description = "OpenToken", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string OpenToken { get; set; } } - [Route("/MediaSources/Close", "POST", Summary = "Closes a media source")] + [Route("/LiveStreams/Close", "POST", Summary = "Closes a media source")] public class CloseMediaSource : IReturnVoid { [ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] @@ -103,7 +101,32 @@ namespace MediaBrowser.Api.Playback public async Task Post(OpenMediaSource request) { - var result = await _mediaSourceManager.OpenLiveStream(request.OpenToken, false, CancellationToken.None).ConfigureAwait(false); + var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); + + var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false); + + var profile = request.DeviceProfile; + if (profile == null) + { + var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (caps != null) + { + profile = caps.DeviceProfile; + } + } + + if (profile != null) + { + var item = _libraryManager.GetItemById(request.ItemId); + + SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, request.SubtitleStreamIndex); + } + + if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl)) + { + result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId; + } + return ToOptimizedResult(result); } @@ -177,11 +200,11 @@ namespace MediaBrowser.Api.Playback return result; } - private void SetDeviceSpecificData(string itemId, - PlaybackInfoResponse result, - DeviceProfile profile, - AuthorizationInfo auth, - int? maxBitrate, + private void SetDeviceSpecificData(string itemId, + PlaybackInfoResponse result, + DeviceProfile profile, + AuthorizationInfo auth, + int? maxBitrate, long startTimeTicks, string mediaSourceId, int? audioStreamIndex, diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 292205c03..9cbbabc8d 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; using System.Threading; @@ -84,11 +85,11 @@ namespace MediaBrowser.Controller.Library /// /// Opens the media source. /// - /// The open token. + /// The request. /// if set to true [enable automatic close]. /// The cancellation token. /// Task<MediaSourceInfo>. - Task OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken); + Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken); /// /// Gets the live stream. diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index ab77dc271..fdedc51a2 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -803,6 +803,12 @@ MediaInfo\IBlurayExaminer.cs + + MediaInfo\LiveStreamRequest.cs + + + MediaInfo\LiveStreamResponse.cs + MediaInfo\MediaProtocol.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index b772e1753..3618aa972 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -759,6 +759,12 @@ MediaInfo\IBlurayExaminer.cs + + MediaInfo\LiveStreamRequest.cs + + + MediaInfo\LiveStreamResponse.cs + MediaInfo\MediaProtocol.cs diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs index 84a815dfc..f8837f15d 100644 --- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs +++ b/MediaBrowser.Model/ApiClient/IConnectionManager.cs @@ -172,5 +172,11 @@ namespace MediaBrowser.Model.ApiClient /// if set to true [remember credentials]. /// Task. Task AuthenticateOffline(UserDto user, string password, bool rememberCredentials); + + /// + /// Gets the offline users. + /// + /// Task<List<UserDto>>. + Task> GetOfflineUsers(); } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index fe67038df..067309512 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -141,6 +141,8 @@ + + diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs new file mode 100644 index 000000000..8078219d8 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -0,0 +1,16 @@ +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamRequest + { + public string OpenToken { get; set; } + public string UserId { get; set; } + public int? MaxStreamingBitrate { get; set; } + public long? StartTimeTicks { get; set; } + public int? AudioStreamIndex { get; set; } + public int? SubtitleStreamIndex { get; set; } + public string ItemId { get; set; } + public DeviceProfile DeviceProfile { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs new file mode 100644 index 000000000..e79e37a71 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs @@ -0,0 +1,9 @@ +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamResponse + { + public MediaSourceInfo MediaSource { get; set; } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 3dbcf4aad..64b1f2c89 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -8,13 +7,14 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; +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; -using MediaBrowser.Server.Implementations.LiveTv; namespace MediaBrowser.Server.Implementations.Library { @@ -23,16 +23,18 @@ namespace MediaBrowser.Server.Implementations.Library private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; + private readonly IJsonSerializer _jsonSerializer; private IMediaSourceProvider[] _providers; private readonly ILogger _logger; - public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger) + public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer) { _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; _logger = logger; + _jsonSerializer = jsonSerializer; } public void AddParts(IEnumerable providers) @@ -317,13 +319,13 @@ namespace MediaBrowser.Server.Implementations.Library private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); - public async Task OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken) + public async Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken) { await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { - var tuple = GetProvider(openToken); + var tuple = GetProvider(request.OpenToken); var provider = tuple.Item1; var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); @@ -344,12 +346,19 @@ namespace MediaBrowser.Server.Implementations.Library StartCloseTimer(); } - if (!string.IsNullOrWhiteSpace(mediaSource.TranscodingUrl)) + var json = _jsonSerializer.SerializeToString(mediaSource); + var clone = _jsonSerializer.DeserializeFromString(json); + + if (!string.IsNullOrWhiteSpace(request.UserId)) { - mediaSource.TranscodingUrl += "&LiveStreamId=" + mediaSource.LiveStreamId; + var user = _userManager.GetUserById(request.UserId); + SetUserProperties(clone, user); } - return mediaSource; + return new LiveStreamResponse + { + MediaSource = clone + }; } finally { diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index ec93ae876..16fc42063 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Devices; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Library; @@ -304,13 +305,21 @@ namespace MediaBrowser.Server.Implementations.Session } } + private async Task GetMediaSource(BaseItem item, string mediaSourceId) + { + var sources = await _mediaSourceManager.GetPlayackMediaSources(item.Id.ToString("N"), false, CancellationToken.None) + .ConfigureAwait(false); + + return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + } + /// /// Updates the now playing item id. /// /// The session. /// The information. /// The library item. - private void UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem) + private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem) { if (string.IsNullOrWhiteSpace(info.MediaSourceId)) { @@ -319,29 +328,27 @@ namespace MediaBrowser.Server.Implementations.Session if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null) { - var runtimeTicks = libraryItem.RunTimeTicks; + var current = session.NowPlayingItem; - if (!string.Equals(info.ItemId, info.MediaSourceId) && - !string.IsNullOrWhiteSpace(info.MediaSourceId)) + if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) { - var runtimeItem = _libraryManager.GetItemById(new Guid(info.MediaSourceId)) ?? - _libraryManager.GetItemById(info.ItemId); + var runtimeTicks = libraryItem.RunTimeTicks; - runtimeTicks = runtimeItem.RunTimeTicks; - } + var mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId).ConfigureAwait(false); - var current = session.NowPlayingItem; + if (mediaSource != null) + { + runtimeTicks = mediaSource.RunTimeTicks; + } - if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) - { - info.Item = GetItemInfo(libraryItem, libraryItem, info.MediaSourceId); + info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource); + + info.Item.RunTimeTicks = runtimeTicks; } else { info.Item = current; } - - info.Item.RunTimeTicks = runtimeTicks; } session.NowPlayingItem = info.Item; @@ -432,6 +439,12 @@ namespace MediaBrowser.Server.Implementations.Session device = device ?? _deviceManager.GetDevice(deviceId); + if (device == null) + { + var userIdString = userId.HasValue ? userId.Value.ToString("N") : null; + device = await _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString).ConfigureAwait(false); + } + if (device != null) { if (!string.IsNullOrEmpty(device.CustomName)) @@ -570,7 +583,7 @@ namespace MediaBrowser.Server.Implementations.Session ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); - UpdateNowPlayingItem(session, info, libraryItem); + await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false); if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode) { @@ -652,7 +665,7 @@ namespace MediaBrowser.Server.Implementations.Session ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); - UpdateNowPlayingItem(session, info, libraryItem); + await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false); var users = GetUsers(session); @@ -731,7 +744,9 @@ namespace MediaBrowser.Server.Implementations.Session if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) { - info.Item = GetItemInfo(libraryItem, libraryItem, info.MediaSourceId); + var mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId).ConfigureAwait(false); + + info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource); } else { @@ -1439,10 +1454,10 @@ namespace MediaBrowser.Server.Implementations.Session /// /// The item. /// The chapter owner. - /// The media source identifier. + /// The media source. /// BaseItemInfo. /// item - private BaseItemInfo GetItemInfo(BaseItem item, BaseItem chapterOwner, string mediaSourceId) + private BaseItemInfo GetItemInfo(BaseItem item, BaseItem chapterOwner, MediaSourceInfo mediaSource) { if (item == null) { @@ -1593,9 +1608,9 @@ namespace MediaBrowser.Server.Implementations.Session info.Chapters = _dtoService.GetChapterInfoDtos(chapterOwner).ToList(); } - if (!string.IsNullOrWhiteSpace(mediaSourceId)) + if (mediaSource != null) { - info.MediaStreams = _mediaSourceManager.GetMediaStreams(mediaSourceId).ToList(); + info.MediaStreams = mediaSource.MediaStreams; } return info; diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 039c5edf3..aad88d022 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -472,7 +472,7 @@ namespace MediaBrowser.Server.Startup.Common ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient); RegisterSingleInstance(ChannelManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager")); + MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer); RegisterSingleInstance(MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager); -- cgit v1.2.3 From 5474215141d92c4012f387d6d9ebe9116ca74abc Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 18:38:32 -0400 Subject: sync updates --- MediaBrowser.Api/Playback/MediaInfoService.cs | 12 ++++++++---- .../Library/MediaSourceManager.cs | 14 +++++++++++++- MediaBrowser.Server.Implementations/Sync/MediaSync.cs | 8 ++++---- MediaBrowser.Server.Implementations/Sync/SyncManager.cs | 2 +- .../Sync/SyncedMediaSourceProvider.cs | 11 ++++++++--- 5 files changed, 34 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index c6678d1ed..b833dd735 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -119,12 +119,16 @@ namespace MediaBrowser.Api.Playback { var item = _libraryManager.GetItemById(request.ItemId); - SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, request.SubtitleStreamIndex); + SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, + request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, + request.SubtitleStreamIndex); } - - if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl)) + else { - result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId; + if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl)) + { + result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId; + } } return ToOptimizedResult(result); diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 64b1f2c89..e832142a9 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -316,7 +316,7 @@ namespace MediaBrowser.Server.Implementations.Library return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } - private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); public async Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken) @@ -330,6 +330,11 @@ namespace MediaBrowser.Server.Implementations.Library var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(mediaSource.LiveStreamId)) + { + throw new InvalidOperationException(string.Format("{0} returned null LiveStreamId", provider.GetType().Name)); + } + SetKeyProperties(provider, mediaSource); var info = new LiveStreamInfo @@ -368,6 +373,13 @@ namespace MediaBrowser.Server.Implementations.Library public async Task GetLiveStream(string id, CancellationToken cancellationToken) { + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentNullException("id"); + } + + _logger.Debug("Getting live stream {0}", id); + await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index dd8ce82ef..620143f17 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -371,7 +371,7 @@ namespace MediaBrowser.Server.Implementations.Sync if (item.IsType("episode")) { - parts.Add("TV"); + //parts.Add("TV"); if (!string.IsNullOrWhiteSpace(item.SeriesName)) { parts.Add(item.SeriesName); @@ -379,12 +379,12 @@ namespace MediaBrowser.Server.Implementations.Sync } else if (item.IsVideo) { - parts.Add("Videos"); + //parts.Add("Videos"); parts.Add(item.Name); } else if (item.IsAudio) { - parts.Add("Music"); + //parts.Add("Music"); if (!string.IsNullOrWhiteSpace(item.AlbumArtist)) { @@ -398,7 +398,7 @@ namespace MediaBrowser.Server.Implementations.Sync } else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase)) { - parts.Add("Photos"); + //parts.Add("Photos"); if (!string.IsNullOrWhiteSpace(item.Album)) { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 2cf6c6853..d1ebbd28e 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -721,7 +721,7 @@ namespace MediaBrowser.Server.Implementations.Sync var jobItemResult = GetJobItems(new SyncJobItemQuery { TargetId = targetId, - Statuses = new SyncJobItemStatus[] + Statuses = new[] { SyncJobItemStatus.ReadyToTransfer } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index 1c17b9993..d1ef523e1 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -92,6 +92,8 @@ namespace MediaBrowser.Server.Implementations.Sync keyList.Add(item.Id); mediaSource.OpenToken = string.Join("|", keyList.ToArray()); } + + list.Add(mediaSource); } public async Task OpenMediaSource(string openToken, CancellationToken cancellationToken) @@ -111,13 +113,16 @@ namespace MediaBrowser.Server.Implementations.Sync var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(localItem.LocalPath, target, cancellationToken).ConfigureAwait(false); var mediaSource = localItem.Item.MediaSources.First(); + mediaSource.LiveStreamId = Guid.NewGuid().ToString(); SetStaticMediaSourceInfo(localItem, mediaSource); foreach (var stream in mediaSource.MediaStreams) { - var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); - - stream.Path = dynamicStreamInfo.Path; + if (!string.IsNullOrWhiteSpace(stream.ExternalId)) + { + var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); + stream.Path = dynamicStreamInfo.Path; + } } mediaSource.Path = dynamicInfo.Path; -- cgit v1.2.3 From 7f537ad149e2dc4407229be7c2fcd2df34b8f288 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 19:47:31 -0400 Subject: sync updates --- .../Library/MediaSourceManager.cs | 9 ++++++++- MediaBrowser.Server.Implementations/Sync/MediaSync.cs | 17 ++++++++++------- MediaBrowser.Server.Implementations/Sync/SyncManager.cs | 12 +++++++----- 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index e832142a9..c752fe48e 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -430,7 +430,14 @@ namespace MediaBrowser.Server.Implementations.Library { var tuple = GetProvider(id); - await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + try + { + await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + } + catch (NotImplementedException) + { + + } LiveStreamInfo removed; if (_openStreams.TryRemove(id, out removed)) diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 620143f17..ae8b22827 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -40,6 +40,7 @@ namespace MediaBrowser.Server.Implementations.Sync CancellationToken cancellationToken) { var serverId = _appHost.SystemId; + var serverName = _appHost.FriendlyName; await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false); progress.Report(3); @@ -51,7 +52,7 @@ namespace MediaBrowser.Server.Implementations.Sync totalProgress += 1; progress.Report(totalProgress); }); - await GetNewMedia(provider, dataProvider, target, serverId, innerProgress, cancellationToken); + await GetNewMedia(provider, dataProvider, target, serverId, serverName, innerProgress, cancellationToken); // Do the data sync twice so the server knows what was removed from the device await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false); @@ -93,6 +94,7 @@ namespace MediaBrowser.Server.Implementations.Sync ISyncDataProvider dataProvider, SyncTarget target, string serverId, + string serverName, IProgress progress, CancellationToken cancellationToken) { @@ -119,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.Sync progress.Report(totalProgress); }); - await GetItem(provider, dataProvider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false); + await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false); numComplete++; startingPercent = numComplete; @@ -133,6 +135,7 @@ namespace MediaBrowser.Server.Implementations.Sync ISyncDataProvider dataProvider, SyncTarget target, string serverId, + string serverName, SyncedItem jobItem, IProgress progress, CancellationToken cancellationToken) @@ -140,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.Sync var libraryItem = jobItem.Item; var internalSyncJobItem = _syncManager.GetJobItem(jobItem.SyncJobItemId); - var localItem = CreateLocalItem(provider, jobItem, target, libraryItem, serverId, jobItem.OriginalFileName); + var localItem = CreateLocalItem(provider, jobItem, target, libraryItem, serverId, serverName, jobItem.OriginalFileName); await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id); @@ -326,9 +329,9 @@ namespace MediaBrowser.Server.Implementations.Sync } } - public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncedItem syncedItem, SyncTarget target, BaseItemDto libraryItem, string serverId, string originalFileName) + public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncedItem syncedItem, SyncTarget target, BaseItemDto libraryItem, string serverId, string serverName, string originalFileName) { - var path = GetDirectoryPath(provider, syncedItem, libraryItem, serverId); + var path = GetDirectoryPath(provider, syncedItem, libraryItem, serverId, serverName); path.Add(GetLocalFileName(provider, libraryItem, originalFileName)); var localPath = provider.GetFullPath(path, target); @@ -361,11 +364,11 @@ namespace MediaBrowser.Server.Implementations.Sync return name; } - private List GetDirectoryPath(IServerSyncProvider provider, SyncedItem syncedItem, BaseItemDto item, string serverId) + private List GetDirectoryPath(IServerSyncProvider provider, SyncedItem syncedItem, BaseItemDto item, string serverId, string serverName) { var parts = new List { - serverId, + serverName, GetSyncJobFolderName(syncedItem, provider) }; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index d1ebbd28e..5e5a0d2fc 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -676,14 +676,16 @@ namespace MediaBrowser.Server.Implementations.Sync syncedItem.Item.MediaSources = new List(); - // This will be null for items that are not audio/video - if (mediaSource == null) + syncedItem.OriginalFileName = Path.GetFileName(libraryItem.Path); + + if (string.IsNullOrWhiteSpace(syncedItem.OriginalFileName)) { - syncedItem.OriginalFileName = Path.GetFileName(libraryItem.Path); + syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path); } - else + + // This will be null for items that are not audio/video + if (mediaSource != null) { - syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path); syncedItem.Item.MediaSources.Add(mediaSource); } -- cgit v1.2.3 From 0bd27381e0ef5a0a17ea93bb86a752c5d2cc2e1a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 23:04:55 -0400 Subject: sync updates --- .../Net/BasePeriodicWebSocketListener.cs | 7 ++- .../Library/MediaSourceManager.cs | 9 +--- .../LiveTv/ChannelImageProvider.cs | 6 ++- .../LiveTv/LiveTvManager.cs | 52 +++++++++++++--------- SharedVersion.cs | 4 +- 5 files changed, 42 insertions(+), 36 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 0afaf955e..3756f1d93 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; using System.Collections.Generic; @@ -75,12 +74,12 @@ namespace MediaBrowser.Controller.Net throw new ArgumentNullException("message"); } - if (message.MessageType.Equals(Name + "Start", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(message.MessageType, Name + "Start", StringComparison.OrdinalIgnoreCase)) { Start(message); } - if (message.MessageType.Equals(Name + "Stop", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(message.MessageType, Name + "Stop", StringComparison.OrdinalIgnoreCase)) { Stop(message); } diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index c752fe48e..e832142a9 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -430,14 +430,7 @@ namespace MediaBrowser.Server.Implementations.Library { var tuple = GetProvider(id); - try - { - await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); - } - catch (NotImplementedException) - { - - } + await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); LiveStreamInfo removed; if (_openStreams.TryRemove(id, out removed)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs index ed67df01e..7c3af0a54 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs @@ -51,11 +51,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv var response = await _httpClient.GetResponse(options).ConfigureAwait(false); - if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) + var contentType = response.ContentType; + + if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { imageResponse.HasImage = true; imageResponse.Stream = response.Content; - imageResponse.SetFormatFromMimeType(response.ContentType); + imageResponse.SetFormatFromMimeType(contentType); } else { diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index b30db6092..f67691859 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -85,8 +85,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv get { return _services; } } - public ILiveTvService ActiveService { get; private set; } - private LiveTvOptions GetConfiguration() { return _config.GetConfiguration("livetv"); @@ -100,8 +98,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv { _services.AddRange(services); - ActiveService = _services.FirstOrDefault(); - foreach (var service in _services) { service.DataSourceChanged += service_DataSourceChanged; @@ -359,7 +355,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (info.RequiresClosing) { - info.LiveStreamId = info.Id; + var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_"; + + info.LiveStreamId = idPrefix + info.Id; } } else @@ -374,7 +372,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (info.RequiresClosing) { - info.LiveStreamId = info.Id; + var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_"; + + info.LiveStreamId = idPrefix + info.Id; } } @@ -384,7 +384,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv var data = new LiveStreamData { Info = info, - ConsumerCount = 1, IsChannel = isChannel, ItemId = id }; @@ -1788,7 +1787,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv class LiveStreamData { internal MediaSourceInfo Info; - internal int ConsumerCount; internal string ItemId; internal bool IsChannel; } @@ -1799,19 +1797,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv try { - var service = ActiveService; + var parts = id.Split(new[] { '_' }, 2); - LiveStreamData data; - if (_openStreams.TryGetValue(id, out data)) - { - if (data.ConsumerCount > 1) - { - data.ConsumerCount--; - _logger.Info("Decrementing live stream client count."); - return; - } + var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase)); + if (service == null) + { + throw new ArgumentException("Service not found."); } + + id = parts[1]; + + LiveStreamData data; _openStreams.TryRemove(id, out data); _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id); @@ -1892,6 +1889,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv Name = service.Name }; + var tunerIdPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_"; + try { var statusInfo = await service.GetStatusInfoAsync(cancellationToken).ConfigureAwait(false); @@ -1913,7 +1912,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv channelName = channel == null ? null : channel.Name; } - return _tvDtoService.GetTunerInfoDto(service.Name, i, channelName); + var dto = _tvDtoService.GetTunerInfoDto(service.Name, i, channelName); + + dto.Id = tunerIdPrefix + dto.Id; + + return dto; }).ToList(); } @@ -1966,7 +1969,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv /// Task. public Task ResetTuner(string id, CancellationToken cancellationToken) { - return ActiveService.ResetTuner(id, cancellationToken); + var parts = id.Split(new[] { '_' }, 2); + + var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase)); + + if (service == null) + { + throw new ArgumentException("Service not found."); + } + + return service.ResetTuner(parts[1], cancellationToken); } public async Task GetLiveTvFolder(string userId, CancellationToken cancellationToken) diff --git a/SharedVersion.cs b/SharedVersion.cs index fca054025..d744ccd55 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5557.40000")] +[assembly: AssemblyVersion("3.0.*")] +//[assembly: AssemblyVersion("3.0.5557.40000")] -- cgit v1.2.3 From a025f4eefaf43c27f33521239d99c47d292728f5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 31 Mar 2015 12:24:16 -0400 Subject: sync updates --- MediaBrowser.Api/BaseApiService.cs | 11 +++ MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 62 +++++++----- MediaBrowser.Api/Playback/TranscodingThrottler.cs | 2 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 8 ++ .../MediaEncoding/ISubtitleEncoder.cs | 8 +- .../MediaEncoding/MediaStreamSelector.cs | 77 ++++++++++++++- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 4 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 4 +- MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs | 2 +- .../Subtitles/SubtitleEncoder.cs | 109 ++++++++++++++------- .../Configuration/UserConfiguration.cs | 1 + MediaBrowser.Model/Dlna/StreamBuilder.cs | 59 ++++++++++- MediaBrowser.Model/Dlna/StreamInfo.cs | 74 ++++++++------ MediaBrowser.Model/Entities/MediaStream.cs | 6 ++ .../MediaInfo/SubtitleScheduledTask.cs | 9 +- .../Dto/DtoService.cs | 8 +- .../Library/MediaSourceManager.cs | 31 ++++-- .../Library/UserViewManager.cs | 2 +- .../LiveTv/LiveTvMediaSourceProvider.cs | 6 +- .../Localization/Server/server.json | 1 + .../Sync/CloudSyncProfile.cs | 5 +- .../Sync/SyncJobProcessor.cs | 4 +- .../ApplicationHost.cs | 2 +- 24 files changed, 365 insertions(+), 132 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 4465be97a..3364c3c6b 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -73,6 +73,17 @@ namespace MediaBrowser.Api return ResultFactory.GetOptimizedResultUsingCache(Request, cacheKey, lastDateModified, cacheDuration, factoryFn); } + /// + /// Infers the server address from the url + /// + /// + protected string GetServerAddress() + { + var index = Request.AbsoluteUri.IndexOf(Request.PathInfo, StringComparison.OrdinalIgnoreCase); + + return Request.AbsoluteUri.Substring(0, index); + } + protected void AssertCanUpdateUser(IUserManager userManager, string userId) { var auth = AuthorizationContext.GetAuthorizationInfo(Request); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 8c4bcf0a3..75321f872 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -699,7 +699,7 @@ namespace MediaBrowser.Api.Playback if (!string.IsNullOrEmpty(state.SubtitleStream.Language)) { - var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath); + var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.MediaSource.Protocol, CancellationToken.None).Result; if (!string.IsNullOrEmpty(charenc)) { diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 6eba19545..0930c0002 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -59,6 +59,9 @@ namespace MediaBrowser.Api.Playback [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string MediaSourceId { get; set; } + + [ApiMember(Name = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string LiveStreamId { get; set; } } [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")] @@ -142,7 +145,7 @@ namespace MediaBrowser.Api.Playback public async Task Post(GetPostedPlaybackInfo request) { - var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false); + var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false); var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); var profile = request.DeviceProfile; @@ -164,29 +167,37 @@ namespace MediaBrowser.Api.Playback return ToOptimizedResult(info); } - private async Task GetPlaybackInfo(string id, string userId, string mediaSourceId = null) + private async Task GetPlaybackInfo(string id, string userId, string mediaSourceId = null, string liveStreamId = null) { var result = new PlaybackInfoResponse(); - IEnumerable mediaSources; - - try - { - mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false); - } - catch (PlaybackException ex) + if (string.IsNullOrWhiteSpace(liveStreamId)) { - mediaSources = new List(); - result.ErrorCode = ex.ErrorCode; - } + IEnumerable mediaSources; + try + { + mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false); + } + catch (PlaybackException ex) + { + mediaSources = new List(); + result.ErrorCode = ex.ErrorCode; + } - result.MediaSources = mediaSources.ToList(); + result.MediaSources = mediaSources.ToList(); - if (!string.IsNullOrWhiteSpace(mediaSourceId)) + if (!string.IsNullOrWhiteSpace(mediaSourceId)) + { + result.MediaSources = result.MediaSources + .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + } + } + else { - result.MediaSources = result.MediaSources - .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) - .ToList(); + var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); + + result.MediaSources = new List { mediaSource }; } if (result.MediaSources.Count == 0) @@ -236,6 +247,8 @@ namespace MediaBrowser.Api.Playback { var streamBuilder = new StreamBuilder(); + var baseUrl = GetServerAddress(); + var options = new VideoOptions { MediaSources = new List { mediaSource }, @@ -275,7 +288,7 @@ namespace MediaBrowser.Api.Playback if (streamInfo != null) { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, baseUrl, auth.Token); } } @@ -293,7 +306,7 @@ namespace MediaBrowser.Api.Playback if (streamInfo != null) { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, baseUrl, auth.Token); } } @@ -307,21 +320,22 @@ namespace MediaBrowser.Api.Playback if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode) { streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + mediaSource.TranscodingUrl = streamInfo.ToUrl(baseUrl, auth.Token); mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; } if (streamInfo != null) { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, baseUrl, auth.Token); } } } - private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken) + private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string baseUrl, string accessToken) { - var profiles = info.GetSubtitleProfiles(false, "-", accessToken); + var profiles = info.GetSubtitleProfiles(false, baseUrl, accessToken); + mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex; foreach (var profile in profiles) { @@ -333,7 +347,7 @@ namespace MediaBrowser.Api.Playback if (profile.DeliveryMethod == SubtitleDeliveryMethod.External) { - stream.DeliveryUrl = profile.Url.TrimStart('-'); + stream.DeliveryUrl = profile.Url; } } } diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index 58cfa086e..ff79bb48f 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback var options = GetOptions(); - if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) + if (/*options.EnableThrottling &&*/ IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) { PauseTranscoding(); } diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index cdfd00ce9..38eae2577 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -304,6 +304,14 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); + if (!request.IsPlayed.HasValue) + { + if (user.Configuration.HidePlayedInLatest) + { + request.IsPlayed = false; + } + } + var list = _userViewManager.GetLatestItems(new LatestItemsQuery { GroupItems = request.GroupItems, diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 37c2bf4d2..e4a2cd007 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -1,4 +1,5 @@ -using System.IO; +using MediaBrowser.Model.MediaInfo; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -47,8 +48,9 @@ namespace MediaBrowser.Controller.MediaEncoding /// Gets the subtitle language encoding parameter. /// /// The path. + /// The protocol. + /// The cancellation token. /// System.String. - string GetSubtitleFileCharacterSet(string path); - + Task GetSubtitleFileCharacterSet(string path, MediaProtocol protocol, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs index 57fddb2b1..a006a1723 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs @@ -41,8 +41,6 @@ namespace MediaBrowser.Controller.MediaEncoding streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages) .ToList(); - var full = streams.Where(s => !s.IsForced); - MediaStream stream = null; if (mode == SubtitlePlaybackMode.None) @@ -55,13 +53,13 @@ namespace MediaBrowser.Controller.MediaEncoding // if the audio language is not understood by the user, load their preferred subs, if there are any if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage)) { - stream = full.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)); + stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)); } } else if (mode == SubtitlePlaybackMode.Always) { // always load the most suitable full subtitles - stream = full.FirstOrDefault(); + stream = streams.FirstOrDefault(s => !s.IsForced); } // load forced subs if we have found no suitable full subtitles @@ -97,6 +95,77 @@ namespace MediaBrowser.Controller.MediaEncoding .ThenBy(i => i.Index); } + public static void SetSubtitleStreamScores(List streams, + List preferredLanguages, + SubtitlePlaybackMode mode, + string audioTrackLanguage) + { + if (mode == SubtitlePlaybackMode.None) + { + return; + } + + streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages) + .ToList(); + + var filteredStreams = new List(); + + if (mode == SubtitlePlaybackMode.Default) + { + // if the audio language is not understood by the user, load their preferred subs, if there are any + if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage)) + { + filteredStreams = streams.Where(s => !s.IsForced && ContainsOrdinal(preferredLanguages, s.Language)) + .ToList(); + } + } + else if (mode == SubtitlePlaybackMode.Always) + { + // always load the most suitable full subtitles + filteredStreams = streams.Where(s => !s.IsForced) + .ToList(); + } + + // load forced subs if we have found no suitable full subtitles + if (filteredStreams.Count == 0) + { + filteredStreams = streams + .Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) + .ToList(); + } + + foreach (var stream in filteredStreams) + { + stream.Score = GetSubtitleScore(stream, preferredLanguages); + } + } + + private static int GetSubtitleScore(MediaStream stream, List languagePreferences) + { + var values = new List(); + + var index = languagePreferences.FindIndex(l => string.Equals(stream.Language, l, StringComparison.OrdinalIgnoreCase)); + + values.Add(index == -1 ? 0 : 100 - index); + + values.Add(stream.IsDefault ? 1 : 0); + values.Add(stream.SupportsExternalStream ? 1 : 0); + values.Add(stream.IsTextSubtitleStream ? 1 : 0); + values.Add(stream.IsExternal ? 1 : 0); + + values.Reverse(); + var scale = 1; + var score = 0; + + foreach (var value in values) + { + score += scale * (value + 1); + scale *= 10; + } + + return score; + } + private static int GetBooleanOrderBy(bool value) { return value ? 0 : 1; diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index b364414d1..3b1cdb542 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -124,7 +124,7 @@ namespace MediaBrowser.Dlna.Didl { if (streamInfo == null) { - var sources = _user == null ? video.GetMediaSources(true).ToList() : _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList(); + var sources = _user == null ? _mediaSourceManager.GetStaticMediaSources(video, true).ToList() : _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList(); streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions { @@ -351,7 +351,7 @@ namespace MediaBrowser.Dlna.Didl if (streamInfo == null) { - var sources = _user == null ? audio.GetMediaSources(true).ToList() : _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); + var sources = _user == null ? _mediaSourceManager.GetStaticMediaSources(audio, true).ToList() : _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions { diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 3eb091a19..38c0f71cc 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -467,10 +467,10 @@ namespace MediaBrowser.Dlna.PlayTo var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ?? _dlnaManager.GetDefaultProfile(); - + var hasMediaSources = item as IHasMediaSources; var mediaSources = hasMediaSources != null - ? (user == null ? hasMediaSources.GetMediaSources(true) : _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList() + ? (user == null ? _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true) : _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList() : new List(); var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex); diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 8266f4d3a..78ac92f25 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -951,7 +951,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!string.IsNullOrEmpty(state.SubtitleStream.Language)) { - var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath); + var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.MediaSource.Protocol, CancellationToken.None).Result; if (!string.IsNullOrEmpty(charenc)) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7d74c51ba..14d3e7284 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -29,8 +30,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IJsonSerializer _json; + private readonly IHttpClient _httpClient; + private readonly IMediaSourceManager _mediaSourceManager; - public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json) + public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager) { _libraryManager = libraryManager; _logger = logger; @@ -38,6 +41,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _json = json; + _httpClient = httpClient; + _mediaSourceManager = mediaSourceManager; } private string SubtitleCachePath @@ -127,9 +132,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles int subtitleStreamIndex, CancellationToken cancellationToken) { - var item = (Video)_libraryManager.GetItemById(new Guid(itemId)); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, false, cancellationToken).ConfigureAwait(false); - var mediaSource = item.GetMediaSources(false) + var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId)); var subtitleStream = mediaSource.MediaStreams @@ -149,20 +154,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false); - var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item3).ConfigureAwait(false); + var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item2, fileInfo.Item4, cancellationToken).ConfigureAwait(false); - return new Tuple(stream, fileInfo.Item2); + return new Tuple(stream, fileInfo.Item3); } - private async Task GetSubtitleStream(string path, bool requiresCharset) + private async Task GetSubtitleStream(string path, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken) { if (requiresCharset) { - var charset = GetSubtitleFileCharacterSet(path); + var charset = await GetSubtitleFileCharacterSet(path, protocol, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(charset)) { - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + using (var fs = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false)) { using (var reader = new StreamReader(fs, GetEncoding(charset))) { @@ -196,7 +201,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private async Task> GetReadableFile(string mediaPath, + private async Task> GetReadableFile(string mediaPath, string[] inputFiles, MediaProtocol protocol, MediaStream subtitleStream, @@ -228,12 +233,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles } // Extract - var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, "." + outputFormat); + var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat); await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken) .ConfigureAwait(false); - return new Tuple(outputPath, outputFormat, false); + return new Tuple(outputPath, MediaProtocol.File, outputFormat, false); } var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) @@ -242,14 +247,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (GetReader(currentFormat, false) == null) { // Convert - var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, ".srt"); + var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt"); - await ConvertTextSubtitleToSrt(subtitleStream.Path, outputPath, cancellationToken).ConfigureAwait(false); + await ConvertTextSubtitleToSrt(subtitleStream.Path, protocol, outputPath, cancellationToken).ConfigureAwait(false); - return new Tuple(outputPath, "srt", true); + return new Tuple(outputPath, MediaProtocol.File, "srt", true); } - return new Tuple(subtitleStream.Path, currentFormat, true); + return new Tuple(subtitleStream.Path, protocol, currentFormat, true); } private async Task GetTrackInfo(Stream stream, @@ -336,10 +341,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Converts the text subtitle to SRT. /// /// The input path. + /// The input protocol. /// The output path. /// The cancellation token. /// Task. - public async Task ConvertTextSubtitleToSrt(string inputPath, string outputPath, CancellationToken cancellationToken) + private async Task ConvertTextSubtitleToSrt(string inputPath, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -349,7 +355,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (!File.Exists(outputPath)) { - await ConvertTextSubtitleToSrtInternal(inputPath, outputPath).ConfigureAwait(false); + await ConvertTextSubtitleToSrtInternal(inputPath, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -362,13 +368,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Converts the text subtitle to SRT internal. /// /// The input path. + /// The input protocol. /// The output path. + /// The cancellation token. /// Task. - /// inputPath + /// + /// inputPath /// or - /// outputPath + /// outputPath + /// /// - private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string outputPath) + private async Task ConvertTextSubtitleToSrtInternal(string inputPath, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -382,7 +392,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); - var encodingParam = GetSubtitleFileCharacterSet(inputPath); + var encodingParam = await GetSubtitleFileCharacterSet(inputPath, inputProtocol, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(encodingParam)) { @@ -688,32 +698,41 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension) + private string GetSubtitleCachePath(string mediaPath, MediaProtocol protocol, int subtitleStreamIndex, string outputSubtitleExtension) { - var ticksParam = string.Empty; + if (protocol == MediaProtocol.File) + { + var ticksParam = string.Empty; + + var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); - var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); + var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension; - var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension; + var prefix = filename.Substring(0, 1); + + return Path.Combine(SubtitleCachePath, prefix, filename); + } + else + { + var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5() + outputSubtitleExtension; - var prefix = filename.Substring(0, 1); + var prefix = filename.Substring(0, 1); - return Path.Combine(SubtitleCachePath, prefix, filename); + return Path.Combine(SubtitleCachePath, prefix, filename); + } } - /// - /// Gets the subtitle language encoding param. - /// - /// The path. - /// System.String. - public string GetSubtitleFileCharacterSet(string path) + public async Task GetSubtitleFileCharacterSet(string path, MediaProtocol protocol, CancellationToken cancellationToken) { - if (GetFileEncoding(path).Equals(Encoding.UTF8)) + if (protocol == MediaProtocol.File) { - return string.Empty; + if (GetFileEncoding(path).Equals(Encoding.UTF8)) + { + return string.Empty; + } } - var charset = DetectCharset(path); + var charset = await DetectCharset(path, protocol, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(charset)) { @@ -769,11 +788,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private string DetectCharset(string path) + private async Task DetectCharset(string path, MediaProtocol protocol, CancellationToken cancellationToken) { try { - using (var file = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var file = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false)) { var detector = new CharsetDetector(); detector.Feed(file); @@ -819,5 +838,19 @@ namespace MediaBrowser.MediaEncoding.Subtitles // It's ok - anything aside from utf is ok since that's what we're looking for return Encoding.Default; } + + private async Task GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken) + { + if (protocol == MediaProtocol.Http) + { + return await _httpClient.Get(path, cancellationToken).ConfigureAwait(false); + } + if (protocol == MediaProtocol.File) + { + return _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + } + + throw new ArgumentOutOfRangeException("protocol"); + } } } diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index a78161140..98641636a 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -54,6 +54,7 @@ namespace MediaBrowser.Model.Configuration public string[] LatestItemsExcludes { get; set; } public bool HasMigratedToPolicy { get; set; } + public bool HidePlayedInLatest { get; set; } /// /// Initializes a new instance of the class. diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 1cc37de57..b27138b4a 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -282,6 +282,49 @@ namespace MediaBrowser.Model.Dlna return playMethods; } + private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles) + { + int highestScore = -1; + + foreach (MediaStream stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue) + { + if (stream.Score.Value > highestScore) + { + highestScore = stream.Score.Value; + } + } + } + + List topStreams = new List(); + foreach (MediaStream stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue && stream.Score.Value == highestScore) + { + topStreams.Add(stream); + } + } + + // If multiple streams have an equal score, try to pick the most efficient one + if (topStreams.Count > 1) + { + foreach (MediaStream stream in topStreams) + { + foreach (SubtitleProfile profile in subtitleProfiles) + { + if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec)) + { + return stream.Index; + } + } + } + } + + // If no optimization panned out, just use the original default + return item.DefaultSubtitleStreamIndex; + } + private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { StreamInfo playlistItem = new StreamInfo @@ -294,7 +337,7 @@ namespace MediaBrowser.Model.Dlna DeviceProfile = options.Profile }; - playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? item.DefaultSubtitleStreamIndex; + playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles); MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex); @@ -618,6 +661,8 @@ namespace MediaBrowser.Model.Dlna // Look for an external profile that matches the stream type (text/graphical) foreach (SubtitleProfile profile in subtitleProfiles) { + bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); + if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; @@ -625,6 +670,11 @@ namespace MediaBrowser.Model.Dlna if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { + if (!requiresConversion) + { + return profile; + } + if (subtitleStream.SupportsExternalStream) { return profile; @@ -640,6 +690,8 @@ namespace MediaBrowser.Model.Dlna foreach (SubtitleProfile profile in subtitleProfiles) { + bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); + if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; @@ -647,6 +699,11 @@ namespace MediaBrowser.Model.Dlna if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { + if (!requiresConversion) + { + return profile; + } + return profile; } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 9bfa684be..3977a9ab0 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -217,9 +217,9 @@ namespace MediaBrowser.Model.Dlna return list; } - public List GetExternalSubtitles(bool includeSelectedTrackOnly, string baseUrl, string accessToken) + public List GetExternalSubtitles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) { - List list = GetSubtitleProfiles(includeSelectedTrackOnly, baseUrl, accessToken); + List list = GetSubtitleProfiles(includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); List newList = new List(); // First add the selected track @@ -235,6 +235,11 @@ namespace MediaBrowser.Model.Dlna } public List GetSubtitleProfiles(bool includeSelectedTrackOnly, string baseUrl, string accessToken) + { + return GetSubtitleProfiles(includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + public List GetSubtitleProfiles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) { List list = new List(); @@ -250,9 +255,7 @@ namespace MediaBrowser.Model.Dlna { if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); - - list.Add(info); + AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks); } } } @@ -263,9 +266,7 @@ namespace MediaBrowser.Model.Dlna { if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); - - list.Add(info); + AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks); } } } @@ -273,44 +274,57 @@ namespace MediaBrowser.Model.Dlna return list; } - private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks) + private void AddSubtitleProfiles(List list, MediaStream stream, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks) { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); - - if (info.DeliveryMethod == SubtitleDeliveryMethod.External) + if (enableAllProfiles) { - if (MediaSource.Protocol == MediaProtocol.Http) - { - info.Url = stream.Path; - } - else if (!string.IsNullOrEmpty(baseUrl)) + foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles) { - info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", - baseUrl, - ItemId, - MediaSourceId, - StringHelper.ToStringCultureInvariant(stream.Index), - StringHelper.ToStringCultureInvariant(startPositionTicks), - SubtitleFormat); + SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }); + + list.Add(info); } } + else + { + SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles); - return info; + list.Add(info); + } } - private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream) + private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles) { - SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile.SubtitleProfiles, Context); - - return new SubtitleStreamInfo + SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, Context); + SubtitleStreamInfo info = new SubtitleStreamInfo { IsForced = stream.IsForced, Language = stream.Language, Name = stream.Language ?? "Unknown", - Format = SubtitleFormat, + Format = subtitleProfile.Format, Index = stream.Index, DeliveryMethod = subtitleProfile.Method }; + + if (info.DeliveryMethod == SubtitleDeliveryMethod.External) + { + if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format)) + { + info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", + baseUrl, + ItemId, + MediaSourceId, + StringHelper.ToStringCultureInvariant(stream.Index), + StringHelper.ToStringCultureInvariant(startPositionTicks), + subtitleProfile.Format); + } + else + { + info.Url = stream.Path; + } + } + + return info; } /// diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 760829ebf..dfeed7450 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -130,6 +130,12 @@ namespace MediaBrowser.Model.Entities /// The index. public int Index { get; set; } + /// + /// Gets or sets the score. + /// + /// The score. + public int? Score { get; set; } + /// /// Gets or sets a value indicating whether this instance is external. /// diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index 2852f5719..0f092b554 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -6,15 +6,14 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Subtitles; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Providers; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.MediaInfo { @@ -23,14 +22,16 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; + private readonly IMediaSourceManager _mediaSourceManager; private readonly ILogger _logger; - public SubtitleScheduledTask(ILibraryManager libraryManager, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger) + public SubtitleScheduledTask(ILibraryManager libraryManager, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, IMediaSourceManager mediaSourceManager) { _libraryManager = libraryManager; _config = config; _subtitleManager = subtitleManager; _logger = logger; + _mediaSourceManager = mediaSourceManager; } public string Name @@ -107,7 +108,7 @@ namespace MediaBrowser.Providers.MediaInfo (options.DownloadMovieSubtitles && video is Movie)) { - var mediaStreams = video.GetMediaSources(false).First().MediaStreams; + var mediaStreams = _mediaSourceManager.GetStaticMediaSources(video, false).First().MediaStreams; var downloadedLanguages = await new SubtitleDownloader(_logger, _subtitleManager) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 756ff6917..dc6f4a525 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -261,7 +261,7 @@ namespace MediaBrowser.Server.Implementations.Dto { if (user == null) { - dto.MediaSources = hasMediaSources.GetMediaSources(true).ToList(); + dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true).ToList(); } else { @@ -269,7 +269,7 @@ namespace MediaBrowser.Server.Implementations.Dto } } } - + if (fields.Contains(ItemFields.Studios)) { AttachStudios(dto, item); @@ -1280,7 +1280,7 @@ namespace MediaBrowser.Server.Implementations.Dto } else { - mediaStreams = iHasMediaSources.GetMediaSources(true).First().MediaStreams; + mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams; } dto.MediaStreams = mediaStreams; @@ -1453,7 +1453,7 @@ namespace MediaBrowser.Server.Implementations.Dto var tvChannel = item as LiveTvChannel; if (tvChannel != null) { - dto.MediaSources = tvChannel.GetMediaSources(true).ToList(); + dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(tvChannel, true).ToList(); } var channelItem = item as IChannelItem; diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index e832142a9..66eeb61f7 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -135,6 +135,7 @@ namespace MediaBrowser.Server.Implementations.Library IEnumerable mediaSources; var hasMediaSources = (IHasMediaSources)item; + User user = null; if (string.IsNullOrWhiteSpace(userId)) { @@ -142,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.Library } else { - var user = _userManager.GetUserById(userId); + user = _userManager.GetUserById(userId); mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user); } @@ -154,6 +155,10 @@ namespace MediaBrowser.Server.Implementations.Library foreach (var source in dynamicMediaSources) { + if (user != null) + { + SetUserProperties(source, user); + } if (source.Protocol == MediaProtocol.File) { source.SupportsDirectStream = File.Exists(source.Path); @@ -225,6 +230,11 @@ namespace MediaBrowser.Server.Implementations.Library return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken); } + public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) + { + return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + } + public IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution) { if (item == null) @@ -288,6 +298,9 @@ namespace MediaBrowser.Server.Implementations.Library preferredSubs, user.Configuration.SubtitleMode, audioLangage); + + MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, + user.Configuration.SubtitleMode, audioLangage); } private IEnumerable SortMediaSources(IEnumerable sources) @@ -311,11 +324,6 @@ namespace MediaBrowser.Server.Implementations.Library .ToList(); } - public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) - { - return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); - } - private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); @@ -428,9 +436,16 @@ namespace MediaBrowser.Server.Implementations.Library try { - var tuple = GetProvider(id); + LiveStreamInfo current; + if (_openStreams.TryGetValue(id, out current)) + { + if (current.MediaSource.RequiresClosing) + { + var tuple = GetProvider(id); - await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); + } + } LiveStreamInfo removed; if (_openStreams.TryRemove(id, out removed)) diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index e63a27551..757f08562 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -215,7 +215,7 @@ namespace MediaBrowser.Server.Implementations.Library if (request.IsPlayed.HasValue) { var val = request.IsPlayed.Value; - if (i.IsPlayed(currentUser) != val) + if (i is Video && i.IsPlayed(currentUser) != val) { return false; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 5de4cf499..d549cad46 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -17,11 +17,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv private readonly ILiveTvManager _liveTvManager; private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; + private readonly IMediaSourceManager _mediaSourceManager; - public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager) + public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager, IMediaSourceManager mediaSourceManager) { _liveTvManager = liveTvManager; _jsonSerializer = jsonSerializer; + _mediaSourceManager = mediaSourceManager; _logger = logManager.GetLogger(GetType().Name); } @@ -63,7 +65,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { var hasMediaSources = (IHasMediaSources)item; - sources = hasMediaSources.GetMediaSources(false) + sources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false) .ToList(); } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index f52d8a9fe..0cff99c5d 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -52,6 +52,7 @@ "HeaderAddUser": "Add User", "LabelAddConnectSupporterHelp": "To add a user who isn't listed, you'll need to first link their account to Emby Connect from their user profile page.", "LabelPinCode": "Pin code:", + "OptionHideWatchedContentFromLatestMedia": "Hide watched content from latest media", "ButtonOk": "Ok", "ButtonCancel": "Cancel", "ButtonExit": "Exit", diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 43fb10df0..73400f834 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -198,8 +198,7 @@ namespace MediaBrowser.Server.Implementations.Sync var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2"; codecProfiles.Add(new CodecProfile { - Type = CodecType.Audio, - Codec = "mpeg4", + Type = CodecType.VideoAudio, Conditions = new[] { new ProfileCondition @@ -207,7 +206,7 @@ namespace MediaBrowser.Server.Implementations.Sync Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, Value = maxAudioChannels, - IsRequired = false + IsRequired = true }, new ProfileCondition { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 7eb015ae7..271b2bb39 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, null, null); + streamInfo.GetExternalSubtitles(false, true, 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; @@ -823,7 +823,7 @@ namespace MediaBrowser.Server.Implementations.Sync var hasMediaSources = item as IHasMediaSources; - var mediaSources = hasMediaSources.GetMediaSources(false).ToList(); + var mediaSources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false).ToList(); var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference) ? new string[] { } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index aad88d022..df7b3f061 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -530,7 +530,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); RegisterSingleInstance(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager)); - SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer); + SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager); RegisterSingleInstance(SubtitleEncoder); await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false); -- cgit v1.2.3 From 5965afecde2536c0a2ec566fd6afdb21add8aa50 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 31 Mar 2015 14:50:08 -0400 Subject: live tv fix --- MediaBrowser.Api/Playback/MediaInfoService.cs | 23 ------ MediaBrowser.Api/Playback/TranscodingThrottler.cs | 2 +- .../HttpClientManager/HttpClientManager.cs | 32 +++++--- MediaBrowser.Controller/LiveTv/LiveTvProgram.cs | 6 ++ MediaBrowser.Controller/Sync/ISyncDataProvider.cs | 16 ++++ MediaBrowser.Model/ApiClient/IApiClient.cs | 5 +- .../MediaInfo/PlaybackInfoRequest.cs | 16 ++++ MediaBrowser.Model/Sync/LocalItem.cs | 5 ++ MediaBrowser.Model/Sync/SyncDataRequest.cs | 1 + .../Channels/ChannelImageProvider.cs | 1 - .../Library/MediaSourceManager.cs | 10 +-- .../LiveTv/LiveTvDtoService.cs | 2 +- .../LiveTv/LiveTvManager.cs | 6 +- .../Sync/MediaSync.cs | 11 +-- .../Sync/SyncManager.cs | 95 +++++++++++++++++++++- .../Sync/TargetDataProvider.cs | 30 ++++++- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 20 files changed, 207 insertions(+), 66 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 0930c0002..ca735f068 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -39,29 +39,6 @@ namespace MediaBrowser.Api.Playback [Route("/Items/{Id}/PlaybackInfo", "POST", Summary = "Gets live playback media info for an item")] public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn { - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } - - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string UserId { get; set; } - - [ApiMember(Name = "MaxStreamingBitrate", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? MaxStreamingBitrate { get; set; } - - [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public long? StartTimeTicks { get; set; } - - [ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? AudioStreamIndex { get; set; } - - [ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? SubtitleStreamIndex { get; set; } - - [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string MediaSourceId { get; set; } - - [ApiMember(Name = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string LiveStreamId { get; set; } } [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")] diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index ff79bb48f..58cfa086e 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback var options = GetOptions(); - if (/*options.EnableThrottling &&*/ IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) + if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) { PauseTranscoding(); } diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index b925649fc..94c91c55a 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -107,30 +107,40 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager private WebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression) { - var request = (HttpWebRequest)WebRequest.Create(options.Url); + var request = WebRequest.Create(options.Url); + var httpWebRequest = request as HttpWebRequest; - AddRequestHeaders(request, options); + if (httpWebRequest != null) + { + AddRequestHeaders(httpWebRequest, options); - request.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None; + httpWebRequest.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None; + } request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); - if (options.EnableKeepAlive) + if (httpWebRequest != null) { - request.KeepAlive = true; + if (options.EnableKeepAlive) + { + httpWebRequest.KeepAlive = true; + } } request.Method = method; request.Timeout = options.TimeoutMs; - if (!string.IsNullOrEmpty(options.Host)) + if (httpWebRequest != null) { - request.Host = options.Host; - } + if (!string.IsNullOrEmpty(options.Host)) + { + httpWebRequest.Host = options.Host; + } - if (!string.IsNullOrEmpty(options.Referer)) - { - request.Referer = options.Referer; + if (!string.IsNullOrEmpty(options.Referer)) + { + httpWebRequest.Referer = options.Referer; + } } //request.ServicePoint.BindIPEndPointDelegate = BindIPEndPointCallback; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 0b07d8b6d..0609df4c6 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -34,6 +34,12 @@ namespace MediaBrowser.Controller.LiveTv /// The channel identifier. public string ExternalChannelId { get; set; } + /// + /// Gets or sets the original air date. + /// + /// The original air date. + public DateTime? OriginalAirDate { get; set; } + /// /// Gets or sets the type of the channel. /// diff --git a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs index f84748b97..04101fd46 100644 --- a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs @@ -14,6 +14,14 @@ namespace MediaBrowser.Controller.Sync /// Task<List<System.String>>. Task> GetServerItemIds(SyncTarget target, string serverId); + /// + /// Gets the synchronize job item ids. + /// + /// The target. + /// The server identifier. + /// Task<List<System.String>>. + Task> GetSyncJobItemIds(SyncTarget target, string serverId); + /// /// Adds the or update. /// @@ -46,5 +54,13 @@ namespace MediaBrowser.Controller.Sync /// The item identifier. /// Task<LocalItem>. Task> GetCachedItems(SyncTarget target, string serverId, string itemId); + /// + /// Gets the cached items by synchronize job item identifier. + /// + /// The target. + /// The server identifier. + /// The synchronize job item identifier. + /// Task<List<LocalItem>>. + Task> GetCachedItemsBySyncJobItemId(SyncTarget target, string serverId, string syncJobItemId); } } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 58af01615..9675de38b 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -248,10 +248,9 @@ namespace MediaBrowser.Model.ApiClient /// /// Gets the playback information. /// - /// The item identifier. - /// The user identifier. + /// The request. /// Task<LiveMediaInfoResult>. - Task GetPlaybackInfo(string itemId, string userId); + Task GetPlaybackInfo(PlaybackInfoRequest request); /// /// Gets the users async. diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs index ffd4995ad..124739073 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -4,6 +4,22 @@ namespace MediaBrowser.Model.MediaInfo { public class PlaybackInfoRequest { + public string Id { get; set; } + + public string UserId { get; set; } + + public int? MaxStreamingBitrate { get; set; } + + public long? StartTimeTicks { get; set; } + + public int? AudioStreamIndex { get; set; } + + public int? SubtitleStreamIndex { get; set; } + + public string MediaSourceId { get; set; } + + public string LiveStreamId { get; set; } + public DeviceProfile DeviceProfile { get; set; } } } diff --git a/MediaBrowser.Model/Sync/LocalItem.cs b/MediaBrowser.Model/Sync/LocalItem.cs index 37f605a59..dbbecaf05 100644 --- a/MediaBrowser.Model/Sync/LocalItem.cs +++ b/MediaBrowser.Model/Sync/LocalItem.cs @@ -31,6 +31,11 @@ namespace MediaBrowser.Model.Sync /// The item identifier. public string ItemId { get; set; } /// + /// Gets or sets the synchronize job item identifier. + /// + /// The synchronize job item identifier. + public string SyncJobItemId { get; set; } + /// /// Gets or sets the user ids with access. /// /// The user ids with access. diff --git a/MediaBrowser.Model/Sync/SyncDataRequest.cs b/MediaBrowser.Model/Sync/SyncDataRequest.cs index dc33239a0..0df4de86d 100644 --- a/MediaBrowser.Model/Sync/SyncDataRequest.cs +++ b/MediaBrowser.Model/Sync/SyncDataRequest.cs @@ -6,6 +6,7 @@ namespace MediaBrowser.Model.Sync { public List LocalItemIds { get; set; } public List OfflineUserIds { get; set; } + public List SyncJobItemIds { get; set; } public string TargetId { get; set; } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs index 5c033e6bd..f13c71c6d 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs @@ -2,7 +2,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using System; using System.Collections.Generic; using System.Linq; using System.Threading; diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 66eeb61f7..4fab95263 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -129,6 +129,11 @@ namespace MediaBrowser.Server.Implementations.Library return list; } + public Task> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken) + { + return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken); + } + public async Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, CancellationToken cancellationToken) { var item = _libraryManager.GetItemById(id); @@ -225,11 +230,6 @@ namespace MediaBrowser.Server.Implementations.Library } } - public Task> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken) - { - return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken); - } - public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) { return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 7f4440fbc..401cf8765 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -373,7 +373,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv StartDate = item.StartDate, OfficialRating = item.OfficialRating, IsHD = item.IsHD, - OriginalAirDate = item.PremiereDate, + OriginalAirDate = item.OriginalAirDate, Audio = item.Audio, CommunityRating = GetClientCommunityRating(item.CommunityRating), IsRepeat = item.IsRepeat, diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index f67691859..a39781d6a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -589,13 +589,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.Name = info.Name; item.OfficialRating = info.OfficialRating; item.Overview = info.Overview; - item.PremiereDate = info.OriginalAirDate; + item.OriginalAirDate = info.OriginalAirDate; item.ProviderImagePath = info.ImagePath; item.ProviderImageUrl = info.ImageUrl; item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks; item.StartDate = info.StartDate; - item.ProductionYear = info.ProductionYear; + item.ProductionYear = info.ProductionYear; + item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate; + await item.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); return item; diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index e7333939e..5bc8b8088 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -67,12 +67,12 @@ namespace MediaBrowser.Server.Implementations.Sync SyncTarget target, CancellationToken cancellationToken) { - var localIds = await dataProvider.GetServerItemIds(target, serverId).ConfigureAwait(false); + var jobItemIds = await dataProvider.GetSyncJobItemIds(target, serverId).ConfigureAwait(false); var result = await _syncManager.SyncData(new SyncDataRequest { TargetId = target.Id, - LocalItemIds = localIds + SyncJobItemIds = jobItemIds }).ConfigureAwait(false); @@ -285,11 +285,11 @@ namespace MediaBrowser.Server.Implementations.Sync private async Task RemoveItem(IServerSyncProvider provider, ISyncDataProvider dataProvider, string serverId, - string itemId, + string syncJobItemId, SyncTarget target, CancellationToken cancellationToken) { - var localItems = await dataProvider.GetCachedItems(target, serverId, itemId); + var localItems = await dataProvider.GetCachedItemsBySyncJobItemId(target, serverId, syncJobItemId); foreach (var localItem in localItems) { @@ -350,7 +350,8 @@ namespace MediaBrowser.Server.Implementations.Sync ItemId = libraryItem.Id, ServerId = serverId, LocalPath = localPath, - Id = GetLocalId(syncedItem.SyncJobItemId, libraryItem.Id) + Id = GetLocalId(syncedItem.SyncJobItemId, libraryItem.Id), + SyncJobItemId = syncedItem.SyncJobItemId }; } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 3acc79088..102218979 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Controller; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -42,7 +43,7 @@ namespace MediaBrowser.Server.Implementations.Sync private readonly ILogger _logger; private readonly IUserManager _userManager; private readonly Func _dtoService; - private readonly IApplicationHost _appHost; + private readonly IServerApplicationHost _appHost; private readonly ITVSeriesManager _tvSeriesManager; private readonly Func _mediaEncoder; private readonly IFileSystem _fileSystem; @@ -60,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Sync public event EventHandler> SyncJobItemUpdated; public event EventHandler> SyncJobItemCreated; - public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func mediaEncoder, IFileSystem fileSystem, Func subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func mediaSourceManager, IJsonSerializer json) + public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func dtoService, IServerApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func mediaEncoder, IFileSystem fileSystem, Func subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func mediaSourceManager, IJsonSerializer json) { _libraryManager = libraryManager; _repo = repo; @@ -94,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Sync public ISyncDataProvider GetDataProvider(IServerSyncProvider provider, SyncTarget target) { - return _dataProviders.GetOrAdd(target.Id, key => new TargetDataProvider(provider, target, _appHost.SystemId, _logger, _json, _fileSystem, _config.CommonApplicationPaths)); + return _dataProviders.GetOrAdd(target.Id, key => new TargetDataProvider(provider, target, _appHost, _logger, _json, _fileSystem, _config.CommonApplicationPaths)); } public async Task CreateJob(SyncJobRequest request) @@ -737,10 +738,15 @@ namespace MediaBrowser.Server.Implementations.Sync public async Task SyncData(SyncDataRequest request) { + if (request.SyncJobItemIds != null) + { + return await SyncDataUsingSyncJobItemIds(request).ConfigureAwait(false); + } + var jobItemResult = GetJobItems(new SyncJobItemQuery { TargetId = request.TargetId, - Statuses = new SyncJobItemStatus[] { SyncJobItemStatus.Synced } + Statuses = new[] { SyncJobItemStatus.Synced } }); var response = new SyncDataResponse(); @@ -816,6 +822,87 @@ namespace MediaBrowser.Server.Implementations.Sync return response; } + private async Task SyncDataUsingSyncJobItemIds(SyncDataRequest request) + { + var jobItemResult = GetJobItems(new SyncJobItemQuery + { + TargetId = request.TargetId, + Statuses = new[] { SyncJobItemStatus.Synced } + }); + + var response = new SyncDataResponse(); + + foreach (var jobItem in jobItemResult.Items) + { + if (request.SyncJobItemIds.Contains(jobItem.Id, StringComparer.OrdinalIgnoreCase)) + { + var job = _repo.GetJob(jobItem.JobId); + var user = _userManager.GetUserById(job.UserId); + + if (jobItem.IsMarkedForRemoval) + { + // Tell the device to remove it since it has been marked for removal + response.ItemIdsToRemove.Add(jobItem.Id); + } + else if (user == null) + { + // Tell the device to remove it since the user is gone now + response.ItemIdsToRemove.Add(jobItem.Id); + } + else if (job.UnwatchedOnly) + { + var libraryItem = _libraryManager.GetItemById(jobItem.ItemId); + + if (IsLibraryItemAvailable(libraryItem)) + { + if (libraryItem.IsPlayed(user) && libraryItem is Video) + { + // Tell the device to remove it since it has been played + response.ItemIdsToRemove.Add(jobItem.Id); + } + } + else + { + // Tell the device to remove it since it's no longer available + response.ItemIdsToRemove.Add(jobItem.Id); + } + } + } + else + { + // Content is no longer on the device + jobItem.Status = SyncJobItemStatus.RemovedFromDevice; + await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); + } + } + + // Now check each item that's on the device + foreach (var syncJobItemId in request.SyncJobItemIds) + { + // See if it's already marked for removal + if (response.ItemIdsToRemove.Contains(syncJobItemId, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + // If there isn't a sync job for this item, mark it for removal + if (!jobItemResult.Items.Any(i => string.Equals(syncJobItemId, i.Id, StringComparison.OrdinalIgnoreCase))) + { + response.ItemIdsToRemove.Add(syncJobItemId); + } + } + + response.ItemIdsToRemove = response.ItemIdsToRemove.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + + var itemsOnDevice = request.LocalItemIds + .Except(response.ItemIdsToRemove) + .ToList(); + + SetUserAccess(request, response, itemsOnDevice); + + return response; + } + private void SetUserAccess(SyncDataRequest request, SyncDataResponse response, List itemIds) { var users = request.OfflineUserIds diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs index ca9d96c12..dea868848 100644 --- a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Controller; using MediaBrowser.Controller.Sync; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; @@ -26,11 +27,11 @@ namespace MediaBrowser.Server.Implementations.Sync private readonly IJsonSerializer _json; private readonly IFileSystem _fileSystem; private readonly IApplicationPaths _appPaths; - private readonly string _serverId; + private readonly IServerApplicationHost _appHost; private readonly SemaphoreSlim _cacheFileLock = new SemaphoreSlim(1, 1); - public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, string serverId, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths) + public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths) { _logger = logger; _json = json; @@ -38,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Sync _target = target; _fileSystem = fileSystem; _appPaths = appPaths; - _serverId = serverId; + _appHost = appHost; } private string GetCachePath() @@ -50,13 +51,21 @@ namespace MediaBrowser.Server.Implementations.Sync { var parts = new List { - _serverId, + _appHost.FriendlyName, "data.json" }; + parts = parts.Select(i => GetValidFilename(_provider, i)).ToList(); + return _provider.GetFullPath(parts, _target); } + private string GetValidFilename(IServerSyncProvider provider, string filename) + { + // We can always add this method to the sync provider if it's really needed + return _fileSystem.GetValidFilename(filename); + } + private async Task CacheData(Stream stream) { var cachePath = GetCachePath(); @@ -167,6 +176,11 @@ namespace MediaBrowser.Server.Implementations.Sync return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).Select(i => i.ItemId).ToList()); } + public Task> GetSyncJobItemIds(SyncTarget target, string serverId) + { + return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).Select(i => i.SyncJobItemId).Where(i => !string.IsNullOrWhiteSpace(i)).ToList()); + } + public Task AddOrUpdate(SyncTarget target, LocalItem item) { return UpdateData(items => @@ -239,5 +253,13 @@ namespace MediaBrowser.Server.Implementations.Sync return items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase) && string.Equals(i.ItemId, itemId, StringComparison.OrdinalIgnoreCase)) .ToList(); } + + public async Task> GetCachedItemsBySyncJobItemId(SyncTarget target, string serverId, string syncJobItemId) + { + var items = await GetCachedData().ConfigureAwait(false); + + return items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase) && string.Equals(i.SyncJobItemId, syncJobItemId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + } } } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 40da3899c..c5acc0a42 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.609 + 3.0.611 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 7aa9d3055..1b29b0272 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.609 + 3.0.611 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 8d65ec431..6ee79dd8c 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.609 + 3.0.611 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 9a5264da9..c8b062f3f 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.609 + 3.0.611 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + -- cgit v1.2.3 From b9f3944b7f2cbfba1dadffd0ac0594ad3cd81f38 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 1 Apr 2015 17:56:32 -0400 Subject: rework media source methods --- .../Library/IMediaSourceManager.cs | 10 +------- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 4 +-- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 2 +- .../Library/MediaSourceManager.cs | 29 +++++----------------- SharedVersion.cs | 4 +-- 5 files changed, 12 insertions(+), 37 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs') diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 9cbbabc8d..5bcc5f313 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -63,15 +63,7 @@ namespace MediaBrowser.Controller.Library /// if set to true [enable path substitution]. /// The user. /// IEnumerable<MediaSourceInfo>. - IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user); - - /// - /// Gets the static media sources. - /// - /// The item. - /// if set to true [enable path substitution]. - /// IEnumerable<MediaSourceInfo>. - IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution); + IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null); /// /// Gets the static media source. diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 3b1cdb542..19dab2246 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -124,7 +124,7 @@ namespace MediaBrowser.Dlna.Didl { if (streamInfo == null) { - var sources = _user == null ? _mediaSourceManager.GetStaticMediaSources(video, true).ToList() : _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList(); + var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList(); streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions { @@ -351,7 +351,7 @@ namespace MediaBrowser.Dlna.Didl if (streamInfo == null) { - var sources = _user == null ? _mediaSourceManager.GetStaticMediaSources(audio, true).ToList() : _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); + var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions { diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 38c0f71cc..5b129243c 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -470,7 +470,7 @@ namespace MediaBrowser.Dlna.PlayTo var hasMediaSources = item as IHasMediaSources; var mediaSources = hasMediaSources != null - ? (user == null ? _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true) : _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList() + ? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList() : new List(); var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex); diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 4fab95263..27a7d4ea9 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -235,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.Library return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } - public IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution) + public IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null) { if (item == null) { @@ -247,31 +247,14 @@ namespace MediaBrowser.Server.Implementations.Library return item.GetMediaSources(enablePathSubstitution); } - return item.GetMediaSources(enablePathSubstitution); - } - - public IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (!(item is Video)) - { - return item.GetMediaSources(enablePathSubstitution); - } - - if (user == null) - { - throw new ArgumentNullException("user"); - } - var sources = item.GetMediaSources(enablePathSubstitution).ToList(); - foreach (var source in sources) + if (user != null) { - SetUserProperties(source, user); + foreach (var source in sources) + { + SetUserProperties(source, user); + } } return sources; diff --git a/SharedVersion.cs b/SharedVersion.cs index e530e59cb..11c0c6b5e 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5569.0")] +[assembly: AssemblyVersion("3.0.*")] +//[assembly: AssemblyVersion("3.0.5569.0")] -- cgit v1.2.3