diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2015-03-28 16:22:27 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2015-03-28 16:22:27 -0400 |
| commit | bd2ea703e31522d505407a33089b95f997f6b062 (patch) | |
| tree | ce9e3e4ea90c90fda7e287e841813b1aa0675294 /MediaBrowser.Server.Implementations | |
| parent | 3add1872c860cae14f85b339fef843ff962574aa (diff) | |
implement modular media sources
Diffstat (limited to 'MediaBrowser.Server.Implementations')
9 files changed, 339 insertions, 76 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs index e0b616605..980c3f31b 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs @@ -169,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Channels foreach (var item in result.Items) { - var channelItem = (IChannelItem)item; + var channelItem = (IChannelMediaItem)item; var channelFeatures = _manager.GetChannelFeatures(channelItem.ChannelId); @@ -179,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Channels { try { - await DownloadChannelItem(item, options, cancellationToken, path); + await DownloadChannelItem(channelItem, options, cancellationToken, path); } catch (OperationCanceledException) { @@ -210,13 +210,13 @@ namespace MediaBrowser.Server.Implementations.Channels return channelOptions.DownloadSizeLimit; } - private async Task DownloadChannelItem(BaseItem item, + private async Task DownloadChannelItem(IChannelMediaItem item, ChannelOptions channelOptions, CancellationToken cancellationToken, string path) { var itemId = item.Id.ToString("N"); - var sources = await _manager.GetChannelItemMediaSources(itemId, false, cancellationToken) + var sources = await _manager.GetStaticMediaSources(item, true, cancellationToken) .ConfigureAwait(false); var cachedVersions = sources.Where(i => i.Protocol == MediaProtocol.File).ToList(); @@ -237,11 +237,9 @@ namespace MediaBrowser.Server.Implementations.Channels } } - var channelItem = (IChannelMediaItem)item; + var destination = Path.Combine(path, item.ChannelId, itemId); - var destination = Path.Combine(path, channelItem.ChannelId, itemId); - - await _manager.DownloadChannelItem(channelItem, destination, new Progress<double>(), cancellationToken) + await _manager.DownloadChannelItem(item, destination, new Progress<double>(), cancellationToken) .ConfigureAwait(false); await RefreshMediaSourceItem(destination, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs new file mode 100644 index 000000000..6a7163bb3 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -0,0 +1,43 @@ +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Dto; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Channels +{ + public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider + { + private readonly ChannelManager _channelManager; + + public ChannelDynamicMediaSourceProvider(IChannelManager channelManager) + { + _channelManager = (ChannelManager)channelManager; + } + + public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken) + { + var channelItem = item as IChannelMediaItem; + + if (channelItem != null) + { + return _channelManager.GetDynamicMediaSources(channelItem, cancellationToken); + } + + return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>()); + } + + public Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index f0f30229e..e22bf2e7f 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -241,10 +241,25 @@ namespace MediaBrowser.Server.Implementations.Channels return item; } - public async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken) + public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken) { - var item = (IChannelMediaItem)_libraryManager.GetItemById(id); + IEnumerable<ChannelMediaInfo> results = item.ChannelMediaSources; + var sources = SortMediaInfoResults(results) + .Select(i => GetMediaSource(item, i)) + .ToList(); + + if (includeCachedVersions) + { + var cachedVersions = GetCachedChannelItemMediaSources(item); + sources.InsertRange(0, cachedVersions); + } + + return sources.Where(IsValidMediaSource); + } + + public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IChannelMediaItem item, CancellationToken cancellationToken) + { var channel = GetChannel(item.ChannelId); var channelPlugin = GetChannelProvider(channel); @@ -252,24 +267,25 @@ namespace MediaBrowser.Server.Implementations.Channels IEnumerable<ChannelMediaInfo> results; - if (requiresCallback != null && includeDynamicSources) + if (requiresCallback != null) { results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); } else { - results = item.ChannelMediaSources; + results = new List<ChannelMediaInfo>(); } - var sources = SortMediaInfoResults(results).Select(i => GetMediaSource(item, i)) + var list = SortMediaInfoResults(results) + .Select(i => GetMediaSource(item, i)) + .Where(IsValidMediaSource) .ToList(); var cachedVersions = GetCachedChannelItemMediaSources(item); + list.InsertRange(0, cachedVersions); - sources.InsertRange(0, cachedVersions); - - return sources.Where(IsValidMediaSource); + return list; } private readonly ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>> _channelItemMediaInfo = @@ -297,14 +313,7 @@ namespace MediaBrowser.Server.Implementations.Channels return list; } - public IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(string id) - { - var item = (IChannelMediaItem)_libraryManager.GetItemById(id); - - return GetCachedChannelItemMediaSources(item); - } - - public IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item) + private IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item) { var filenamePrefix = item.Id.ToString("N"); var parentPath = Path.Combine(ChannelDownloadPath, item.ChannelId); @@ -339,7 +348,6 @@ namespace MediaBrowser.Server.Implementations.Channels if (source != null) { - source.Type = MediaSourceType.Cache; return new[] { source }; } } @@ -1408,8 +1416,7 @@ namespace MediaBrowser.Server.Implementations.Channels public async Task DownloadChannelItem(IChannelMediaItem item, string destination, IProgress<double> progress, CancellationToken cancellationToken) { - var itemId = item.Id.ToString("N"); - var sources = await GetChannelItemMediaSources(itemId, true, cancellationToken) + var sources = await GetDynamicMediaSources(item, cancellationToken) .ConfigureAwait(false); var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList(); 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<MediaSourceInfo> 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<IEnumerable<MediaSourceInfo>> 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<IEnumerable<MediaSourceInfo>> 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<string, string> _openStreams = + new ConcurrentDictionary<string, string>(); + private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); + public async Task<MediaSourceInfo> 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<IMediaSourceProvider, string> 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<IMediaSourceProvider, string>(provider, keys[1]); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + } + + private readonly object _disposeLock = new object(); + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + 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(); + } + } + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 59daa4921..202a051e3 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,10 +1,8 @@ -using System.Globalization; -using MediaBrowser.Common; +using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -15,7 +13,6 @@ using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; @@ -342,6 +339,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv var service = GetService(channel); _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; } else { @@ -351,6 +350,8 @@ 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; } _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs new file mode 100644 index 000000000..186bc499d --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -0,0 +1,77 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.Dto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.LiveTv +{ + public class LiveTvMediaSourceProvider : IMediaSourceProvider + { + private readonly ILiveTvManager _liveTvManager; + + public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager) + { + _liveTvManager = liveTvManager; + } + + public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken) + { + var channelItem = item as ILiveTvItem; + + if (channelItem != null) + { + var hasMetadata = (IHasMetadata)channelItem; + + if (string.IsNullOrWhiteSpace(hasMetadata.Path)) + { + return GetMediaSourcesInternal(channelItem, cancellationToken); + } + } + + return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>()); + } + + private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(ILiveTvItem item, CancellationToken cancellationToken) + { + var hasMediaSources = (IHasMediaSources)item; + + var sources = hasMediaSources.GetMediaSources(false) + .ToList(); + + foreach (var source in sources) + { + source.Type = MediaSourceType.Default; + source.RequiresOpening = true; + + var openKeys = new List<string>(); + openKeys.Add(item.GetType().Name); + openKeys.Add(item.Id.ToString("N")); + source.OpenKey = string.Join("|", openKeys.ToArray()); + } + + return sources; + } + + public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken) + { + var keys = openKey.Split(new[] { '|' }, 2); + + if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase)) + { + return await _liveTvManager.GetChannelStream(keys[1], cancellationToken).ConfigureAwait(false); + } + + return await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false); + } + + public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + { + return _liveTvManager.CloseLiveStream(closeKey, cancellationToken); + } + } +} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 3eb414068..db2397d2f 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -111,6 +111,7 @@ <Compile Include="Branding\BrandingConfigurationFactory.cs" /> <Compile Include="Channels\ChannelConfigurations.cs" /> <Compile Include="Channels\ChannelDownloadScheduledTask.cs" /> + <Compile Include="Channels\ChannelDynamicMediaSourceProvider.cs" /> <Compile Include="Channels\ChannelImageProvider.cs" /> <Compile Include="Channels\ChannelItemImageProvider.cs" /> <Compile Include="Channels\ChannelManager.cs" /> @@ -225,6 +226,7 @@ <Compile Include="LiveTv\LiveTvConfigurationFactory.cs" /> <Compile Include="LiveTv\LiveTvDtoService.cs" /> <Compile Include="LiveTv\LiveTvManager.cs" /> + <Compile Include="LiveTv\LiveTvMediaSourceProvider.cs" /> <Compile Include="LiveTv\ProgramImageProvider.cs" /> <Compile Include="LiveTv\RecordingImageProvider.cs" /> <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" /> diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 03a7e92a4..dd8ce82ef 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -161,6 +161,7 @@ namespace MediaBrowser.Server.Implementations.Sync { mediaSource.Path = sendFileResult.Path; mediaSource.Protocol = sendFileResult.Protocol; + mediaSource.RequiredHttpHeaders = sendFileResult.RequiredHttpHeaders; mediaSource.SupportsTranscoding = false; } } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index 4172cfc2d..25a52fb95 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Sync; @@ -61,7 +62,7 @@ namespace MediaBrowser.Server.Implementations.Sync { foreach (var mediaSource in localItem.Item.MediaSources) { - await TryAddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget, cancellationToken).ConfigureAwait(false); + AddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget); } } } @@ -71,41 +72,70 @@ namespace MediaBrowser.Server.Implementations.Sync return list; } - private async Task TryAddMediaSource(List<MediaSourceInfo> list, + private void AddMediaSource(List<MediaSourceInfo> list, LocalItem item, MediaSourceInfo mediaSource, IServerSyncProvider provider, - SyncTarget target, - CancellationToken cancellationToken) + SyncTarget target) { + SetStaticMediaSourceInfo(item, mediaSource); + var requiresDynamicAccess = provider as IHasDynamicAccess; - if (requiresDynamicAccess == null) + if (requiresDynamicAccess != null) { - list.Add(mediaSource); - return; + mediaSource.RequiresOpening = true; + + var keyList = new List<string>(); + 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()); } + } - try - { - var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(item.LocalPath, target, cancellationToken).ConfigureAwait(false); + public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken) + { + var openKeys = openKey.Split(new[] { '|' }, 3); - foreach (var stream in mediaSource.MediaStreams) - { - var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); + var provider = _syncManager.ServerSyncProviders + .FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase)); - stream.Path = dynamicStreamInfo.Path; - } + var target = provider.GetAllSyncTargets() + .FirstOrDefault(i => string.Equals(openKeys[1], i.Id.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase)); - mediaSource.Path = dynamicInfo.Path; - mediaSource.Protocol = dynamicInfo.Protocol; + var dataProvider = _syncManager.GetDataProvider(provider, target); + var localItem = await dataProvider.Get(target, openKeys[2]).ConfigureAwait(false); - list.Add(mediaSource); - } - catch (Exception ex) + var requiresDynamicAccess = (IHasDynamicAccess)provider; + var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(localItem.LocalPath, target, cancellationToken).ConfigureAwait(false); + + var mediaSource = localItem.Item.MediaSources.First(); + SetStaticMediaSourceInfo(localItem, mediaSource); + + foreach (var stream in mediaSource.MediaStreams) { - _logger.ErrorException("Error getting dynamic media source info", ex); + var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); + + stream.Path = dynamicStreamInfo.Path; } + + mediaSource.Path = dynamicInfo.Path; + mediaSource.Protocol = dynamicInfo.Protocol; + mediaSource.RequiredHttpHeaders = dynamicInfo.RequiredHttpHeaders; + + return mediaSource; + } + + private void SetStaticMediaSourceInfo(LocalItem item, MediaSourceInfo mediaSource) + { + mediaSource.Id = item.Id; + mediaSource.SupportsTranscoding = false; + } + + public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken) + { + throw new NotImplementedException(); } } } |
