From 7e312e75bbb1324748319b29c9353716ed93a8da Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 26 Mar 2015 16:31:57 -0400 Subject: update stream sorting --- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 085408f2e2..2e829b436a 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.598 + 3.0.599 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool -- cgit v1.2.3 From d8cbd649176566dbdcc49e72d0fa2ddd4f25d536 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 27 Mar 2015 16:55:31 -0400 Subject: fix mp4 sync encoding --- .../Channels/ChannelMediaInfo.cs | 5 ++- MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs | 9 +++-- MediaBrowser.Model/ApiClient/ServerCredentials.cs | 21 +++++++--- MediaBrowser.Model/ApiClient/ServerInfo.cs | 8 +++- MediaBrowser.Model/Dlna/StreamInfoSorter.cs | 2 +- .../LiveTv/LiveTvManager.cs | 46 +++++++++++++++------- .../Sync/MediaSync.cs | 15 ++++--- MediaBrowser.Server.Mono/Native/BaseMonoApp.cs | 1 - Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- SharedVersion.cs | 4 +- 13 files changed, 82 insertions(+), 41 deletions(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 94fa1ac02f..1672b75fac 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -38,6 +38,7 @@ namespace MediaBrowser.Controller.Channels public string Id { get; set; } public bool ReadAtNativeFramerate { get; set; } + public bool SupportsDirectPlay { get; set; } public ChannelMediaInfo() { @@ -45,6 +46,7 @@ namespace MediaBrowser.Controller.Channels // This is most common Protocol = MediaProtocol.Http; + SupportsDirectPlay = true; } public MediaSourceInfo ToMediaSource() @@ -63,7 +65,8 @@ namespace MediaBrowser.Controller.Channels Name = id, Id = id, ReadAtNativeFramerate = ReadAtNativeFramerate, - SupportsDirectStream = Protocol == MediaProtocol.File || Protocol == MediaProtocol.Http + SupportsDirectStream = Protocol == MediaProtocol.File || Protocol == MediaProtocol.Http, + SupportsDirectPlay = SupportsDirectPlay }; var bitrate = (AudioBitrate ?? 0) + (VideoBitrate ?? 0); diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index a714b160ec..b8ed97b1fc 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -73,10 +73,13 @@ namespace MediaBrowser.MediaEncoding.Encoder args; } - var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", - 5.ToString(UsCulture)); + if (state.Options.Context == EncodingContext.Streaming) + { + var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", + 5.ToString(UsCulture)); - args += keyFrameArg; + args += keyFrameArg; + } var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; diff --git a/MediaBrowser.Model/ApiClient/ServerCredentials.cs b/MediaBrowser.Model/ApiClient/ServerCredentials.cs index d911f81211..6ba2a80c87 100644 --- a/MediaBrowser.Model/ApiClient/ServerCredentials.cs +++ b/MediaBrowser.Model/ApiClient/ServerCredentials.cs @@ -1,7 +1,6 @@ using MediaBrowser.Model.Extensions; using System; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Model.ApiClient { @@ -24,7 +23,12 @@ namespace MediaBrowser.Model.ApiClient throw new ArgumentNullException("server"); } - var list = Servers.ToList(); + // Clone the existing list of servers + var list = new List(); + foreach (ServerInfo serverInfo in Servers) + { + list.Add(serverInfo); + } var index = FindIndex(list, server.Id); @@ -32,8 +36,11 @@ namespace MediaBrowser.Model.ApiClient { var existing = list[index]; - // Merge the data - existing.DateLastAccessed = new[] { existing.DateLastAccessed, server.DateLastAccessed }.Max(); + // Take the most recent DateLastAccessed + if (server.DateLastAccessed > existing.DateLastAccessed) + { + existing.DateLastAccessed = server.DateLastAccessed; + } existing.UserLinkType = server.UserLinkType; @@ -64,7 +71,11 @@ namespace MediaBrowser.Model.ApiClient } if (server.WakeOnLanInfos != null && server.WakeOnLanInfos.Count > 0) { - existing.WakeOnLanInfos = server.WakeOnLanInfos.ToList(); + existing.WakeOnLanInfos = new List(); + foreach (WakeOnLanInfo info in server.WakeOnLanInfos) + { + existing.WakeOnLanInfos.Add(info); + } } if (server.LastConnectionMode.HasValue) { diff --git a/MediaBrowser.Model/ApiClient/ServerInfo.cs b/MediaBrowser.Model/ApiClient/ServerInfo.cs index cc062f2f60..e1fa581d7b 100644 --- a/MediaBrowser.Model/ApiClient/ServerInfo.cs +++ b/MediaBrowser.Model/ApiClient/ServerInfo.cs @@ -3,7 +3,6 @@ using MediaBrowser.Model.Extensions; using MediaBrowser.Model.System; using System; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Model.ApiClient { @@ -83,7 +82,12 @@ namespace MediaBrowser.Model.ApiClient throw new ArgumentNullException("user"); } - var list = Users.ToList(); + // Clone the existing list of users + var list = new List(); + foreach (ServerUserInfo serverUserInfo in Users) + { + list.Add(serverUserInfo); + } var index = FindIndex(list, user.Id); diff --git a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs index 87c6615480..0cccd80804 100644 --- a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs +++ b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Model.Dlna case PlayMethod.DirectPlay: return 0; default: - return 2; + return 1; } }).ThenBy(i => diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 2bc6cbadfe..59daa4921a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -333,10 +333,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv try { MediaSourceInfo info; + var isVideo = true; if (isChannel) { var channel = GetInternalChannel(id); + isVideo = channel.ChannelType == ChannelType.TV; 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); @@ -344,6 +346,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv else { var recording = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false); + isVideo = !string.Equals(recording.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase); var service = GetService(recording); _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.RecordingInfo.Id); @@ -351,7 +354,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); - Sanitize(info); + Normalize(info, isVideo); var data = new LiveStreamData { @@ -377,25 +380,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - private void Sanitize(MediaSourceInfo mediaSource) + private void Normalize(MediaSourceInfo mediaSource, bool isVideo) { if (mediaSource.MediaStreams.Count == 0) { - mediaSource.MediaStreams.AddRange(new List + if (isVideo) { - new MediaStream + mediaSource.MediaStreams.AddRange(new List { - 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 - }, - new MediaStream + new MediaStream + { + 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 + }, + new MediaStream + { + Type = MediaStreamType.Audio, + // Set the index to -1 because we don't know the exact index of the audio stream within the container + Index = -1 + } + }); + } + else + { + mediaSource.MediaStreams.AddRange(new List { - Type = MediaStreamType.Audio, - // Set the index to -1 because we don't know the exact index of the audio stream within the container - Index = -1 - } - }); + new MediaStream + { + Type = MediaStreamType.Audio, + // Set the index to -1 because we don't know the exact index of the audio stream within the container + Index = -1 + } + }); + } } // Clean some bad data coming from providers diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 936f1b1fe8..cac0e15546 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -1,5 +1,4 @@ -using System.IO; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Sync; @@ -10,6 +9,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Sync; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -140,9 +140,6 @@ namespace MediaBrowser.Server.Implementations.Sync var libraryItem = jobItem.Item; var internalSyncJobItem = _syncManager.GetJobItem(jobItem.SyncJobItemId); - var fileTransferProgress = new ActionableProgress(); - fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92)); - var localItem = CreateLocalItem(provider, jobItem, target, libraryItem, serverId, jobItem.OriginalFileName); await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id); @@ -152,6 +149,9 @@ namespace MediaBrowser.Server.Implementations.Sync try { + var fileTransferProgress = new ActionableProgress(); + fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92)); + var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath, target, fileTransferProgress, cancellationToken).ConfigureAwait(false); if (localItem.Item.MediaSources != null) @@ -342,7 +342,10 @@ namespace MediaBrowser.Server.Implementations.Sync private string GetSyncJobFolderName(SyncedItem syncedItem, IServerSyncProvider provider) { - var name = syncedItem.SyncJobName + syncedItem.SyncJobDateCreated.ToLocalTime().ToString("g"); + var name = syncedItem.SyncJobName + "-" + syncedItem.SyncJobDateCreated + .ToLocalTime() + .ToString("g") + .Replace(" ", "-"); name = GetValidFilename(provider, name); diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index 1ec0109ad8..aea6d73679 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -139,7 +139,6 @@ namespace MediaBrowser.Server.Mono.Native } else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase)) { - // TODO: How to detect BSD? info.OperatingSystem = Startup.Common.OperatingSystem.Bsd; } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index d229302bde..dccef68ed4 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.599 + 3.0.600 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 65205fa250..b35ce68d6d 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.599 + 3.0.600 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 2e829b436a..ea45fdfe19 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.599 + 3.0.600 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index f5d5b788bb..6fc7e46393 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.599 + 3.0.600 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + diff --git a/SharedVersion.cs b/SharedVersion.cs index fa4ca1ac7e..621b97a63d 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("3.0.*")] -//[assembly: AssemblyVersion("3.0.5557.20000")] +//[assembly: AssemblyVersion("3.0.*")] +[assembly: AssemblyVersion("3.0.5557.30000")] -- cgit v1.2.3 From d12bcc2d249bce7f04c7927058366dff49161098 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 27 Mar 2015 22:19:20 -0400 Subject: sync updates --- .../MediaBrowser.Controller.csproj | 1 + .../Sync/IRequiresDynamicAccess.cs | 18 ++++++++ .../Sync/IServerSyncProvider.cs | 9 ---- MediaBrowser.Model/Entities/MediaStream.cs | 6 +++ MediaBrowser.Model/Sync/LocalItem.cs | 6 +++ .../Sync/MediaSync.cs | 52 +++++----------------- .../Sync/SyncedMediaSourceProvider.cs | 51 +++++++++++++++++++-- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 11 files changed, 95 insertions(+), 60 deletions(-) create mode 100644 MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index d7a69ceb22..809d1f6f46 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -393,6 +393,7 @@ + diff --git a/MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs b/MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs new file mode 100644 index 0000000000..820a1dce08 --- /dev/null +++ b/MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs @@ -0,0 +1,18 @@ +using MediaBrowser.Model.Sync; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Sync +{ + public interface IRequiresDynamicAccess + { + /// + /// Gets the file information. + /// + /// The remote path. + /// The target. + /// The cancellation token. + /// Task<SendFileResult>. + Task GetFileInfo(string remotePath, SyncTarget target, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index abf884e9d5..9cccd41509 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -54,14 +54,5 @@ namespace MediaBrowser.Controller.Sync /// The target. /// System.String. string GetParentDirectoryPath(string path, SyncTarget target); - - /// - /// Gets the file system entries. - /// - /// The path. - /// The target. - /// The cancellation token. - /// Task<List<DeviceFileInfo>>. - Task> GetFileSystemEntries(string path, SyncTarget target, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 4af32bb500..66fb486284 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -168,6 +168,12 @@ namespace MediaBrowser.Model.Entities /// The filename. public string Path { get; set; } + /// + /// Gets or sets the external identifier. + /// + /// The external identifier. + public string ExternalId { get; set; } + /// /// Gets or sets the pixel format. /// diff --git a/MediaBrowser.Model/Sync/LocalItem.cs b/MediaBrowser.Model/Sync/LocalItem.cs index c6a10298fe..37f605a590 100644 --- a/MediaBrowser.Model/Sync/LocalItem.cs +++ b/MediaBrowser.Model/Sync/LocalItem.cs @@ -35,9 +35,15 @@ namespace MediaBrowser.Model.Sync /// /// The user ids with access. public List UserIdsWithAccess { get; set; } + /// + /// Gets or sets the additional files. + /// + /// The additional files. + public List AdditionalFiles { get; set; } public LocalItem() { + AdditionalFiles = new List(); UserIdsWithAccess = new List(); } } diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index cac0e15546..befddabd8a 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -215,6 +215,13 @@ namespace MediaBrowser.Server.Implementations.Sync var remotePath = GetRemoteSubtitlePath(localItem, mediaStream, provider, target); var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, new Progress(), cancellationToken).ConfigureAwait(false); + // This is the path that will be used when talking to the provider + mediaStream.ExternalId = remotePath; + + // Keep track of all additional files for cleanup later. + localItem.AdditionalFiles.Add(remotePath); + + // This is the public path clients will use mediaStream.Path = sendFileResult.Path; requiresSave = true; } @@ -280,13 +287,14 @@ namespace MediaBrowser.Server.Implementations.Sync foreach (var localItem in localItems) { - var files = await GetFiles(provider, localItem, target, cancellationToken); + var files = localItem.AdditionalFiles.ToList(); + files.Insert(0, localItem.LocalPath); foreach (var file in files) { - _logger.Debug("Removing {0} from {1}.", file.Path, target.Name); + _logger.Debug("Removing {0} from {1}.", file, target.Name); - await provider.DeleteFile(file.Path, target, cancellationToken).ConfigureAwait(false); + await provider.DeleteFile(file, target, cancellationToken).ConfigureAwait(false); } await dataProvider.Delete(target, localItem.Id).ConfigureAwait(false); @@ -417,43 +425,5 @@ namespace MediaBrowser.Server.Implementations.Sync // We can always add this method to the sync provider if it's really needed return _fileSystem.GetValidFilename(filename); } - - private async Task> GetFiles(IServerSyncProvider provider, LocalItem item, SyncTarget target, CancellationToken cancellationToken) - { - var path = item.LocalPath; - path = provider.GetParentDirectoryPath(path, target); - - var list = await provider.GetFileSystemEntries(path, target, cancellationToken).ConfigureAwait(false); - - var itemFiles = new List(); - - var name = Path.GetFileNameWithoutExtension(item.LocalPath); - - foreach (var file in list.Where(f => f.Name.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)) - { - var itemFile = new ItemFileInfo - { - Path = file.Path, - Name = file.Name - }; - - if (IsSubtitleFile(file.Name)) - { - itemFile.Type = ItemFileType.Subtitles; - } - - itemFiles.Add(itemFile); - } - - return itemFiles; - } - - private static readonly string[] SupportedSubtitleExtensions = { ".srt", ".vtt" }; - private bool IsSubtitleFile(string path) - { - var ext = Path.GetExtension(path) ?? string.Empty; - - return SupportedSubtitleExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); - } } } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index 893b16b140..e7e1d1c633 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Sync; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Sync; using System; using System.Collections.Generic; @@ -16,10 +17,12 @@ namespace MediaBrowser.Server.Implementations.Sync { private readonly SyncManager _syncManager; private readonly IServerApplicationHost _appHost; + private readonly ILogger _logger; - public SyncedMediaSourceProvider(ISyncManager syncManager, IServerApplicationHost appHost) + public SyncedMediaSourceProvider(ISyncManager syncManager, IServerApplicationHost appHost, ILogger logger) { _appHost = appHost; + _logger = logger; _syncManager = (SyncManager)syncManager; } @@ -28,7 +31,7 @@ namespace MediaBrowser.Server.Implementations.Sync var jobItemResult = _syncManager.GetJobItems(new SyncJobItemQuery { AddMetadata = false, - Statuses = new SyncJobItemStatus[] { SyncJobItemStatus.Synced }, + Statuses = new[] { SyncJobItemStatus.Synced }, ItemId = item.Id.ToString("N") }); @@ -49,14 +52,17 @@ namespace MediaBrowser.Server.Implementations.Sync if (targetTuple != null) { var syncTarget = targetTuple.Item2; - + var syncProvider = targetTuple.Item1; var dataProvider = _syncManager.GetDataProvider(targetTuple.Item1, syncTarget); var localItems = await dataProvider.GetCachedItems(syncTarget, serverId, item.Id.ToString("N")).ConfigureAwait(false); foreach (var localItem in localItems) { - list.AddRange(localItem.Item.MediaSources); + foreach (var mediaSource in localItem.Item.MediaSources) + { + await TryAddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget, cancellationToken).ConfigureAwait(false); + } } } } @@ -64,5 +70,42 @@ namespace MediaBrowser.Server.Implementations.Sync return list; } + + private async Task TryAddMediaSource(List list, + LocalItem item, + MediaSourceInfo mediaSource, + IServerSyncProvider provider, + SyncTarget target, + CancellationToken cancellationToken) + { + var requiresDynamicAccess = provider as IRequiresDynamicAccess; + + if (requiresDynamicAccess == null) + { + list.Add(mediaSource); + return; + } + + try + { + var dynamicInfo = await requiresDynamicAccess.GetFileInfo(item.LocalPath, target, cancellationToken).ConfigureAwait(false); + + foreach (var stream in mediaSource.MediaStreams) + { + var dynamicStreamInfo = await requiresDynamicAccess.GetFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); + + stream.Path = dynamicStreamInfo.Path; + } + + mediaSource.Path = dynamicInfo.Path; + mediaSource.Protocol = dynamicInfo.Protocol; + + list.Add(mediaSource); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting dynamic media source info", ex); + } + } } } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index dccef68ed4..84187e72bc 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.600 + 3.0.602 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index b35ce68d6d..59ec2aa65b 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.600 + 3.0.602 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index ea45fdfe19..6f4b6a7e39 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.600 + 3.0.602 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 6fc7e46393..4d919a965e 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.600 + 3.0.602 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + -- cgit v1.2.3 From bda0b2f7c490fbd2f462902aba13ee7db80688c3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 28 Mar 2015 01:07:29 -0400 Subject: sync updates --- .../MediaBrowser.Controller.csproj | 4 +-- MediaBrowser.Controller/Sync/IHasDynamicAccess.cs | 18 ++++++++++++++ .../Sync/IRequiresDynamicAccess.cs | 18 -------------- .../Sync/IServerSyncProvider.cs | 2 +- MediaBrowser.Controller/Sync/SendFileResult.cs | 18 -------------- MediaBrowser.Controller/Sync/SyncedFileInfo.cs | 29 ++++++++++++++++++++++ .../Sync/MediaSync.cs | 2 +- .../Sync/SyncedMediaSourceProvider.cs | 6 ++--- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +-- 12 files changed, 60 insertions(+), 49 deletions(-) create mode 100644 MediaBrowser.Controller/Sync/IHasDynamicAccess.cs delete mode 100644 MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs delete mode 100644 MediaBrowser.Controller/Sync/SendFileResult.cs create mode 100644 MediaBrowser.Controller/Sync/SyncedFileInfo.cs (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 809d1f6f46..2fa62f79f2 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -393,13 +393,13 @@ - + - + diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs new file mode 100644 index 0000000000..f907de7290 --- /dev/null +++ b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs @@ -0,0 +1,18 @@ +using MediaBrowser.Model.Sync; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Sync +{ + public interface IHasDynamicAccess + { + /// + /// Gets the synced file information. + /// + /// The remote path. + /// The target. + /// The cancellation token. + /// Task<SyncedFileInfo>. + Task GetSyncedFileInfo(string remotePath, SyncTarget target, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs b/MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs deleted file mode 100644 index 820a1dce08..0000000000 --- a/MediaBrowser.Controller/Sync/IRequiresDynamicAccess.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MediaBrowser.Model.Sync; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Sync -{ - public interface IRequiresDynamicAccess - { - /// - /// Gets the file information. - /// - /// The remote path. - /// The target. - /// The cancellation token. - /// Task<SendFileResult>. - Task GetFileInfo(string remotePath, SyncTarget target, CancellationToken cancellationToken); - } -} diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 9cccd41509..46bbbd3299 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Sync /// The progress. /// The cancellation token. /// Task. - Task SendFile(Stream stream, string remotePath, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + Task SendFile(Stream stream, string remotePath, SyncTarget target, IProgress progress, CancellationToken cancellationToken); /// /// Deletes the file. diff --git a/MediaBrowser.Controller/Sync/SendFileResult.cs b/MediaBrowser.Controller/Sync/SendFileResult.cs deleted file mode 100644 index 62753444a4..0000000000 --- a/MediaBrowser.Controller/Sync/SendFileResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.Controller.Sync -{ - public class SendFileResult - { - /// - /// Gets or sets the path. - /// - /// The path. - public string Path { get; set; } - /// - /// Gets or sets the protocol. - /// - /// The protocol. - public MediaProtocol Protocol { get; set; } - } -} diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs new file mode 100644 index 0000000000..550af2d554 --- /dev/null +++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs @@ -0,0 +1,29 @@ +using MediaBrowser.Model.MediaInfo; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Sync +{ + public class SyncedFileInfo + { + /// + /// Gets or sets the path. + /// + /// The path. + public string Path { get; set; } + /// + /// Gets or sets the protocol. + /// + /// The protocol. + public MediaProtocol Protocol { get; set; } + /// + /// Gets or sets the required HTTP headers. + /// + /// The required HTTP headers. + public Dictionary RequiredHttpHeaders { get; set; } + + public SyncedFileInfo() + { + RequiredHttpHeaders = new Dictionary(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index befddabd8a..03a7e92a45 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -301,7 +301,7 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private async Task SendFile(IServerSyncProvider provider, string inputPath, string remotePath, SyncTarget target, IProgress progress, CancellationToken cancellationToken) + private async Task SendFile(IServerSyncProvider provider, string inputPath, string remotePath, SyncTarget target, IProgress progress, CancellationToken cancellationToken) { _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, remotePath); using (var stream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index e7e1d1c633..4172cfc2da 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.Sync SyncTarget target, CancellationToken cancellationToken) { - var requiresDynamicAccess = provider as IRequiresDynamicAccess; + var requiresDynamicAccess = provider as IHasDynamicAccess; if (requiresDynamicAccess == null) { @@ -88,11 +88,11 @@ namespace MediaBrowser.Server.Implementations.Sync try { - var dynamicInfo = await requiresDynamicAccess.GetFileInfo(item.LocalPath, target, cancellationToken).ConfigureAwait(false); + var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(item.LocalPath, target, cancellationToken).ConfigureAwait(false); foreach (var stream in mediaSource.MediaStreams) { - var dynamicStreamInfo = await requiresDynamicAccess.GetFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); + var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false); stream.Path = dynamicStreamInfo.Path; } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 84187e72bc..31441ff791 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.602 + 3.0.603 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 59ec2aa65b..568a47dfe0 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.602 + 3.0.603 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 6f4b6a7e39..fb00fd8407 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.602 + 3.0.603 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 4d919a965e..c36fb6d6cd 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.602 + 3.0.603 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + -- cgit v1.2.3 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 --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 255 ++++++--------------- MediaBrowser.Api/Playback/Dash/MpegDashService.cs | 6 +- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 6 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 11 +- MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 2 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 108 ++++++--- .../Playback/Progressive/AudioService.cs | 6 +- .../Progressive/BaseProgressiveStreamingService.cs | 2 +- .../Playback/Progressive/VideoService.cs | 6 +- MediaBrowser.Api/Playback/StreamState.cs | 23 +- MediaBrowser.Api/Subtitles/SubtitleService.cs | 2 +- .../Channels/ChannelAudioItem.cs | 16 +- .../Channels/ChannelVideoItem.cs | 16 +- .../Channels/IChannelManager.cs | 6 +- .../Library/IMediaSourceManager.cs | 24 ++ .../Library/IMediaSourceProvider.cs | 16 ++ MediaBrowser.Controller/LiveTv/ILiveTvItem.cs | 4 +- .../LiveTv/LiveTvAudioRecording.cs | 21 +- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 2 +- .../LiveTv/LiveTvVideoRecording.cs | 23 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 2 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 24 +- MediaBrowser.Model/Dlna/SubtitleProfile.cs | 27 ++- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 5 + MediaBrowser.Model/Dto/MediaSourceType.cs | 2 +- MediaBrowser.Model/Entities/MediaStream.cs | 5 + .../MediaInfo/PlaybackInfoRequest.cs | 2 - .../MediaInfo/AudioImageProvider.cs | 2 +- .../Channels/ChannelDownloadScheduledTask.cs | 14 +- .../Channels/ChannelDynamicMediaSourceProvider.cs | 43 ++++ .../Channels/ChannelManager.cs | 47 ++-- .../Library/MediaSourceManager.cs | 148 ++++++++++-- .../LiveTv/LiveTvManager.cs | 9 +- .../LiveTv/LiveTvMediaSourceProvider.cs | 77 +++++++ .../MediaBrowser.Server.Implementations.csproj | 2 + .../Sync/MediaSync.cs | 1 + .../Sync/SyncedMediaSourceProvider.cs | 74 ++++-- .../ApplicationHost.cs | 2 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 6 +- Nuget/MediaBrowser.Model.Signed.nuspec | 6 +- Nuget/MediaBrowser.Server.Core.nuspec | 6 +- 42 files changed, 697 insertions(+), 366 deletions(-) create mode 100644 MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs create mode 100644 MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index bc194b45b0..435bda2c44 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; @@ -65,7 +64,6 @@ namespace MediaBrowser.Api.Playback protected IFileSystem FileSystem { get; private set; } - protected ILiveTvManager LiveTvManager { get; private set; } protected IDlnaManager DlnaManager { get; private set; } protected IDeviceManager DeviceManager { get; private set; } protected ISubtitleEncoder SubtitleEncoder { get; private set; } @@ -75,14 +73,13 @@ namespace MediaBrowser.Api.Playback /// /// Initializes a new instance of the class. /// - protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) + protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) { ZipClient = zipClient; MediaSourceManager = mediaSourceManager; DeviceManager = deviceManager; SubtitleEncoder = subtitleEncoder; DlnaManager = dlnaManager; - LiveTvManager = liveTvManager; FileSystem = fileSystem; ServerConfigurationManager = serverConfig; UserManager = userManager; @@ -95,11 +92,10 @@ namespace MediaBrowser.Api.Playback /// Gets the command line arguments. /// /// The output path. - /// The transcoding job identifier. /// The state. /// if set to true [is encoding]. /// System.String. - protected abstract string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding); + protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding); /// /// Gets the type of the transcoding job. @@ -128,7 +124,7 @@ namespace MediaBrowser.Api.Playback var outputFileExtension = GetOutputFileExtension(state); - var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false); + var data = GetCommandLineArguments("dummy\\dummy", state, false); data += "-" + (state.Request.DeviceId ?? string.Empty); data += "-" + (state.Request.StreamId ?? string.Empty); @@ -719,8 +715,10 @@ namespace MediaBrowser.Api.Playback seconds.ToString(UsCulture)); } + var mediaPath = state.MediaPath ?? string.Empty; + return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB", - state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"), + mediaPath.Replace('\\', '/').Replace(":/", "\\:/"), state.InternalSubtitleStreamOffset.ToString(UsCulture), seconds.ToString(UsCulture)); } @@ -895,12 +893,11 @@ namespace MediaBrowser.Api.Playback /// /// Gets the input argument. /// - /// The transcoding job identifier. /// The state. /// System.String. - protected string GetInputArgument(string transcodingJobId, StreamState state) + protected string GetInputArgument(StreamState state) { - var arg = "-i " + GetInputPathArgument(transcodingJobId, state); + var arg = "-i " + GetInputPathArgument(state); if (state.SubtitleStream != null) { @@ -913,27 +910,18 @@ namespace MediaBrowser.Api.Playback return arg; } - private string GetInputPathArgument(string transcodingJobId, StreamState state) + private string GetInputPathArgument(StreamState state) { - //if (state.InputProtocol == MediaProtocol.File && - // state.RunTimeTicks.HasValue && - // state.VideoType == VideoType.VideoFile && - // !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) - //{ - // if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo) - // { - // } - //} - var protocol = state.InputProtocol; + var mediaPath = state.MediaPath ?? string.Empty; - var inputPath = new[] { state.MediaPath }; + var inputPath = new[] { mediaPath }; if (state.IsInputVideo) { if (!(state.VideoType == VideoType.Iso && state.IsoMount == null)) { - inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames); + inputPath = MediaEncoderHelpers.GetInputArgument(mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames); } } @@ -947,55 +935,20 @@ namespace MediaBrowser.Api.Playback state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false); } - if (string.IsNullOrEmpty(state.MediaPath)) + if (state.MediaSource.RequiresOpening) { - var checkCodecs = false; - - if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name)) - { - var streamInfo = await LiveTvManager.GetChannelStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false); + var mediaSource = await MediaSourceManager.OpenMediaSource(state.MediaSource.OpenKey, cancellationTokenSource.Token) + .ConfigureAwait(false); - state.LiveTvStreamId = streamInfo.Id; + AttachMediaSourceInfo(state, mediaSource, state.VideoRequest, state.RequestedUrl); - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; - - await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); - - AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl); - checkCodecs = true; - } - - else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) || - string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name)) + if (state.VideoRequest != null) { - var streamInfo = await LiveTvManager.GetRecordingStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; - - await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); - - AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl); - checkCodecs = true; + TryStreamCopy(state, state.VideoRequest); } - var videoRequest = state.VideoRequest; - - if (videoRequest != null && checkCodecs) - { - if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) - { - state.OutputVideoCodec = "copy"; - } - - if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs)) - { - state.OutputAudioCodec = "copy"; - } - } + // TODO: This is only needed for live tv + await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } } @@ -1017,7 +970,7 @@ namespace MediaBrowser.Api.Playback await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false); var transcodingId = Guid.NewGuid().ToString("N"); - var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true); + var commandLineArgs = GetCommandLineArguments(outputPath, state, true); if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging) { @@ -1644,7 +1597,7 @@ namespace MediaBrowser.Api.Playback request.AudioCodec = InferAudioCodec(url); } - var state = new StreamState(LiveTvManager, Logger) + var state = new StreamState(MediaSourceManager, Logger) { Request = request, RequestedUrl = url @@ -1658,109 +1611,20 @@ namespace MediaBrowser.Api.Playback var item = LibraryManager.GetItemById(request.Id); - List mediaStreams = null; + state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - state.ItemType = item.GetType().Name; - state.ItemId = item.Id.ToString("N"); var archivable = item as IArchivable; state.IsInputArchive = archivable != null && archivable.IsArchive; - if (item is ILiveTvRecording) - { - var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false); - - state.VideoType = VideoType.VideoFile; - state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - - var path = recording.RecordingInfo.Path; - var mediaUrl = recording.RecordingInfo.Url; - - var source = string.IsNullOrEmpty(request.MediaSourceId) - ? recording.GetMediaSources(false).First() - : MediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false); - - mediaStreams = source.MediaStreams; - - // Just to prevent this from being null and causing other methods to fail - state.MediaPath = string.Empty; - - if (!string.IsNullOrEmpty(path)) - { - state.MediaPath = path; - state.InputProtocol = MediaProtocol.File; - } - else if (!string.IsNullOrEmpty(mediaUrl)) - { - state.MediaPath = mediaUrl; - state.InputProtocol = MediaProtocol.Http; - } - - state.RunTimeTicks = recording.RunTimeTicks; - state.DeInterlace = true; - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - state.InputContainer = recording.Container; - state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate; - } - else if (item is LiveTvChannel) - { - var channel = LiveTvManager.GetInternalChannel(request.Id); - - state.VideoType = VideoType.VideoFile; - state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - mediaStreams = new List(); - - state.DeInterlace = true; - - // Just to prevent this from being null and causing other methods to fail - state.MediaPath = string.Empty; - } - else - { - var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false); - - var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) - ? mediaSources.First() - : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - - mediaStreams = mediaSource.MediaStreams; - - state.MediaPath = mediaSource.Path; - state.InputProtocol = mediaSource.Protocol; - state.InputContainer = mediaSource.Container; - state.InputFileSize = mediaSource.Size; - state.InputBitrate = mediaSource.Bitrate; - state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - state.RunTimeTicks = mediaSource.RunTimeTicks; - state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - - var video = item as Video; - - if (video != null) - { - state.IsInputVideo = true; - - if (mediaSource.VideoType.HasValue) - { - state.VideoType = mediaSource.VideoType.Value; - } - - state.IsoType = mediaSource.IsoType; - - state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); - - if (mediaSource.Timestamp.HasValue) - { - state.InputTimestamp = mediaSource.Timestamp.Value; - } - } - - } + var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, 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; - AttachMediaStreamInfo(state, mediaStreams, videoRequest, url); + AttachMediaSourceInfo(state, mediaSource, videoRequest, url); var container = Path.GetExtension(state.RequestedUrl); @@ -1801,15 +1665,7 @@ namespace MediaBrowser.Api.Playback if (videoRequest != null) { - if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) - { - state.OutputVideoCodec = "copy"; - } - - if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs)) - { - state.OutputAudioCodec = "copy"; - } + TryStreamCopy(state, videoRequest); } state.OutputFilePath = GetOutputFilePath(state); @@ -1817,11 +1673,47 @@ namespace MediaBrowser.Api.Playback return state; } - private void AttachMediaStreamInfo(StreamState state, + private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest) + { + if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) + { + state.OutputVideoCodec = "copy"; + } + + if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs)) + { + state.OutputAudioCodec = "copy"; + } + } + + private void AttachMediaSourceInfo(StreamState state, MediaSourceInfo mediaSource, VideoStreamRequest videoRequest, string requestedUrl) { + state.MediaPath = mediaSource.Path; + state.InputProtocol = mediaSource.Protocol; + state.InputContainer = mediaSource.Container; + state.InputFileSize = mediaSource.Size; + state.InputBitrate = mediaSource.Bitrate; + state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + state.RunTimeTicks = mediaSource.RunTimeTicks; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + + if (mediaSource.VideoType.HasValue) + { + state.VideoType = mediaSource.VideoType.Value; + } + + state.IsoType = mediaSource.IsoType; + + state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); + + if (mediaSource.Timestamp.HasValue) + { + state.InputTimestamp = mediaSource.Timestamp.Value; + } + state.InputProtocol = mediaSource.Protocol; state.MediaPath = mediaSource.Path; state.RunTimeTicks = mediaSource.RunTimeTicks; @@ -1830,21 +1722,16 @@ namespace MediaBrowser.Api.Playback state.InputFileSize = mediaSource.Size; state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - if (state.ReadInputAtNativeFramerate) + if (state.ReadInputAtNativeFramerate || + mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) { state.OutputAudioSync = "1000"; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; } - AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl); - } + var mediaStreams = mediaSource.MediaStreams; - private void AttachMediaStreamInfo(StreamState state, - List mediaStreams, - VideoStreamRequest videoRequest, - string requestedUrl) - { if (videoRequest != null) { if (string.IsNullOrEmpty(videoRequest.VideoCodec)) @@ -1873,7 +1760,7 @@ namespace MediaBrowser.Api.Playback state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } - state.AllMediaStreams = mediaStreams; + state.MediaSource = mediaSource; } private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs index 3c3f4c0397..692e8d4e74 100644 --- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Api.Playback.Dash public class MpegDashService : BaseHlsService { - public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) + public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) { NetworkManager = networkManager; } @@ -447,7 +447,7 @@ namespace MediaBrowser.Api.Playback.Dash return args; } - protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) + protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { // test url http://192.168.1.2:8096/videos/233e8905d559a8f230db9bffd2ac9d6d/master.mpd?mediasourceid=233e8905d559a8f230db9bffd2ac9d6d&videocodec=h264&audiocodec=aac&maxwidth=1280&videobitrate=500000&audiobitrate=128000&profile=baseline&level=3 // Good info on i-frames http://blog.streamroot.io/encode-multi-bitrate-videos-mpeg-dash-mse-based-media-players/ @@ -461,7 +461,7 @@ namespace MediaBrowser.Api.Playback.Dash var args = string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts {5} -f dash -init_seg_name \"{6}\" -media_seg_name \"{7}\" -use_template 0 -use_timeline 1 -min_seg_duration {8} -y \"{9}\"", inputModifier, - GetInputArgument(transcodingJobId, state), + GetInputArgument(state), threads, GetMapArgs(state), GetVideoArguments(state), diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 09574e772c..207bc2f679 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls /// public abstract class BaseHlsService : BaseStreamingService { - protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) + protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) { } @@ -212,7 +212,7 @@ namespace MediaBrowser.Api.Playback.Hls } } - protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) + protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; @@ -240,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls var args = string.Format("{0} {1} {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", itsOffset, inputModifier, - GetInputArgument(transcodingJobId, state), + GetInputArgument(state), threads, GetMapArgs(state), GetVideoArguments(state), diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 48523e2556..b166bc319f 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { - public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) + public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) { NetworkManager = networkManager; } @@ -414,7 +414,8 @@ namespace MediaBrowser.Api.Playback.Hls var request = (GetMasterHlsVideoStream)state.Request; - var subtitleStreams = state.AllMediaStreams + var subtitleStreams = state.MediaSource + .MediaStreams .Where(i => i.IsTextSubtitleStream) .ToList(); @@ -684,7 +685,7 @@ namespace MediaBrowser.Api.Playback.Hls return args; } - protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) + protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { var threads = GetNumberOfThreads(state, false); @@ -699,7 +700,7 @@ namespace MediaBrowser.Api.Playback.Hls 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}\"", inputModifier, - GetInputArgument(transcodingJobId, state), + GetInputArgument(state), threads, GetMapArgs(state), GetVideoArguments(state), @@ -713,7 +714,7 @@ namespace MediaBrowser.Api.Playback.Hls 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}\"", inputModifier, - GetInputArgument(transcodingJobId, state), + GetInputArgument(state), threads, GetMapArgs(state), GetVideoArguments(state), diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index e1baf8c121..b1964f4aef 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback.Hls /// public class VideoHlsService : BaseHlsService { - public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) + public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) { } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index e219f41869..cef8a34e57 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Session; using ServiceStack; @@ -38,20 +39,23 @@ 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 = "GET")] + [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 = "GET")] + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string UserId { get; set; } - [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + [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 = "GET")] + [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 = "GET")] + [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; } } [Authenticated] @@ -82,7 +86,7 @@ namespace MediaBrowser.Api.Playback public async Task Post(GetPostedPlaybackInfo request) { - var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSource).ConfigureAwait(false); + var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false); var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); var profile = request.DeviceProfile; @@ -97,36 +101,36 @@ namespace MediaBrowser.Api.Playback if (profile != null) { - var mediaSourceId = request.MediaSource == null ? null : request.MediaSource.Id; + var mediaSourceId = request.MediaSourceId; SetDeviceSpecificData(request.Id, info, profile, authInfo, null, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); } return ToOptimizedResult(info); } - private async Task GetPlaybackInfo(string id, string userId, MediaSourceInfo mediaSource = null) + private async Task GetPlaybackInfo(string id, string userId, string mediaSourceId = null) { var result = new PlaybackInfoResponse(); - if (mediaSource == null) + IEnumerable mediaSources; + + try { - IEnumerable mediaSources; + mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false); + } + catch (PlaybackException ex) + { + mediaSources = new List(); + result.ErrorCode = ex.ErrorCode; + } - 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(); - } - else + if (!string.IsNullOrWhiteSpace(mediaSourceId)) { - result.MediaSources = new List { mediaSource }; + result.MediaSources = result.MediaSources + .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) + .ToList(); } if (result.MediaSources.Count == 0) @@ -185,9 +189,9 @@ namespace MediaBrowser.Api.Playback mediaSource.SupportsDirectStream = true; // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = item is Video ? - streamBuilder.BuildVideoItem(options) : - streamBuilder.BuildAudioItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); if (streamInfo == null || !streamInfo.IsDirectStream) { @@ -201,9 +205,9 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = item is Video ? - streamBuilder.BuildVideoItem(options) : - streamBuilder.BuildAudioItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); if (streamInfo == null || !streamInfo.IsDirectStream) { @@ -214,9 +218,9 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsTranscoding) { // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = item is Video ? - streamBuilder.BuildVideoItem(options) : - streamBuilder.BuildAudioItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode) { @@ -227,6 +231,46 @@ namespace MediaBrowser.Api.Playback } } } + + SortMediaSources(result); + } + + private void SortMediaSources(PlaybackInfoResponse result) + { + var originalList = result.MediaSources.ToList(); + + result.MediaSources = result.MediaSources.OrderBy(i => + { + // Nothing beats direct playing a file + if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File) + { + return 0; + } + + return 1; + + }).ThenBy(i => + { + // Let's assume direct streaming a file is just as desirable as direct playing a remote url + if (i.SupportsDirectPlay || i.SupportsDirectStream) + { + return 0; + } + + return 1; + + }).ThenBy(i => + { + switch (i.Protocol) + { + case MediaProtocol.File: + return 0; + default: + return 1; + } + + }).ThenBy(originalList.IndexOf) + .ToList(); } } } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index af2cf6b038..fee5011596 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient) + public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient) { } @@ -55,7 +55,7 @@ namespace MediaBrowser.Api.Playback.Progressive return ProcessRequest(request, true); } - protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) + protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { var audioTranscodeParams = new List(); @@ -84,7 +84,7 @@ namespace MediaBrowser.Api.Playback.Progressive return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"", inputModifier, - GetInputArgument(transcodingJobId, state), + GetInputArgument(state), threads, vn, string.Join(" ", audioTranscodeParams.ToArray()), diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 7a2990e2af..8ed17ca17e 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Api.Playback.Progressive protected readonly IImageProcessor ImageProcessor; protected readonly IHttpClient HttpClient; - protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) + protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) { ImageProcessor = imageProcessor; HttpClient = httpClient; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index eb18288e98..540c39a0c7 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient) { } @@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Playback.Progressive return ProcessRequest(request, true); } - protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) + protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { // Get the output codec name var videoCodec = state.OutputVideoCodec; @@ -106,7 +106,7 @@ namespace MediaBrowser.Api.Playback.Progressive return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"", inputModifier, - GetInputArgument(transcodingJobId, state), + GetInputArgument(state), keyFrame, GetMapArgs(state), GetVideoArguments(state, videoCodec), diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 1d4dd1aafb..37f2c7702c 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -1,6 +1,7 @@ -using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -17,7 +18,7 @@ namespace MediaBrowser.Api.Playback public class StreamState : IDisposable { private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; + private readonly IMediaSourceManager _mediaSourceManager; public string RequestedUrl { get; set; } @@ -39,7 +40,7 @@ namespace MediaBrowser.Api.Playback public string InputContainer { get; set; } - public List AllMediaStreams { get; set; } + public MediaSourceInfo MediaSource { get; set; } public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } @@ -64,8 +65,6 @@ namespace MediaBrowser.Api.Playback public List PlayableStreamFileNames { get; set; } - public string LiveTvStreamId { get; set; } - public int SegmentLength = 3; public bool EnableGenericHlsSegmenter = false; public int HlsListSize @@ -86,14 +85,13 @@ namespace MediaBrowser.Api.Playback public List SupportedAudioCodecs { get; set; } - public StreamState(ILiveTvManager liveTvManager, ILogger logger) + public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger) { - _liveTvManager = liveTvManager; + _mediaSourceManager = mediaSourceManager; _logger = logger; SupportedAudioCodecs = new List(); PlayableStreamFileNames = new List(); RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - AllMediaStreams = new List(); } public string InputAudioSync { get; set; } @@ -113,9 +111,6 @@ namespace MediaBrowser.Api.Playback public long? EncodingDurationTicks { get; set; } - public string ItemType { get; set; } - public string ItemId { get; set; } - public string GetMimeType(string outputPath) { if (!string.IsNullOrEmpty(MimeType)) @@ -187,15 +182,15 @@ namespace MediaBrowser.Api.Playback private async void DisposeLiveStream() { - if (!string.IsNullOrEmpty(LiveTvStreamId)) + if (MediaSource.RequiresClosing) { try { - await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseMediaSource(MediaSource.CloseKey, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { - _logger.ErrorException("Error closing live tv stream", ex); + _logger.ErrorException("Error closing media source", ex); } } } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 07eb74e81b..73589d6777 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -192,7 +192,7 @@ namespace MediaBrowser.Api.Subtitles { var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); - var mediaSource = item.GetMediaSources(false) + var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null) .First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id)); var subtitleStream = mediaSource.MediaStreams diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 91b2407bee..8d90246765 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -75,17 +75,23 @@ namespace MediaBrowser.Controller.Channels public override IEnumerable GetMediaSources(bool enablePathSubstitution) { - var list = base.GetMediaSources(enablePathSubstitution).ToList(); - - var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None) - .Result.ToList(); + var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None) + .Result.ToList(); if (sources.Count > 0) { return sources; } - list.InsertRange(0, sources); + var list = base.GetMediaSources(enablePathSubstitution).ToList(); + + foreach (var mediaSource in list) + { + if (string.IsNullOrWhiteSpace(mediaSource.Path)) + { + mediaSource.Type = MediaSourceType.Placeholder; + } + } return list; } diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index d7d4483cd1..8eec2021b5 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -90,17 +90,23 @@ namespace MediaBrowser.Controller.Channels public override IEnumerable GetMediaSources(bool enablePathSubstitution) { - var list = base.GetMediaSources(enablePathSubstitution).ToList(); - - var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None) - .Result.ToList(); + var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None) + .Result.ToList(); if (sources.Count > 0) { return sources; } - list.InsertRange(0, sources); + var list = base.GetMediaSources(enablePathSubstitution).ToList(); + + foreach (var mediaSource in list) + { + if (string.IsNullOrWhiteSpace(mediaSource.Path)) + { + mediaSource.Type = MediaSourceType.Placeholder; + } + } return list; } diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 05015da37d..f5c4ab3733 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -112,11 +112,11 @@ namespace MediaBrowser.Controller.Channels /// /// Gets the channel item media sources. /// - /// The identifier. - /// if set to true [include dynamic sources]. + /// The item. + /// if set to true [include cached versions]. /// The cancellation token. /// Task{IEnumerable{MediaSourceInfo}}. - Task> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken); + Task> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken); /// /// Gets the channel folder. diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index c21fed6fc5..fda17aa279 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -64,6 +64,14 @@ namespace MediaBrowser.Controller.Library /// 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); + /// /// Gets the static media source. /// @@ -72,5 +80,21 @@ namespace MediaBrowser.Controller.Library /// if set to true [enable path substitution]. /// MediaSourceInfo. MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); + + /// + /// Opens the media source. + /// + /// The open key. + /// The cancellation token. + /// Task<MediaSourceInfo>. + Task OpenMediaSource(string openKey, CancellationToken cancellationToken); + + /// + /// Closes the media source. + /// + /// The close key. + /// The cancellation token. + /// Task. + Task CloseMediaSource(string closeKey, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs index 461285d6cc..c5f5b54010 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs @@ -15,5 +15,21 @@ namespace MediaBrowser.Controller.Library /// The cancellation token. /// Task<IEnumerable<MediaSourceInfo>>. Task> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken); + + /// + /// Opens the media source. + /// + /// The open key. + /// The cancellation token. + /// Task<MediaSourceInfo>. + Task OpenMediaSource(string openKey, CancellationToken cancellationToken); + + /// + /// Closes the media source. + /// + /// The close key. + /// The cancellation token. + /// Task. + Task CloseMediaSource(string closeKey, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvItem.cs b/MediaBrowser.Controller/LiveTv/ILiveTvItem.cs index d3334e8ea0..6c277a2e19 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvItem.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvItem.cs @@ -1,8 +1,10 @@ - +using System; + namespace MediaBrowser.Controller.LiveTv { public interface ILiveTvItem { + Guid Id { get; } string ServiceName { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 9815066efc..0dc296d5a5 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -1,10 +1,12 @@ -using System.Runtime.Serialization; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Users; +using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; namespace MediaBrowser.Controller.LiveTv { @@ -99,5 +101,20 @@ namespace MediaBrowser.Controller.LiveTv { return user.Policy.EnableLiveTvManagement; } + + public override IEnumerable GetMediaSources(bool enablePathSubstitution) + { + var list = base.GetMediaSources(enablePathSubstitution).ToList(); + + foreach (var mediaSource in list) + { + if (string.IsNullOrWhiteSpace(mediaSource.Path)) + { + mediaSource.Type = MediaSourceType.Placeholder; + } + } + + return list; + } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 75e418bcc9..1e13d8f3f4 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv Name = Name, Path = Path, RunTimeTicks = RunTimeTicks, - Type = MediaSourceType.Default + Type = MediaSourceType.Placeholder }; list.Add(info); diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 207684d55d..3669f94403 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -1,9 +1,11 @@ -using System.Runtime.Serialization; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using System.Linq; using MediaBrowser.Model.Users; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; namespace MediaBrowser.Controller.LiveTv { @@ -97,5 +99,20 @@ namespace MediaBrowser.Controller.LiveTv { return user.Policy.EnableLiveTvManagement; } + + public override IEnumerable GetMediaSources(bool enablePathSubstitution) + { + var list = base.GetMediaSources(enablePathSubstitution).ToList(); + + foreach (var mediaSource in list) + { + if (string.IsNullOrWhiteSpace(mediaSource.Path)) + { + mediaSource.Type = MediaSourceType.Placeholder; + } + } + + return list; + } } } diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index f8f939f479..17385bda62 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -476,7 +476,7 @@ namespace MediaBrowser.Dlna.PlayTo var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex); playlistItem.StreamInfo.StartPositionTicks = startPostionTicks; - playlistItem.StreamUrl = playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken); + playlistItem.StreamUrl = playlistItem.StreamInfo.ToDlnaUrl(_serverAddress, _accessToken); var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager) .GetItemDidl(item, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo); diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 62ac321fe3..6534eda10c 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -239,6 +239,16 @@ namespace MediaBrowser.Model.Dlna return playlistItem; } + private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options) + { + if (item.Protocol == MediaProtocol.File) + { + return options.Profile.MaxStaticBitrate; + } + + return options.GetMaxBitrate(); + } + private List GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) { DirectPlayProfile directPlayProfile = null; @@ -263,7 +273,7 @@ namespace MediaBrowser.Model.Dlna // The profile describes what the device supports // If device requirements are satisfied then allow both direct stream and direct play - if (IsAudioEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate)) + if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options))) { playMethods.Add(PlayMethod.DirectPlay); } @@ -293,7 +303,7 @@ namespace MediaBrowser.Model.Dlna MediaStream videoStream = item.VideoStream; // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough - bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate, subtitleStream, options); + bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options); bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options); if (isEligibleForDirectPlay || isEligibleForDirectStream) @@ -604,6 +614,11 @@ namespace MediaBrowser.Model.Dlna // Look for an external profile that matches the stream type (text/graphical) foreach (SubtitleProfile profile in subtitleProfiles) { + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { if (subtitleStream.SupportsExternalStream) @@ -621,6 +636,11 @@ namespace MediaBrowser.Model.Dlna foreach (SubtitleProfile profile in subtitleProfiles) { + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { return profile; diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs index d3989829ca..1795c374a4 100644 --- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs +++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs @@ -1,4 +1,6 @@ -using System.Xml.Serialization; +using MediaBrowser.Model.Extensions; +using System.Collections.Generic; +using System.Xml.Serialization; namespace MediaBrowser.Model.Dlna { @@ -13,5 +15,28 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("didlMode")] public string DidlMode { get; set; } + [XmlAttribute("language")] + public string Language { get; set; } + + public List GetLanguages() + { + List list = new List(); + foreach (string i in (Language ?? string.Empty).Split(',')) + { + if (!string.IsNullOrEmpty(i)) list.Add(i); + } + return list; + } + + public bool SupportsLanguage(string language) + { + if (string.IsNullOrEmpty(language)) + { + language = "und"; + } + + List languages = GetLanguages(); + return languages.Count == 0 || ListHelper.ContainsIgnoreCase(languages, language); + } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 31d310acda..92af8d671c 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Dto public bool SupportsDirectStream { get; set; } public bool SupportsDirectPlay { get; set; } + public bool RequiresOpening { get; set; } + public string OpenKey { get; set; } + public bool RequiresClosing { get; set; } + public string CloseKey { get; set; } + public VideoType? VideoType { get; set; } public IsoType? IsoType { get; set; } diff --git a/MediaBrowser.Model/Dto/MediaSourceType.cs b/MediaBrowser.Model/Dto/MediaSourceType.cs index a9cd71df56..e049785025 100644 --- a/MediaBrowser.Model/Dto/MediaSourceType.cs +++ b/MediaBrowser.Model/Dto/MediaSourceType.cs @@ -4,6 +4,6 @@ namespace MediaBrowser.Model.Dto { Default = 0, Grouping = 1, - Cache = 2 + Placeholder = 2 } } \ No newline at end of file diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 66fb486284..fa075490a5 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -141,6 +141,11 @@ namespace MediaBrowser.Model.Entities { if (Type != MediaStreamType.Subtitle) return false; + if (string.IsNullOrEmpty(Codec) && !IsExternal) + { + return false; + } + return IsTextFormat(Codec); } } diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs index 783fb41203..ffd4995ad9 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -1,11 +1,9 @@ using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Dto; namespace MediaBrowser.Model.MediaInfo { public class PlaybackInfoRequest { public DeviceProfile DeviceProfile { get; set; } - public MediaSourceInfo MediaSource { get; set; } } } diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 65d8e287f9..99be102f85 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -96,7 +96,7 @@ namespace MediaBrowser.Providers.MediaInfo var album = item.Parent as MusicAlbum; var filename = item.Album ?? string.Empty; - filename += item.Artists.FirstOrDefault() ?? string.Empty; + filename += string.Join(",", item.Artists.ToArray()); filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary"; filename = filename.GetMD5() + ".jpg"; diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs index e0b616605d..980c3f31b7 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(), cancellationToken) + await _manager.DownloadChannelItem(item, destination, new Progress(), 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 0000000000..6a7163bb3b --- /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> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken) + { + var channelItem = item as IChannelMediaItem; + + if (channelItem != null) + { + return _channelManager.GetDynamicMediaSources(channelItem, cancellationToken); + } + + return Task.FromResult>(new List()); + } + + public Task 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 f0f30229ec..e22bf2e7f7 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> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken) + public async Task> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken) { - var item = (IChannelMediaItem)_libraryManager.GetItemById(id); + IEnumerable 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> GetDynamicMediaSources(IChannelMediaItem item, CancellationToken cancellationToken) + { var channel = GetChannel(item.ChannelId); var channelPlugin = GetChannelProvider(channel); @@ -252,24 +267,25 @@ namespace MediaBrowser.Server.Implementations.Channels IEnumerable 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(); } - 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>> _channelItemMediaInfo = @@ -297,14 +313,7 @@ namespace MediaBrowser.Server.Implementations.Channels return list; } - public IEnumerable GetCachedChannelItemMediaSources(string id) - { - var item = (IChannelMediaItem)_libraryManager.GetItemById(id); - - return GetCachedChannelItemMediaSources(item); - } - - public IEnumerable GetCachedChannelItemMediaSources(IChannelMediaItem item) + private IEnumerable 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 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 39219b5415..40cf240d7e 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(); + } + } + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 59daa4921a..202a051e3b 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 0000000000..186bc499db --- /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> 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>(new List()); + } + + private async Task> 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(); + openKeys.Add(item.GetType().Name); + openKeys.Add(item.Id.ToString("N")); + source.OpenKey = string.Join("|", openKeys.ToArray()); + } + + return sources; + } + + public async Task 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 3eb414068f..db2397d2f7 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -111,6 +111,7 @@ + @@ -225,6 +226,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 03a7e92a45..dd8ce82ef1 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 4172cfc2da..25a52fb950 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 list, + private void AddMediaSource(List 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(); + 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 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(); } } } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 9a7f03341c..039c5edf38 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, ChannelManager, LogManager.GetLogger("MediaSourceManager")); + MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager")); RegisterSingleInstance(MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager); diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 31441ff791..e1659bfb28 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -9,8 +9,8 @@ https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false - Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. - Copyright © Media Browser 2013 + 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 568a47dfe0..294bc519eb 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -4,13 +4,13 @@ MediaBrowser.Common 3.0.603 MediaBrowser.Common - Media Browser Team + Emby Team ebr,Luke,scottisafool https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false - Contains common model objects and interfaces used by all Media Browser solutions. - Copyright © Media Browser 2013 + Contains common model objects and interfaces used by all Emby solutions. + Copyright © Emby 2013 diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index fb00fd8407..bcbd1d5bea 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -4,13 +4,13 @@ MediaBrowser.Model.Signed 3.0.603 MediaBrowser.Model - Signed Edition - Media Browser Team + Emby Team ebr,Luke,scottisafool https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false - Contains common model objects and interfaces used by all Media Browser solutions. - Copyright © Media Browser 2013 + Contains common model objects and interfaces used by all Emby solutions. + Copyright © Emby 2013 diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index c36fb6d6cd..ee3925db5b 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -4,13 +4,13 @@ MediaBrowser.Server.Core 3.0.603 Media Browser.Server.Core - Media Browser Team + Emby Team ebr,Luke,scottisafool https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false - Contains core components required to build plugins for Media Browser Server. - Copyright © Media Browser 2013 + Contains core components required to build plugins for Emby Server. + Copyright © Emby 2013 -- cgit v1.2.3 From dd8dd1938a990d9c4c9bac384665dc9d82c4bc35 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 14:16:40 -0400 Subject: update live stream generation --- MediaBrowser.Api/ApiEntryPoint.cs | 2 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 ++++ MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 9 ++++++++ MediaBrowser.Model/ApiClient/IApiClient.cs | 7 +++++++ MediaBrowser.Model/Dlna/StreamInfo.cs | 3 +++ MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs | 19 +++++++++++++++++ MediaBrowser.Model/Session/PlaybackProgressInfo.cs | 5 +++++ MediaBrowser.Model/Session/PlaybackStopInfo.cs | 5 +++++ .../LiveTv/LiveTvManager.cs | 12 +++++++++-- .../Session/SessionManager.cs | 24 ++++++++++++++++++++++ Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 14 files changed, 93 insertions(+), 9 deletions(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index a6958b95d3..9f4ad58937 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -187,7 +187,7 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(deviceId)) { - var audioCodec = state.ActualOutputVideoCodec; + var audioCodec = state.ActualOutputAudioCodec; var videoCodec = state.ActualOutputVideoCodec; _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5a4cdaaa93..34c5e5f7c8 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1513,6 +1513,10 @@ namespace MediaBrowser.Api.Playback { request.StreamId = val; } + else if (i == 22) + { + request.LiveStreamId = val; + } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 1e13d8f3f4..cb10003ed8 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -82,6 +82,15 @@ namespace MediaBrowser.Controller.LiveTv /// null if [has image] contains no value, true if [has image]; otherwise, false. public bool? HasProviderImage { get; set; } + public override LocationType LocationType + { + get + { + // TODO: This should be removed + return LocationType.Remote; + } + } + protected override string CreateSortName() { double number = 0; diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 19d6acf339..58af016150 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -1486,5 +1486,12 @@ namespace MediaBrowser.Model.ApiClient /// The query. /// Task<List<RecommendationDto>>. Task> GetMovieRecommendations(MovieRecommendationQuery query); + /// + /// Opens the live stream. + /// + /// The request. + /// The cancellation token. + /// Task<LiveStreamResponse>. + Task OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 60615d1da9..dc24627960 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -210,6 +210,9 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("StreamId", streamId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); + string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; + list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); + return list; } diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs index 8078219d82..563006a4df 100644 --- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -12,5 +12,24 @@ namespace MediaBrowser.Model.MediaInfo public int? SubtitleStreamIndex { get; set; } public string ItemId { get; set; } public DeviceProfile DeviceProfile { get; set; } + + public LiveStreamRequest() + { + + } + + public LiveStreamRequest(AudioOptions options) + { + MaxStreamingBitrate = options.MaxBitrate; + ItemId = options.ItemId; + DeviceProfile = options.Profile; + + VideoOptions videoOptions = options as VideoOptions; + if (videoOptions != null) + { + AudioStreamIndex = videoOptions.AudioStreamIndex; + SubtitleStreamIndex = videoOptions.SubtitleStreamIndex; + } + } } } diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs index f04dea1eac..a029df4a93 100644 --- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs +++ b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs @@ -78,5 +78,10 @@ namespace MediaBrowser.Model.Session /// /// The play method. public PlayMethod PlayMethod { get; set; } + /// + /// Gets or sets the live stream identifier. + /// + /// The live stream identifier. + public string LiveStreamId { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlaybackStopInfo.cs b/MediaBrowser.Model/Session/PlaybackStopInfo.cs index 38025f1832..a3bdc9a960 100644 --- a/MediaBrowser.Model/Session/PlaybackStopInfo.cs +++ b/MediaBrowser.Model/Session/PlaybackStopInfo.cs @@ -36,5 +36,10 @@ namespace MediaBrowser.Model.Session /// /// The position ticks. public long? PositionTicks { get; set; } + /// + /// Gets or sets the live stream identifier. + /// + /// The live stream identifier. + public string LiveStreamId { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 86b31e0e5d..b30db60927 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -356,7 +356,11 @@ 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.LiveStreamId = info.Id; + + if (info.RequiresClosing) + { + info.LiveStreamId = info.Id; + } } else { @@ -367,7 +371,11 @@ 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.LiveStreamId = info.Id; + + if (info.RequiresClosing) + { + info.LiveStreamId = info.Id; + } } _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 16fc420639..cc5aef54aa 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -679,6 +679,18 @@ namespace MediaBrowser.Server.Implementations.Session } } + if (!string.IsNullOrWhiteSpace(info.LiveStreamId)) + { + try + { + await _mediaSourceManager.PingLiveStream(info.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error closing live stream", ex); + } + } + EventHelper.FireEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs { Item = libraryItem, @@ -769,6 +781,18 @@ namespace MediaBrowser.Server.Implementations.Session } } + if (!string.IsNullOrWhiteSpace(info.LiveStreamId)) + { + try + { + await _mediaSourceManager.CloseLiveStream(info.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error closing live stream", ex); + } + } + EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs { Item = libraryItem, diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index e1659bfb28..ee3bdd2b6b 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.603 + 3.0.606 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 294bc519eb..a8f3ea1f38 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.603 + 3.0.606 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index bcbd1d5bea..7bd4a090dd 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.603 + 3.0.606 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index ee3925db5b..3f39ace9dd 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.603 + 3.0.606 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 10b9a865b7baed2fb83ec7a71e0524ffe80ea609 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 14:31:28 -0400 Subject: update live stream handling --- MediaBrowser.Api/ApiEntryPoint.cs | 18 +++++++++--------- MediaBrowser.Api/Playback/BaseStreamingService.cs | 6 +++--- MediaBrowser.Api/Playback/Dash/MpegDashService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 2 +- MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs | 6 +++--- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- MediaBrowser.Api/Playback/StreamRequest.cs | 2 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 4 ++-- MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs | 6 +++--- Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 13 files changed, 30 insertions(+), 30 deletions(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 9f4ad58937..444977e352 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -132,7 +132,7 @@ namespace MediaBrowser.Api /// Called when [transcode beginning]. /// /// The path. - /// The stream identifier. + /// The play session identifier. /// The transcoding job identifier. /// The type. /// The process. @@ -141,7 +141,7 @@ namespace MediaBrowser.Api /// The cancellation token source. /// TranscodingJob. public TranscodingJob OnTranscodeBeginning(string path, - string streamId, + string playSessionId, string transcodingJobId, TranscodingJobType type, Process process, @@ -160,7 +160,7 @@ namespace MediaBrowser.Api DeviceId = deviceId, CancellationTokenSource = cancellationTokenSource, Id = transcodingJobId, - StreamId = streamId + PlaySessionId = playSessionId }; _activeTranscodingJobs.Add(job); @@ -324,10 +324,10 @@ namespace MediaBrowser.Api /// Kills the single transcoding job. /// /// The device id. - /// The stream identifier. + /// The play session identifier. /// The delete files. /// Task. - internal void KillTranscodingJobs(string deviceId, string streamId, Func deleteFiles) + internal void KillTranscodingJobs(string deviceId, string playSessionId, Func deleteFiles) { if (string.IsNullOrEmpty(deviceId)) { @@ -338,7 +338,7 @@ namespace MediaBrowser.Api { if (string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)) { - return string.IsNullOrWhiteSpace(streamId) || string.Equals(streamId, j.StreamId, StringComparison.OrdinalIgnoreCase); + return string.IsNullOrWhiteSpace(playSessionId) || string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase); } return false; @@ -539,10 +539,10 @@ namespace MediaBrowser.Api public class TranscodingJob { /// - /// Gets or sets the stream identifier. + /// Gets or sets the play session identifier. /// - /// The stream identifier. - public string StreamId { get; set; } + /// The play session identifier. + public string PlaySessionId { get; set; } /// /// Gets or sets the path. /// diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 34c5e5f7c8..c4cdfc9edd 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -126,7 +126,7 @@ namespace MediaBrowser.Api.Playback var data = GetCommandLineArguments("dummy\\dummy", state, false); data += "-" + (state.Request.DeviceId ?? string.Empty); - data += "-" + (state.Request.StreamId ?? string.Empty); + data += "-" + (state.Request.PlaySessionId ?? string.Empty); data += "-" + (state.Request.ClientTime ?? string.Empty); var dataHash = data.GetMD5().ToString("N"); @@ -1009,7 +1009,7 @@ namespace MediaBrowser.Api.Playback } var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, - state.Request.StreamId, + state.Request.PlaySessionId, transcodingId, TranscodingJobType, process, @@ -1511,7 +1511,7 @@ namespace MediaBrowser.Api.Playback } else if (i == 21) { - request.StreamId = val; + request.PlaySessionId = val; } else if (i == 22) { diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs index 692e8d4e74..ba3f172579 100644 --- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs @@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Dash // If the playlist doesn't already exist, startup ffmpeg try { - ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, p => false); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false); if (currentTranscodingIndex.HasValue) { diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 4f7c0444b2..4f043d3217 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -135,7 +135,7 @@ namespace MediaBrowser.Api.Playback.Hls // If the playlist doesn't already exist, startup ffmpeg try { - ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, p => false); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false); if (currentTranscodingIndex.HasValue) { diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 1fe0661d9f..5d8c67abe9 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -54,8 +54,8 @@ namespace MediaBrowser.Api.Playback.Hls [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] public string DeviceId { get; set; } - [ApiMember(Name = "StreamId", Description = "The stream id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string StreamId { get; set; } + [ApiMember(Name = "PlaySessionId", Description = "The play session id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] + public string PlaySessionId { get; set; } } /// @@ -95,7 +95,7 @@ namespace MediaBrowser.Api.Playback.Hls public void Delete(StopEncodingProcess request) { - ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, path => true); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, path => true); } /// diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 6f8c2cc505..c6678d1ed1 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -194,7 +194,7 @@ namespace MediaBrowser.Api.Playback } else { - result.StreamId = Guid.NewGuid().ToString("N"); + result.PlaySessionId = Guid.NewGuid().ToString("N"); } return result; diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 7ed4fcd965..75242e604c 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -71,7 +71,7 @@ namespace MediaBrowser.Api.Playback public string Params { get; set; } public string ClientTime { get; set; } - public string StreamId { get; set; } + public string PlaySessionId { get; set; } public string LiveStreamId { get; set; } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index dc24627960..12319a1227 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -206,8 +206,8 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("Profile", item.VideoProfile ?? string.Empty)); list.Add(new NameValuePair("Cabac", item.Cabac.HasValue ? item.Cabac.Value.ToString() : string.Empty)); - string streamId = item.PlaybackInfo == null ? null : item.PlaybackInfo.StreamId; - list.Add(new NameValuePair("StreamId", streamId ?? string.Empty)); + string playSessionId = item.PlaybackInfo == null ? null : item.PlaybackInfo.PlaySessionId; + list.Add(new NameValuePair("PlaySessionId", playSessionId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs index ed0824e8a4..1f8936d018 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs @@ -13,10 +13,10 @@ namespace MediaBrowser.Model.MediaInfo public List MediaSources { get; set; } /// - /// Gets or sets the stream identifier. + /// Gets or sets the play session identifier. /// - /// The stream identifier. - public string StreamId { get; set; } + /// The play session identifier. + public string PlaySessionId { get; set; } /// /// Gets or sets the error code. diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index ee3bdd2b6b..57a1db722d 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.606 + 3.0.607 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 a8f3ea1f38..9ad9c18ca2 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.606 + 3.0.607 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 7bd4a090dd..261ddd37d1 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.606 + 3.0.607 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 3f39ace9dd..49a38aaeb3 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.606 + 3.0.607 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 66ea0b256686b15f01a062cc5deaaf445905f4fb Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 14:45:48 -0400 Subject: updated nuget --- Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 57a1db722d..dd28aae3ac 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.607 + 3.0.608 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 9ad9c18ca2..0124a609de 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.607 + 3.0.608 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 261ddd37d1..3297b66066 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.607 + 3.0.608 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 49a38aaeb3..0a749c98b6 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.607 + 3.0.608 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 5f044cfd68eecf116df1a646806fc091f3fb63aa Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 30 Mar 2015 12:16:34 -0400 Subject: add setting to control transcodng throttle --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 11 +- MediaBrowser.Api/Playback/StreamState.cs | 36 ++++ MediaBrowser.Api/Playback/TranscodingThrottler.cs | 26 ++- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 8 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 4 +- MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs | 81 ++------- MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs | 54 +++++- .../Encoder/EncodingJobFactory.cs | 181 ++++++++------------- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 - MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs | 2 +- .../Configuration/EncodingOptions.cs | 4 + MediaBrowser.Model/Dlna/ConditionProcessor.cs | 13 +- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 8 +- MediaBrowser.Model/Dlna/DeviceProfile.cs | 6 +- MediaBrowser.Model/Dlna/ProfileConditionValue.cs | 5 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 13 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 36 ++++ MediaBrowser.Model/Dto/MediaSourceInfo.cs | 34 +++- .../Localization/JavaScript/javascript.json | 3 +- .../Localization/Server/server.json | 4 +- .../Sync/CloudSyncProfile.cs | 145 ++++++++++++++++- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 2 +- .../MediaBrowser.WebDashboard.csproj | 4 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 28 files changed, 466 insertions(+), 232 deletions(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index c4cdfc9edd..8c4bcf0a31 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1080,7 +1080,7 @@ namespace MediaBrowser.Api.Playback { if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo) { - transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger); + transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager); state.TranscodingThrottler.Start(); } } @@ -2012,7 +2012,6 @@ namespace MediaBrowser.Api.Playback } var audioCodec = state.ActualOutputAudioCodec; - var videoCodec = state.ActualOutputVideoCodec; var mediaProfile = state.VideoRequest == null ? @@ -2033,7 +2032,9 @@ namespace MediaBrowser.Api.Playback state.TargetTimestamp, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames); + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount); if (mediaProfile != null) { @@ -2118,7 +2119,9 @@ namespace MediaBrowser.Api.Playback state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount ).FirstOrDefault() ?? string.Empty; } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index b097f3b6af..2d1e896db0 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -346,6 +346,42 @@ namespace MediaBrowser.Api.Playback } } + public int? TargetVideoStreamCount + { + get + { + if (Request.Static) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (Request.Static) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + /// /// Predicts the audio sample rate that will be in the output stream /// diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index 19a9fe9343..58cfa086e3 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Logging; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Logging; using System; using System.IO; using System.Threading; @@ -11,13 +13,18 @@ namespace MediaBrowser.Api.Playback private readonly ILogger _logger; private Timer _timer; private bool _isPaused; + private readonly IConfigurationManager _config; - private readonly long _gapLengthInTicks = TimeSpan.FromMinutes(2).Ticks; - - public TranscodingThrottler(TranscodingJob job, ILogger logger) + public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config) { _job = job; _logger = logger; + _config = config; + } + + private EncodingOptions GetOptions() + { + return _config.GetConfiguration("encoding"); } public void Start() @@ -33,7 +40,9 @@ namespace MediaBrowser.Api.Playback return; } - if (IsThrottleAllowed(_job)) + var options = GetOptions(); + + if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) { PauseTranscoding(); } @@ -79,19 +88,20 @@ namespace MediaBrowser.Api.Playback } } - private bool IsThrottleAllowed(TranscodingJob job) + private bool IsThrottleAllowed(TranscodingJob job, int thresholdSeconds) { var bytesDownloaded = job.BytesDownloaded ?? 0; var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0; var downloadPositionTicks = job.DownloadPositionTicks ?? 0; var path = job.Path; + var gapLengthInTicks = TimeSpan.FromSeconds(thresholdSeconds).Ticks; if (downloadPositionTicks > 0 && transcodingPositionTicks > 0) { // HLS - time-based consideration - var targetGap = _gapLengthInTicks; + var targetGap = gapLengthInTicks; var gap = transcodingPositionTicks - downloadPositionTicks; if (gap < targetGap) @@ -113,7 +123,7 @@ namespace MediaBrowser.Api.Playback var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length; // Estimate the bytes the transcoder should be ahead - double gapFactor = _gapLengthInTicks; + double gapFactor = gapLengthInTicks; gapFactor /= transcodingPositionTicks; var targetGap = bytesTranscoded * gapFactor; diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 469b60a9c2..7f696f300b 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -158,7 +158,9 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TranscodeSeekInfo, streamInfo.IsTargetAnamorphic, streamInfo.IsTargetCabac, - streamInfo.TargetRefFrames); + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount); foreach (var contentFeature in contentFeatureList) { @@ -280,7 +282,9 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TargetTimestamp, streamInfo.IsTargetAnamorphic, streamInfo.IsTargetCabac, - streamInfo.TargetRefFrames); + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount); var filename = url.Substring(0, url.IndexOf('?')); diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 17385bda62..3eb091a194 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -526,7 +526,9 @@ namespace MediaBrowser.Dlna.PlayTo streamInfo.TranscodeSeekInfo, streamInfo.IsTargetAnamorphic, streamInfo.IsTargetCabac, - streamInfo.TargetRefFrames); + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount); return list.FirstOrDefault(); } diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index a6a87a3fcb..e9204ef5b0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class AudioEncoder : BaseEncoder { - public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager) + public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) { } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 44e0d15173..8266f4d3ad 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -14,6 +13,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,10 +31,8 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly ILogger Logger; protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IFileSystem FileSystem; - protected readonly ILiveTvManager LiveTvManager; protected readonly IIsoManager IsoManager; protected readonly ILibraryManager LibraryManager; - protected readonly IChannelManager ChannelManager; protected readonly ISessionManager SessionManager; protected readonly ISubtitleEncoder SubtitleEncoder; protected readonly IMediaSourceManager MediaSourceManager; @@ -45,20 +43,18 @@ namespace MediaBrowser.MediaEncoding.Encoder ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, - ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, - IChannelManager channelManager, - ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) + ISessionManager sessionManager, + ISubtitleEncoder subtitleEncoder, + IMediaSourceManager mediaSourceManager) { MediaEncoder = mediaEncoder; Logger = logger; ConfigurationManager = configurationManager; FileSystem = fileSystem; - LiveTvManager = liveTvManager; IsoManager = isoManager; LibraryManager = libraryManager; - ChannelManager = channelManager; SessionManager = sessionManager; SubtitleEncoder = subtitleEncoder; MediaSourceManager = mediaSourceManager; @@ -68,7 +64,7 @@ namespace MediaBrowser.MediaEncoding.Encoder IProgress progress, CancellationToken cancellationToken) { - var encodingJob = await new EncodingJobFactory(Logger, LiveTvManager, LibraryManager, ChannelManager, MediaSourceManager) + var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager) .CreateJob(options, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false); encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); @@ -477,53 +473,25 @@ namespace MediaBrowser.MediaEncoding.Encoder state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false); } - if (string.IsNullOrEmpty(state.MediaPath)) + if (state.MediaSource.RequiresOpening) { - var checkCodecs = false; - - if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name)) + var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest { - var streamInfo = await LiveTvManager.GetChannelStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; + OpenToken = state.MediaSource.OpenToken - await Task.Delay(1500, cancellationToken).ConfigureAwait(false); + }, false, cancellationToken).ConfigureAwait(false); - AttachMediaStreamInfo(state, streamInfo, state.Options); - checkCodecs = true; - } + AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options); - else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) || - string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name)) + if (state.IsVideoRequest) { - var streamInfo = await LiveTvManager.GetRecordingStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; - - await Task.Delay(1500, cancellationToken).ConfigureAwait(false); - - AttachMediaStreamInfo(state, streamInfo, state.Options); - checkCodecs = true; + EncodingJobFactory.TryStreamCopy(state, state.Options); } + } - if (state.IsVideoRequest && checkCodecs) - { - if (state.VideoStream != null && EncodingJobFactory.CanStreamCopyVideo(state.Options, state.VideoStream)) - { - state.OutputVideoCodec = "copy"; - } - - if (state.AudioStream != null && EncodingJobFactory.CanStreamCopyAudio(state.Options, state.AudioStream, state.SupportedAudioCodecs)) - { - state.OutputAudioCodec = "copy"; - } - } + if (state.MediaSource.BufferMs.HasValue) + { + await Task.Delay(state.MediaSource.BufferMs.Value, cancellationToken).ConfigureAwait(false); } } @@ -531,22 +499,7 @@ namespace MediaBrowser.MediaEncoding.Encoder MediaSourceInfo mediaSource, EncodingJobOptions videoRequest) { - state.InputProtocol = mediaSource.Protocol; - state.MediaPath = mediaSource.Path; - state.RunTimeTicks = mediaSource.RunTimeTicks; - state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - state.InputBitrate = mediaSource.Bitrate; - state.InputFileSize = mediaSource.Size; - state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - - if (state.ReadInputAtNativeFramerate) - { - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - } - - EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest); + EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest); } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index c8d121eead..767f3f829b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -1,7 +1,8 @@ -using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -26,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public EncodingJobOptions Options { get; set; } public string InputContainer { get; set; } - public List AllMediaStreams { get; set; } + public MediaSourceInfo MediaSource { get; set; } public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } public MediaStream SubtitleStream { get; set; } @@ -76,12 +77,12 @@ namespace MediaBrowser.MediaEncoding.Encoder } private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; + private readonly IMediaSourceManager _mediaSourceManager; - public EncodingJob(ILogger logger, ILiveTvManager liveTvManager) + public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) { _logger = logger; - _liveTvManager = liveTvManager; + _mediaSourceManager = mediaSourceManager; Id = Guid.NewGuid().ToString("N"); RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -89,7 +90,6 @@ namespace MediaBrowser.MediaEncoding.Encoder SupportedAudioCodecs = new List(); PlayableStreamFileNames = new List(); RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - AllMediaStreams = new List(); TaskCompletionSource = new TaskCompletionSource(); } @@ -136,15 +136,15 @@ namespace MediaBrowser.MediaEncoding.Encoder private async void DisposeLiveStream() { - if (!string.IsNullOrEmpty(LiveTvStreamId)) + if (MediaSource.RequiresClosing) { try { - await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { - _logger.ErrorException("Error closing live tv stream", ex); + _logger.ErrorException("Error closing media source", ex); } } } @@ -394,6 +394,42 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + public int? TargetVideoStreamCount + { + get + { + if (Options.Static) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (Options.Static) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded) { var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index c5783e1882..8d82010749 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -1,9 +1,10 @@ -using MediaBrowser.Controller.Channels; +using System.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; @@ -19,19 +20,15 @@ namespace MediaBrowser.MediaEncoding.Encoder public class EncodingJobFactory { private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; private readonly IMediaSourceManager _mediaSourceManager; protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public EncodingJobFactory(ILogger logger, ILiveTvManager liveTvManager, ILibraryManager libraryManager, IChannelManager channelManager, IMediaSourceManager mediaSourceManager) + public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager) { _logger = logger; - _liveTvManager = liveTvManager; _libraryManager = libraryManager; - _channelManager = channelManager; _mediaSourceManager = mediaSourceManager; } @@ -42,9 +39,9 @@ namespace MediaBrowser.MediaEncoding.Encoder if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); - } - - var state = new EncodingJob(_logger, _liveTvManager) + } + + var state = new EncodingJob(_logger, _mediaSourceManager) { Options = options, IsVideoRequest = isVideoRequest, @@ -58,106 +55,17 @@ namespace MediaBrowser.MediaEncoding.Encoder } var item = _libraryManager.GetItemById(request.ItemId); - - List mediaStreams = null; - state.ItemType = item.GetType().Name; - if (item is ILiveTvRecording) - { - var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false); - - state.VideoType = VideoType.VideoFile; - state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - - var path = recording.RecordingInfo.Path; - var mediaUrl = recording.RecordingInfo.Url; - - var source = string.IsNullOrEmpty(request.MediaSourceId) - ? recording.GetMediaSources(false).First() - : _mediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false); - - mediaStreams = source.MediaStreams; - - // Just to prevent this from being null and causing other methods to fail - state.MediaPath = string.Empty; - - if (!string.IsNullOrEmpty(path)) - { - state.MediaPath = path; - state.InputProtocol = MediaProtocol.File; - } - else if (!string.IsNullOrEmpty(mediaUrl)) - { - state.MediaPath = mediaUrl; - state.InputProtocol = MediaProtocol.Http; - } - - state.RunTimeTicks = recording.RunTimeTicks; - state.DeInterlace = true; - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - state.InputContainer = recording.Container; - state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate; - } - else if (item is LiveTvChannel) - { - var channel = _liveTvManager.GetInternalChannel(request.ItemId); - - state.VideoType = VideoType.VideoFile; - state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - mediaStreams = new List(); - - state.DeInterlace = true; - - // Just to prevent this from being null and causing other methods to fail - state.MediaPath = string.Empty; - } - else - { - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); + state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) - ? mediaSources.First() - : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - - mediaStreams = mediaSource.MediaStreams; - - state.MediaPath = mediaSource.Path; - state.InputProtocol = mediaSource.Protocol; - state.InputContainer = mediaSource.Container; - state.InputFileSize = mediaSource.Size; - state.InputBitrate = mediaSource.Bitrate; - state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - state.RunTimeTicks = mediaSource.RunTimeTicks; - state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - - var video = item as Video; - - if (video != null) - { - state.IsInputVideo = true; - - if (mediaSource.VideoType.HasValue) - { - state.VideoType = mediaSource.VideoType.Value; - } + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); - state.IsoType = mediaSource.IsoType; + var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) + ? mediaSources.First() + : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); - - if (mediaSource.Timestamp.HasValue) - { - state.InputTimestamp = mediaSource.Timestamp.Value; - } - } - - state.RunTimeTicks = mediaSource.RunTimeTicks; - } - - AttachMediaStreamInfo(state, mediaStreams, request); + AttachMediaStreamInfo(state, mediaSource, options); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; @@ -185,26 +93,73 @@ namespace MediaBrowser.MediaEncoding.Encoder ApplyDeviceProfileSettings(state); - if (isVideoRequest) + TryStreamCopy(state, request); + + return state; + } + + internal static void TryStreamCopy(EncodingJob state, + EncodingJobOptions videoRequest) + { + if (state.IsVideoRequest) { - if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream)) + if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) { state.OutputVideoCodec = "copy"; } - if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs)) + if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs)) { state.OutputAudioCodec = "copy"; } } - - return state; } internal static void AttachMediaStreamInfo(EncodingJob state, - List mediaStreams, + MediaSourceInfo mediaSource, EncodingJobOptions videoRequest) { + state.MediaPath = mediaSource.Path; + state.InputProtocol = mediaSource.Protocol; + state.InputContainer = mediaSource.Container; + state.InputFileSize = mediaSource.Size; + state.InputBitrate = mediaSource.Bitrate; + state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + state.RunTimeTicks = mediaSource.RunTimeTicks; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + + if (mediaSource.VideoType.HasValue) + { + state.VideoType = mediaSource.VideoType.Value; + } + + state.IsoType = mediaSource.IsoType; + + state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); + + if (mediaSource.Timestamp.HasValue) + { + state.InputTimestamp = mediaSource.Timestamp.Value; + } + + state.InputProtocol = mediaSource.Protocol; + state.MediaPath = mediaSource.Path; + state.RunTimeTicks = mediaSource.RunTimeTicks; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + state.InputBitrate = mediaSource.Bitrate; + state.InputFileSize = mediaSource.Size; + state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + + if (state.ReadInputAtNativeFramerate || + mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) + { + state.OutputAudioSync = "1000"; + state.InputVideoSync = "-1"; + state.InputAudioSync = "1"; + } + + var mediaStreams = mediaSource.MediaStreams; + if (videoRequest != null) { if (string.IsNullOrEmpty(videoRequest.VideoCodec)) @@ -233,7 +188,7 @@ namespace MediaBrowser.MediaEncoding.Encoder state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } - state.AllMediaStreams = mediaStreams; + state.MediaSource = mediaSource; } /// @@ -771,7 +726,9 @@ namespace MediaBrowser.MediaEncoding.Encoder state.TargetTimestamp, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames); + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount); if (mediaProfile != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7fd91bf6f8..4258898073 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -577,10 +577,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger, ConfigurationManager, FileSystem, - LiveTvManager, IsoManager, LibraryManager, - ChannelManager, SessionManager, SubtitleEncoder(), MediaSourceManager()) @@ -599,10 +597,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger, ConfigurationManager, FileSystem, - LiveTvManager, IsoManager, LibraryManager, - ChannelManager, SessionManager, SubtitleEncoder(), MediaSourceManager()) diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index b8ed97b1fc..26d4a76509 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class VideoEncoder : BaseEncoder { - public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager) + public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) { } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f24367298e..afd67eb153 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -8,12 +8,16 @@ namespace MediaBrowser.Model.Configuration public double DownMixAudioBoost { get; set; } public string H264Encoder { get; set; } public bool EnableDebugLogging { get; set; } + public bool EnableThrottling { get; set; } + public int ThrottleThresholdSeconds { get; set; } public EncodingOptions() { H264Encoder = "libx264"; DownMixAudioBoost = 2; EncodingQuality = EncodingQuality.Auto; + EnableThrottling = true; + ThrottleThresholdSeconds = 120; } } } diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index e0a8e239e1..3769634440 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -20,7 +20,9 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp? timestamp, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { switch (condition.Property) { @@ -56,6 +58,10 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, width); case ProfileConditionValue.RefFrames: return IsConditionSatisfied(condition, refFrames); + case ProfileConditionValue.NumAudioStreams: + return IsConditionSatisfied(condition, numAudioStreams); + case ProfileConditionValue.NumVideoStreams: + return IsConditionSatisfied(condition, numVideoStreams); case ProfileConditionValue.VideoTimestamp: return IsConditionSatisfied(condition, timestamp); default: @@ -92,7 +98,8 @@ namespace MediaBrowser.Model.Dlna public bool IsVideoAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, - string audioProfile) + string audioProfile, + bool? isSecondaryTrack) { switch (condition.Property) { @@ -102,6 +109,8 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, audioBitrate); case ProfileConditionValue.AudioChannels: return IsConditionSatisfied(condition, audioChannels); + case ProfileConditionValue.IsSecondaryAudio: + return IsConditionSatisfied(condition, isSecondaryTrack); default: throw new ArgumentException("Unexpected condition on audio file: " + condition.Property); } diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index a3eeecff2f..8161f1c268 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -117,7 +117,9 @@ namespace MediaBrowser.Model.Dlna TranscodeSeekInfo transcodeSeekInfo, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); @@ -158,7 +160,9 @@ namespace MediaBrowser.Model.Dlna timestamp, isAnamorphic, isCabac, - refFrames); + refFrames, + numVideoStreams, + numAudioStreams); List orgPnValues = new List(); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 4b137a268c..8b9b2edf36 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -281,7 +281,9 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp timestamp, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { container = StringHelper.TrimStart((container ?? string.Empty), '.'); @@ -315,7 +317,7 @@ namespace MediaBrowser.Model.Dlna var anyOff = false; foreach (ProfileCondition c in i.Conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { anyOff = true; break; diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs index ae6dc74c8c..7563ffb5af 100644 --- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -17,6 +17,9 @@ VideoTimestamp = 12, IsAnamorphic = 13, RefFrames = 14, - IsCabac = 15 + IsCabac = 15, + NumAudioStreams = 16, + NumVideoStreams = 17, + IsSecondaryAudio } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 6534eda10c..1cc37de57c 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -495,10 +495,13 @@ namespace MediaBrowser.Model.Dlna int? packetLength = videoStream == null ? null : videoStream.PacketLength; int? refFrames = videoStream == null ? null : videoStream.RefFrames; + int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio); + int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video); + // Check container conditions foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { return null; } @@ -525,7 +528,7 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { return null; } @@ -554,7 +557,8 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile)) + bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream); + if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile, isSecondaryAudio)) { return null; } @@ -752,6 +756,9 @@ namespace MediaBrowser.Model.Dlna case ProfileConditionValue.AudioProfile: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: + case ProfileConditionValue.NumAudioStreams: + case ProfileConditionValue.NumVideoStreams: + case ProfileConditionValue.IsSecondaryAudio: case ProfileConditionValue.VideoTimestamp: { // Not supported yet diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 12319a1227..feee2d765c 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -672,6 +672,42 @@ namespace MediaBrowser.Model.Dlna } } + public int? TargetVideoStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + public List GetSelectableAudioStreams() { return GetSelectableStreams(MediaStreamType.Audio); diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 3b45137241..302d18bc0f 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -46,8 +46,8 @@ namespace MediaBrowser.Model.Dto public int? Bitrate { get; set; } public TransportStreamTimestamp? Timestamp { get; set; } - public Dictionary RequiredHttpHeaders { get; set; } - + public Dictionary RequiredHttpHeaders { get; set; } + public string TranscodingUrl { get; set; } public string TranscodingSubProtocol { get; set; } public string TranscodingContainer { get; set; } @@ -135,5 +135,35 @@ namespace MediaBrowser.Model.Dto return null; } + + public int? GetStreamCount(MediaStreamType type) + { + int numMatches = 0; + int numStreams = 0; + + foreach (MediaStream i in MediaStreams) + { + numStreams++; + if (i.Type == type) + { + numMatches++; + } + } + + return numStreams == 0 ? (int?)null : numMatches; + } + + public bool? IsSecondaryAudio(MediaStream stream) + { + foreach (MediaStream currentStream in MediaStreams) + { + if (currentStream.Type == MediaStreamType.Audio) + { + return currentStream.Index != stream.Index; + } + } + + return null; + } } } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index fdcc4ceb66..ae20fafbab 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -35,6 +35,8 @@ "HeaderConfirmation": "Confirmation", "MessageKeyUpdated": "Thank you. Your supporter key has been updated.", "MessageKeyRemoved": "Thank you. Your supporter key has been removed.", + "TitleLiveTV": "Live TV", + "TitleSync": "Sync", "ErrorLaunchingChromecast": "There was an error launching chromecast. Please ensure your device is connected to your wireless network.", "MessageErrorLoadingSupporterInfo": "There was an error loading supporter information. Please try again later.", "MessageLinkYourSupporterKey": "Link your supporter key with up to {0} Emby Connect members to enjoy free access to the following apps:", @@ -93,7 +95,6 @@ "HeaderWelcomeToProjectWebClient": "Welcome to the Emby Web Client", "ButtonTakeTheTour": "Take the tour", "HeaderWelcomeBack": "Welcome back!", - "TitleSync": "Sync", "TitlePlugins": "Plugins", "ButtonTakeTheTourToSeeWhatsNew": "Take the tour to see what's new", "MessageNoSyncJobsFound": "No sync jobs found. Create sync jobs using the Sync buttons found throughout the web interface.", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 0fc603a083..f52d8a9fe3 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1400,5 +1400,7 @@ "HeaderUpcomingPrograms": "Upcoming Programs", "ButtonMoreItems": "More...", "LabelShowLibraryTileNames": "Show library tile names", - "LabelShowLibraryTileNamesHelp": "Determines if labels will be displayed underneath library tiles on the home page" + "LabelShowLibraryTileNamesHelp": "Determines if labels will be displayed underneath library tiles on the home page", + "OptionEnableTranscodingThrottle": "Enable throttling", + "OptionEnableTranscodingThrottleHelp": "Throttling will automatically adjust transcoding speed in order to minimize server cpu utilization during playback." } diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 8d4c40fdc5..c3e8cf9444 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Dlna; +using System.Collections.Generic; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.Server.Implementations.Sync { @@ -25,6 +26,9 @@ namespace MediaBrowser.Server.Implementations.Sync mkvAudio += ",dca"; } + var videoProfile = "high|main|baseline|constrained baseline"; + var videoLevel = "41"; + DirectPlayProfiles = new[] { new DirectPlayProfile @@ -48,13 +52,37 @@ namespace MediaBrowser.Server.Implementations.Sync } }; - ContainerProfiles = new ContainerProfile[] { }; + ContainerProfiles = new[] + { + new ContainerProfile + { + Type = DlnaProfileType.Video, + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.NotEquals, + Property = ProfileConditionValue.NumAudioStreams, + Value = "0", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.EqualsAny, + Property = ProfileConditionValue.NumVideoStreams, + Value = "1", + IsRequired = false + } + } + } + }; - CodecProfiles = new[] + var codecProfiles = new List { new CodecProfile { Type = CodecType.Video, + Codec = "h264", Conditions = new [] { new ProfileCondition @@ -65,23 +93,134 @@ namespace MediaBrowser.Server.Implementations.Sync IsRequired = false }, new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Width, + Value = "1920", + IsRequired = true + }, + new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080", + IsRequired = true + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.RefFrames, + Value = "4", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoFramerate, + Value = "30", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsAnamorphic, + Value = "false", IsRequired = false }, new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoLevel, + Value = videoLevel, + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.EqualsAny, + Property = ProfileConditionValue.VideoProfile, + Value = videoProfile, + IsRequired = false + } + } + }, + new CodecProfile + { + Type = CodecType.Video, + Codec = "mpeg4", + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoBitDepth, + Value = "8", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Width, + Value = "1920", + IsRequired = true + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Height, + Value = "1080", + IsRequired = true + }, + new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.RefFrames, Value = "4", IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoFramerate, + Value = "30", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsAnamorphic, + Value = "false", + IsRequired = false } } } }; + var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2"; + codecProfiles.Add(new CodecProfile + { + Type = CodecType.Audio, + Codec = "mpeg4", + Conditions = new[] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.AudioChannels, + Value = maxAudioChannels, + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsSecondaryAudio, + Value = "false", + IsRequired = false + } + } + }); + + CodecProfiles = codecProfiles.ToArray(); + SubtitleProfiles = new[] { new SubtitleProfile diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 0a995a8455..8982b5739c 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -364,7 +364,7 @@ namespace MediaBrowser.WebDashboard.Api "backdrops.js", "sync.js", "syncjob.js", - "syncservices.js", + "appservices.js", "playlistmanager.js", "mediaplayer.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 03a704a46b..2725d63ad8 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -151,7 +151,7 @@ PreserveNewest - + PreserveNewest @@ -175,7 +175,7 @@ PreserveNewest - + PreserveNewest diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index dd28aae3ac..40da3899c5 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.608 + 3.0.609 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 0124a609de..7aa9d30551 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.608 + 3.0.609 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 3297b66066..8d65ec4315 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.608 + 3.0.609 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 0a749c98b6..9a5264da95 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.608 + 3.0.609 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 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 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 0930c00024..ca735f0684 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 ff79bb48f4..58cfa086e3 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 b925649fce..94c91c55aa 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 0b07d8b6d1..0609df4c6b 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 f84748b971..04101fd465 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 58af016150..9675de38b0 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 ffd4995ad9..124739073a 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 37f605a590..dbbecaf057 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 dc33239a04..0df4de86d1 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 5c033e6bdc..f13c71c6df 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 66eeb61f72..4fab95263f 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 7f4440fbce..401cf87657 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 f676918593..a39781d6a3 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 e7333939e3..5bc8b80887 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 3acc79088d..102218979c 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 ca9d96c12f..dea8688482 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 40da3899c5..c5acc0a42d 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 7aa9d30551..1b29b02729 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 8d65ec4315..6ee79dd8c9 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 9a5264da95..c8b062f3f3 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 2632093ebbc95f8963b8d3adbd973dda61854496 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 31 Mar 2015 14:52:41 -0400 Subject: updated nuget --- Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Nuget/MediaBrowser.Model.Signed.nuspec') diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index c5acc0a42d..d310716661 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.611 + 3.0.612 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 1b29b02729..d4d7de019a 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.611 + 3.0.612 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 6ee79dd8c9..de9c503b92 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.611 + 3.0.612 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index c8b062f3f3..b0008f9d03 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.611 + 3.0.612 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