From 2b7a80cfb5b9212260734c095a5b3439af7d64e2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 4 Apr 2015 15:35:29 -0400 Subject: improve direct play of live streams --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 425889807..18d9ccece 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Session; +using MediaBrowser.MediaEncoding.Probing; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -103,15 +104,17 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Gets the media info. /// /// The input files. + /// The primary path. /// The protocol. /// if set to true [is audio]. + /// if set to true [extract chapters]. /// The cancellation token. /// Task. - public Task GetMediaInfo(string[] inputFiles, MediaProtocol protocol, bool isAudio, - CancellationToken cancellationToken) + public Task GetMediaInfo(string[] inputFiles, string primaryPath, MediaProtocol protocol, bool isAudio, + bool extractChapters, CancellationToken cancellationToken) { - return GetMediaInfoInternal(GetInputArgument(inputFiles, protocol), !isAudio, - GetProbeSizeArgument(inputFiles, protocol), cancellationToken); + return GetMediaInfoInternal(GetInputArgument(inputFiles, protocol), primaryPath, protocol, !isAudio && extractChapters, + GetProbeSizeArgument(inputFiles, protocol), isAudio, cancellationToken); } /// @@ -141,13 +144,17 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Gets the media info internal. /// /// The input path. + /// The primary path. + /// The protocol. /// if set to true [extract chapters]. /// The probe size argument. + /// if set to true [is audio]. /// The cancellation token. /// Task{MediaInfoResult}. /// - private async Task GetMediaInfoInternal(string inputPath, bool extractChapters, + private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, string probeSizeArgument, + bool isAudio, CancellationToken cancellationToken) { var args = extractChapters @@ -244,7 +251,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - return result; + return new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, isAudio, primaryPath, protocol); } /// -- cgit v1.2.3 From 30104bd8de62715d127823e69dc0de9e65d99840 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 5 Apr 2015 11:01:57 -0400 Subject: probe live streams after opening --- MediaBrowser.Api/Sync/SyncService.cs | 11 +- .../Entities/Audio/MusicArtist.cs | 1 + MediaBrowser.Controller/Entities/Folder.cs | 3 +- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 18 +- MediaBrowser.Controller/Entities/PhotoAlbum.cs | 36 +- .../Entities/UserViewBuilder.cs | 2 +- MediaBrowser.Controller/IO/ThrottledStream.cs | 393 +++++++++++++++++++++ .../MediaBrowser.Controller.csproj | 3 + .../MediaEncoding/IMediaEncoder.cs | 8 +- .../MediaEncoding/MediaInfoRequest.cs | 24 ++ .../Sync/IRemoteSyncProvider.cs | 10 + MediaBrowser.Controller/Sync/ISyncManager.cs | 14 + .../ContentDirectory/ControlHandler.cs | 8 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 20 +- .../Probing/ProbeResultNormalizer.cs | 18 +- .../MediaBrowser.Model.Portable.csproj | 6 +- .../MediaBrowser.Model.net35.csproj | 6 +- MediaBrowser.Model/Entities/MediaInfo.cs | 65 ---- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- MediaBrowser.Model/MediaInfo/MediaInfo.cs | 66 ++++ MediaBrowser.Model/Sync/SyncOptions.cs | 1 + MediaBrowser.Model/Sync/SyncQualityOption.cs | 5 + MediaBrowser.Model/Users/UserPolicy.cs | 10 +- .../BoxSets/BoxSetMetadataService.cs | 6 +- .../MediaInfo/FFProbeAudioInfo.cs | 17 +- .../MediaInfo/FFProbeVideoInfo.cs | 23 +- .../Collections/ManualCollectionsFolder.cs | 4 +- .../HttpServer/ThrottledStream.cs | 393 --------------------- .../Library/MediaSourceManager.cs | 27 +- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../LiveTv/LiveTvMediaSourceProvider.cs | 10 +- .../Localization/Server/server.json | 8 +- .../MediaBrowser.Server.Implementations.csproj | 1 - .../Photos/PhotoAlbumImageProvider.cs | 2 +- .../Sync/AppSyncProvider.cs | 2 +- .../Sync/MediaSync.cs | 27 +- .../Sync/MultiProviderSync.cs | 9 +- .../Sync/ServerSyncScheduledTask.cs | 9 +- .../Sync/SyncManager.cs | 56 ++- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 43 files changed, 750 insertions(+), 588 deletions(-) create mode 100644 MediaBrowser.Controller/IO/ThrottledStream.cs create mode 100644 MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs create mode 100644 MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs delete mode 100644 MediaBrowser.Model/Entities/MediaInfo.cs create mode 100644 MediaBrowser.Model/MediaInfo/MediaInfo.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs index b9dbf5946..d5f88e6a4 100644 --- a/MediaBrowser.Api/Sync/SyncService.cs +++ b/MediaBrowser.Api/Sync/SyncService.cs @@ -248,6 +248,9 @@ namespace MediaBrowser.Api.Sync result.Targets = _syncManager.GetSyncTargets(request.UserId) .ToList(); + var auth = AuthorizationContext.GetAuthorizationInfo(Request); + var authenticatedUser = _userManager.GetUserById(auth.UserId); + if (!string.IsNullOrWhiteSpace(request.TargetId)) { result.Targets = result.Targets @@ -255,11 +258,11 @@ namespace MediaBrowser.Api.Sync .ToList(); result.QualityOptions = _syncManager - .GetQualityOptions(request.TargetId) + .GetQualityOptions(request.TargetId, authenticatedUser) .ToList(); result.ProfileOptions = _syncManager - .GetProfileOptions(request.TargetId) + .GetProfileOptions(request.TargetId, authenticatedUser) .ToList(); } @@ -277,10 +280,6 @@ namespace MediaBrowser.Api.Sync } }; - var auth = AuthorizationContext.GetAuthorizationInfo(Request); - - var authenticatedUser = _userManager.GetUserById(auth.UserId); - var items = request.ItemIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(_libraryManager.GetItemById) .Where(i => i != null); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 4185590ab..e0c14821e 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Users; using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 14095f7ff..61e5acdb3 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -991,8 +991,9 @@ namespace MediaBrowser.Controller.Entities } var locations = user.RootFolder - .GetChildren(user, true) + .Children .OfType() + .Where(i => i.IsVisible(user)) .SelectMany(i => i.PhysicalLocations) .ToList(); diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 0778643da..02e9d4cf9 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -175,19 +175,19 @@ namespace MediaBrowser.Controller.Entities.Movies public override bool IsVisible(User user) { - if (base.IsVisible(user)) - { - var userId = user.Id.ToString("N"); - - // Need to check Count > 0 for boxsets created prior to the introduction of Shares - if (Shares.Count > 0 && !Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase))) - { - //return false; - } + var userId = user.Id.ToString("N"); + // Need to check Count > 0 for boxsets created prior to the introduction of Shares + if (Shares.Count > 0 && Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase))) + { return true; } + if (base.IsVisible(user)) + { + return GetChildren(user, true).Any(); + } + return false; } } diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index 24ebf8815..5b48a70e9 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -1,11 +1,15 @@ -using MediaBrowser.Model.Configuration; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Users; +using System; using System.Linq; using System.Runtime.Serialization; -using MediaBrowser.Model.Users; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { - public class PhotoAlbum : Folder + public class PhotoAlbum : Folder, IMetadataContainer { public override bool SupportsLocalMetadata { @@ -28,5 +32,31 @@ namespace MediaBrowser.Controller.Entities { return config.BlockUnratedItems.Contains(UnratedItem.Other); } + + public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken) + { + var items = GetRecursiveChildren().ToList(); + + var totalItems = items.Count; + var numComplete = 0; + + // Refresh songs + foreach (var item in items) + { + cancellationToken.ThrowIfCancellationRequested(); + + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); + } + + // Refresh current item + await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + + progress.Report(100); + } } } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 0e602dabe..c01814bce 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -259,7 +259,7 @@ namespace MediaBrowser.Controller.Entities list.Add(await GetUserView(SpecialFolder.MusicLatest, user, "0", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "1", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "3", parent).ConfigureAwait(false)); + //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "3", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "4", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "5", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "6", parent).ConfigureAwait(false)); diff --git a/MediaBrowser.Controller/IO/ThrottledStream.cs b/MediaBrowser.Controller/IO/ThrottledStream.cs new file mode 100644 index 000000000..1df00b45a --- /dev/null +++ b/MediaBrowser.Controller/IO/ThrottledStream.cs @@ -0,0 +1,393 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.IO +{ + /// + /// Class for streaming data with throttling support. + /// + public class ThrottledStream : Stream + { + /// + /// A constant used to specify an infinite number of bytes that can be transferred per second. + /// + public const long Infinite = 0; + + #region Private members + /// + /// The base stream. + /// + private readonly Stream _baseStream; + + /// + /// The maximum bytes per second that can be transferred through the base stream. + /// + private long _maximumBytesPerSecond; + + /// + /// The number of bytes that has been transferred since the last throttle. + /// + private long _byteCount; + + /// + /// The start time in milliseconds of the last throttle. + /// + private long _start; + #endregion + + #region Properties + /// + /// Gets the current milliseconds. + /// + /// The current milliseconds. + protected long CurrentMilliseconds + { + get + { + return Environment.TickCount; + } + } + + /// + /// Gets or sets the maximum bytes per second that can be transferred through the base stream. + /// + /// The maximum bytes per second. + public long MaximumBytesPerSecond + { + get + { + return _maximumBytesPerSecond; + } + set + { + if (MaximumBytesPerSecond != value) + { + _maximumBytesPerSecond = value; + Reset(); + } + } + } + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + /// true if the stream supports reading; otherwise, false. + public override bool CanRead + { + get + { + return _baseStream.CanRead; + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + /// + /// true if the stream supports seeking; otherwise, false. + public override bool CanSeek + { + get + { + return _baseStream.CanSeek; + } + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + /// + /// true if the stream supports writing; otherwise, false. + public override bool CanWrite + { + get + { + return _baseStream.CanWrite; + } + } + + /// + /// Gets the length in bytes of the stream. + /// + /// + /// A long value representing the length of the stream in bytes. + /// The base stream does not support seeking. + /// Methods were called after the stream was closed. + public override long Length + { + get + { + return _baseStream.Length; + } + } + + /// + /// Gets or sets the position within the current stream. + /// + /// + /// The current position within the stream. + /// An I/O error occurs. + /// The base stream does not support seeking. + /// Methods were called after the stream was closed. + public override long Position + { + get + { + return _baseStream.Position; + } + set + { + _baseStream.Position = value; + } + } + #endregion + + public long MinThrottlePosition; + + #region Ctor + /// + /// Initializes a new instance of the class. + /// + /// The base stream. + /// The maximum bytes per second that can be transferred through the base stream. + /// Thrown when is a null reference. + /// Thrown when is a negative value. + public ThrottledStream(Stream baseStream, long maximumBytesPerSecond) + { + if (baseStream == null) + { + throw new ArgumentNullException("baseStream"); + } + + if (maximumBytesPerSecond < 0) + { + throw new ArgumentOutOfRangeException("maximumBytesPerSecond", + maximumBytesPerSecond, "The maximum number of bytes per second can't be negative."); + } + + _baseStream = baseStream; + _maximumBytesPerSecond = maximumBytesPerSecond; + _start = CurrentMilliseconds; + _byteCount = 0; + } + #endregion + + #region Public methods + /// + /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. + /// + /// An I/O error occurs. + public override void Flush() + { + _baseStream.Flush(); + } + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The base stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. + public override int Read(byte[] buffer, int offset, int count) + { + Throttle(count); + + return _baseStream.Read(buffer, offset, count); + } + + /// + /// Sets the position within the current stream. + /// + /// A byte offset relative to the origin parameter. + /// A value of type indicating the reference point used to obtain the new position. + /// + /// The new position within the current stream. + /// + /// An I/O error occurs. + /// The base stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. + public override long Seek(long offset, SeekOrigin origin) + { + return _baseStream.Seek(offset, origin); + } + + /// + /// Sets the length of the current stream. + /// + /// The desired length of the current stream in bytes. + /// The base stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. + public override void SetLength(long value) + { + _baseStream.SetLength(value); + } + + private long _bytesWritten; + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies count bytes from buffer to the current stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// An I/O error occurs. + /// The base stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. + public override void Write(byte[] buffer, int offset, int count) + { + Throttle(count); + + _baseStream.Write(buffer, offset, count); + + _bytesWritten += count; + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await ThrottleAsync(count, cancellationToken).ConfigureAwait(false); + + await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + + _bytesWritten += count; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return _baseStream.ToString(); + } + #endregion + + private bool ThrottleCheck(int bufferSizeInBytes) + { + if (_bytesWritten < MinThrottlePosition) + { + return false; + } + + // Make sure the buffer isn't empty. + if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0) + { + return false; + } + + return true; + } + + #region Protected methods + /// + /// Throttles for the specified buffer size in bytes. + /// + /// The buffer size in bytes. + protected void Throttle(int bufferSizeInBytes) + { + if (!ThrottleCheck(bufferSizeInBytes)) + { + return ; + } + + _byteCount += bufferSizeInBytes; + long elapsedMilliseconds = CurrentMilliseconds - _start; + + if (elapsedMilliseconds > 0) + { + // Calculate the current bps. + long bps = _byteCount * 1000L / elapsedMilliseconds; + + // If the bps are more then the maximum bps, try to throttle. + if (bps > _maximumBytesPerSecond) + { + // Calculate the time to sleep. + long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond; + int toSleep = (int)(wakeElapsed - elapsedMilliseconds); + + if (toSleep > 1) + { + try + { + // The time to sleep is more then a millisecond, so sleep. + Thread.Sleep(toSleep); + } + catch (ThreadAbortException) + { + // Eatup ThreadAbortException. + } + + // A sleep has been done, reset. + Reset(); + } + } + } + } + + protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken) + { + if (!ThrottleCheck(bufferSizeInBytes)) + { + return; + } + + _byteCount += bufferSizeInBytes; + long elapsedMilliseconds = CurrentMilliseconds - _start; + + if (elapsedMilliseconds > 0) + { + // Calculate the current bps. + long bps = _byteCount * 1000L / elapsedMilliseconds; + + // If the bps are more then the maximum bps, try to throttle. + if (bps > _maximumBytesPerSecond) + { + // Calculate the time to sleep. + long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond; + int toSleep = (int)(wakeElapsed - elapsedMilliseconds); + + if (toSleep > 1) + { + // The time to sleep is more then a millisecond, so sleep. + await Task.Delay(toSleep, cancellationToken).ConfigureAwait(false); + + // A sleep has been done, reset. + Reset(); + } + } + } + } + + /// + /// Will reset the bytecount to 0 and reset the start time to the current time. + /// + protected void Reset() + { + long difference = CurrentMilliseconds - _start; + + // Only reset counters when a known history is available of more then 1 second. + if (difference > 1000) + { + _byteCount = 0; + _start = CurrentMilliseconds; + } + } + #endregion + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 9a4a2cb62..8c4154966 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -171,6 +171,7 @@ + @@ -212,6 +213,7 @@ + @@ -393,6 +395,7 @@ + diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index bb5674864..5bec7980a 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -67,14 +67,10 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Gets the media info. /// - /// The input files. - /// The primary path. - /// The protocol. - /// if set to true [is audio]. - /// if set to true [extract chapters]. + /// The request. /// The cancellation token. /// Task. - Task GetMediaInfo(string[] inputFiles, string primaryPath, MediaProtocol protocol, bool isAudio, bool extractChapters, CancellationToken cancellationToken); + Task GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken); /// /// Gets the probe size argument. diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs new file mode 100644 index 000000000..ca0c2fdbb --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -0,0 +1,24 @@ +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.MediaInfo; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.MediaEncoding +{ + public class MediaInfoRequest + { + public string InputPath { get; set; } + public MediaProtocol Protocol { get; set; } + public bool ExtractChapters { get; set; } + public DlnaProfileType MediaType { get; set; } + public IIsoMount MountedIso { get; set; } + public VideoType VideoType { get; set; } + public List PlayableStreamFileNames { get; set; } + + public MediaInfoRequest() + { + PlayableStreamFileNames = new List(); + } + } +} diff --git a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs new file mode 100644 index 000000000..aeb7a3bff --- /dev/null +++ b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Controller.Sync +{ + /// + /// A marker interface + /// + public interface IRemoteSyncProvider + { + } +} diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 3b6e20edc..97591551c 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -174,6 +174,13 @@ namespace MediaBrowser.Controller.Sync /// The target identifier. /// IEnumerable<SyncQualityOption>. IEnumerable GetQualityOptions(string targetId); + /// + /// Gets the quality options. + /// + /// The target identifier. + /// The user. + /// IEnumerable<SyncQualityOption>. + IEnumerable GetQualityOptions(string targetId, User user); /// /// Gets the profile options. @@ -181,5 +188,12 @@ namespace MediaBrowser.Controller.Sync /// The target identifier. /// IEnumerable<SyncQualityOption>. IEnumerable GetProfileOptions(string targetId); + /// + /// Gets the profile options. + /// + /// The target identifier. + /// The user. + /// IEnumerable<SyncProfileOption>. + IEnumerable GetProfileOptions(string targetId, User user); } } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 5ccea52ba..abd649ad7 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -223,7 +223,7 @@ namespace MediaBrowser.Dlna.ContentDirectory if (string.Equals(flag, "BrowseMetadata")) { totalCount = 1; - + if (item.IsFolder || serverItem.StubType.HasValue) { var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false)); @@ -350,7 +350,7 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private async Task> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) + private Task> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) { var folder = (Folder)item; @@ -389,7 +389,7 @@ namespace MediaBrowser.Dlna.ContentDirectory isFolder = true; } - return await folder.GetItems(new InternalItemsQuery + return folder.GetItems(new InternalItemsQuery { Limit = limit, StartIndex = startIndex, @@ -401,7 +401,7 @@ namespace MediaBrowser.Dlna.ContentDirectory IsFolder = isFolder, MediaTypes = mediaTypes.ToArray() - }).ConfigureAwait(false); + }); } private async Task> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 18d9ccece..be636c0ba 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Session; using MediaBrowser.MediaEncoding.Probing; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -103,18 +104,17 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Gets the media info. /// - /// The input files. - /// The primary path. - /// The protocol. - /// if set to true [is audio]. - /// if set to true [extract chapters]. + /// The request. /// The cancellation token. /// Task. - public Task GetMediaInfo(string[] inputFiles, string primaryPath, MediaProtocol protocol, bool isAudio, - bool extractChapters, CancellationToken cancellationToken) + public Task GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) { - return GetMediaInfoInternal(GetInputArgument(inputFiles, protocol), primaryPath, protocol, !isAudio && extractChapters, - GetProbeSizeArgument(inputFiles, protocol), isAudio, cancellationToken); + var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; + + var inputFiles = MediaEncoderHelpers.GetInputArgument(request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); + + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, + GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, cancellationToken); } /// @@ -152,7 +152,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The cancellation token. /// Task{MediaInfoResult}. /// - private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, + private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, string probeSizeArgument, bool isAudio, CancellationToken cancellationToken) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 48e8b6ee2..7df3cb56f 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -25,9 +25,9 @@ namespace MediaBrowser.MediaEncoding.Probing _fileSystem = fileSystem; } - public Model.Entities.MediaInfo GetMediaInfo(InternalMediaInfoResult data, bool isAudio, string path, MediaProtocol protocol) + public Model.MediaInfo.MediaInfo GetMediaInfo(InternalMediaInfoResult data, bool isAudio, string path, MediaProtocol protocol) { - var info = new Model.Entities.MediaInfo + var info = new Model.MediaInfo.MediaInfo { Path = path, Protocol = protocol @@ -342,7 +342,7 @@ namespace MediaBrowser.MediaEncoding.Probing return null; } - private void SetAudioRuntimeTicks(InternalMediaInfoResult result, Model.Entities.MediaInfo data) + private void SetAudioRuntimeTicks(InternalMediaInfoResult result, Model.MediaInfo.MediaInfo data) { if (result.streams != null) { @@ -369,7 +369,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } - private void SetSize(InternalMediaInfoResult data, Model.Entities.MediaInfo info) + private void SetSize(InternalMediaInfoResult data, Model.MediaInfo.MediaInfo info) { if (data.format != null) { @@ -384,7 +384,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } - private void SetAudioInfoFromTags(Model.Entities.MediaInfo audio, Dictionary tags) + private void SetAudioInfoFromTags(Model.MediaInfo.MediaInfo audio, Dictionary tags) { var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); @@ -591,7 +591,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// The audio. /// The tags. /// Name of the tag. - private void FetchStudios(Model.Entities.MediaInfo audio, Dictionary tags, string tagName) + private void FetchStudios(Model.MediaInfo.MediaInfo audio, Dictionary tags, string tagName) { var val = FFProbeHelpers.GetDictionaryValue(tags, tagName); @@ -626,7 +626,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The information. /// The tags. - private void FetchGenres(Model.Entities.MediaInfo info, Dictionary tags) + private void FetchGenres(Model.MediaInfo.MediaInfo info, Dictionary tags) { var val = FFProbeHelpers.GetDictionaryValue(tags, "genre"); @@ -697,7 +697,7 @@ namespace MediaBrowser.MediaEncoding.Probing private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames) - private void FetchWtvInfo(Model.Entities.MediaInfo video, InternalMediaInfoResult data) + private void FetchWtvInfo(Model.MediaInfo.MediaInfo video, InternalMediaInfoResult data) { if (data.format == null || data.format.tags == null) { @@ -806,7 +806,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } - private void ExtractTimestamp(Model.Entities.MediaInfo video) + private void ExtractTimestamp(Model.MediaInfo.MediaInfo video) { if (video.VideoType == VideoType.VideoFile) { diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index fdedc51a2..c3ec911de 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -560,9 +560,6 @@ Entities\MBRegistrationRecord.cs - - Entities\MediaInfo.cs - Entities\MediaStream.cs @@ -809,6 +806,9 @@ MediaInfo\LiveStreamResponse.cs + + MediaInfo\MediaInfo.cs + MediaInfo\MediaProtocol.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 3618aa972..437fcc8ff 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -525,9 +525,6 @@ Entities\MBRegistrationRecord.cs - - Entities\MediaInfo.cs - Entities\MediaStream.cs @@ -765,6 +762,9 @@ MediaInfo\LiveStreamResponse.cs + + MediaInfo\MediaInfo.cs + MediaInfo\MediaProtocol.cs diff --git a/MediaBrowser.Model/Entities/MediaInfo.cs b/MediaBrowser.Model/Entities/MediaInfo.cs deleted file mode 100644 index 67efe3108..000000000 --- a/MediaBrowser.Model/Entities/MediaInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -using MediaBrowser.Model.Dto; -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Model.Entities -{ - public class MediaInfo : MediaSourceInfo, IHasProviderIds - { - public List Chapters { get; set; } - - /// - /// Gets or sets the title. - /// - /// The title. - public string Title { get; set; } - /// - /// Gets or sets the album. - /// - /// The album. - public string Album { get; set; } - /// - /// Gets or sets the artists. - /// - /// The artists. - public List Artists { get; set; } - /// - /// Gets or sets the album artists. - /// - /// The album artists. - public List AlbumArtists { get; set; } - /// - /// Gets or sets the studios. - /// - /// The studios. - public List Studios { get; set; } - public List Genres { get; set; } - public int? IndexNumber { get; set; } - public int? ParentIndexNumber { get; set; } - public int? ProductionYear { get; set; } - public DateTime? PremiereDate { get; set; } - public List People { get; set; } - public Dictionary ProviderIds { get; set; } - /// - /// Gets or sets the official rating. - /// - /// The official rating. - public string OfficialRating { get; set; } - /// - /// Gets or sets the overview. - /// - /// The overview. - public string Overview { get; set; } - - public MediaInfo() - { - Chapters = new List(); - Artists = new List(); - AlbumArtists = new List(); - Studios = new List(); - Genres = new List(); - People = new List(); - ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 067309512..2a210b7a2 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -226,7 +226,7 @@ - + diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs new file mode 100644 index 000000000..21f258693 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs @@ -0,0 +1,66 @@ +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.MediaInfo +{ + public class MediaInfo : MediaSourceInfo, IHasProviderIds + { + public List Chapters { get; set; } + + /// + /// Gets or sets the title. + /// + /// The title. + public string Title { get; set; } + /// + /// Gets or sets the album. + /// + /// The album. + public string Album { get; set; } + /// + /// Gets or sets the artists. + /// + /// The artists. + public List Artists { get; set; } + /// + /// Gets or sets the album artists. + /// + /// The album artists. + public List AlbumArtists { get; set; } + /// + /// Gets or sets the studios. + /// + /// The studios. + public List Studios { get; set; } + public List Genres { get; set; } + public int? IndexNumber { get; set; } + public int? ParentIndexNumber { get; set; } + public int? ProductionYear { get; set; } + public DateTime? PremiereDate { get; set; } + public List People { get; set; } + public Dictionary ProviderIds { get; set; } + /// + /// Gets or sets the official rating. + /// + /// The official rating. + public string OfficialRating { get; set; } + /// + /// Gets or sets the overview. + /// + /// The overview. + public string Overview { get; set; } + + public MediaInfo() + { + Chapters = new List(); + Artists = new List(); + AlbumArtists = new List(); + Studios = new List(); + Genres = new List(); + People = new List(); + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Model/Sync/SyncOptions.cs b/MediaBrowser.Model/Sync/SyncOptions.cs index 294f7bcef..765dea86b 100644 --- a/MediaBrowser.Model/Sync/SyncOptions.cs +++ b/MediaBrowser.Model/Sync/SyncOptions.cs @@ -4,5 +4,6 @@ namespace MediaBrowser.Model.Sync public class SyncOptions { public string TemporaryPath { get; set; } + public long UploadSpeedLimitBytes { get; set; } } } diff --git a/MediaBrowser.Model/Sync/SyncQualityOption.cs b/MediaBrowser.Model/Sync/SyncQualityOption.cs index 597b98727..6eff4b9a4 100644 --- a/MediaBrowser.Model/Sync/SyncQualityOption.cs +++ b/MediaBrowser.Model/Sync/SyncQualityOption.cs @@ -23,5 +23,10 @@ namespace MediaBrowser.Model.Sync /// /// true if this instance is default; otherwise, false. public bool IsDefault { get; set; } + /// + /// Gets or sets a value indicating whether this instance is original quality. + /// + /// true if this instance is original quality; otherwise, false. + public bool IsOriginalQuality { get; set; } } } diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 640f03e2a..b3c599496 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -39,6 +39,8 @@ namespace MediaBrowser.Model.Users public bool EnableLiveTvAccess { get; set; } public bool EnableMediaPlayback { get; set; } + public bool EnableMediaPlaybackTranscoding { get; set; } + public bool EnableContentDeletion { get; set; } public bool EnableContentDownloading { get; set; } @@ -47,6 +49,7 @@ namespace MediaBrowser.Model.Users /// /// true if [enable synchronize]; otherwise, false. public bool EnableSync { get; set; } + public bool EnableSyncTranscoding { get; set; } public string[] EnabledDevices { get; set; } public bool EnableAllDevices { get; set; } @@ -62,9 +65,14 @@ namespace MediaBrowser.Model.Users public UserPolicy() { EnableSync = true; - EnableLiveTvManagement = true; + EnableSyncTranscoding = true; + EnableMediaPlayback = true; + EnableMediaPlaybackTranscoding = true; + + EnableLiveTvManagement = true; EnableLiveTvAccess = true; + EnableSharedDeviceControl = true; BlockedTags = new string[] { }; diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index 3ac3cccb3..92327c9bc 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -16,11 +15,8 @@ namespace MediaBrowser.Providers.BoxSets { public class BoxSetMetadataService : MetadataService { - private readonly ILocalizationManager _iLocalizationManager; - - public BoxSetMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILocalizationManager iLocalizationManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager) + public BoxSetMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager) { - _iLocalizationManager = iLocalizationManager; } /// diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index b1bed7310..3e816802e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; @@ -53,7 +54,7 @@ namespace MediaBrowser.Providers.MediaInfo private const string SchemaVersion = "2"; - private async Task GetMediaInfo(BaseItem item, CancellationToken cancellationToken) + private async Task GetMediaInfo(BaseItem item, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -64,7 +65,7 @@ namespace MediaBrowser.Providers.MediaInfo try { - return _json.DeserializeFromFile(cachePath); + return _json.DeserializeFromFile(cachePath); } catch (FileNotFoundException) { @@ -74,9 +75,13 @@ namespace MediaBrowser.Providers.MediaInfo { } - var inputPath = new[] { item.Path }; + var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest + { + InputPath = item.Path, + MediaType = DlnaProfileType.Audio, + Protocol = MediaProtocol.File - var result = await _mediaEncoder.GetMediaInfo(inputPath, item.Path, MediaProtocol.File, true, false, cancellationToken).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); _json.SerializeToFile(result, cachePath); @@ -91,7 +96,7 @@ namespace MediaBrowser.Providers.MediaInfo /// The cancellation token. /// The media information. /// Task. - protected Task Fetch(Audio audio, CancellationToken cancellationToken, Model.Entities.MediaInfo mediaInfo) + protected Task Fetch(Audio audio, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo) { var mediaStreams = mediaInfo.MediaStreams; @@ -115,7 +120,7 @@ namespace MediaBrowser.Providers.MediaInfo /// /// The audio. /// The data. - private void FetchDataFromTags(Audio audio, Model.Entities.MediaInfo data) + private void FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data) { // Only set Name if title was found in the dictionary if (!string.IsNullOrEmpty(data.Title)) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index cec66f3c1..c433018c0 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -1,5 +1,6 @@ using DvdLib.Ifo; using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Chapters; @@ -129,9 +130,9 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } - private const string SchemaVersion = "1"; + private const string SchemaVersion = "2"; - private async Task GetMediaInfo(Video item, + private async Task GetMediaInfo(Video item, IIsoMount isoMount, CancellationToken cancellationToken) { @@ -144,7 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo try { - return _json.DeserializeFromFile(cachePath); + return _json.DeserializeFromFile(cachePath); } catch (FileNotFoundException) { @@ -158,9 +159,17 @@ namespace MediaBrowser.Providers.MediaInfo ? MediaProtocol.Http : MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, protocol, isoMount, item.PlayableStreamFileNames); + var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest + { + PlayableStreamFileNames = item.PlayableStreamFileNames, + MountedIso = isoMount, + ExtractChapters = true, + VideoType = item.VideoType, + MediaType = DlnaProfileType.Video, + InputPath = item.Path, + Protocol = protocol - var result = await _mediaEncoder.GetMediaInfo(inputPath, item.Path, protocol, false, true, cancellationToken).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); _json.SerializeToFile(result, cachePath); @@ -170,7 +179,7 @@ namespace MediaBrowser.Providers.MediaInfo protected async Task Fetch(Video video, CancellationToken cancellationToken, - Model.Entities.MediaInfo mediaInfo, + Model.MediaInfo.MediaInfo mediaInfo, IIsoMount isoMount, BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) @@ -348,7 +357,7 @@ namespace MediaBrowser.Providers.MediaInfo return _blurayExaminer.GetDiscInfo(path); } - private void FetchEmbeddedInfo(Video video, Model.Entities.MediaInfo data) + private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data) { if (!video.LockedFields.Contains(MetadataFields.OfficialRating)) { diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs index bbe37cb50..8f5d8fe9b 100644 --- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs @@ -14,9 +14,7 @@ namespace MediaBrowser.Server.Implementations.Collections public override bool IsVisible(User user) { - return base.IsVisible(user) && GetChildren(user, false) - .OfType() - .Any(i => i.IsVisible(user)); + return base.IsVisible(user) && GetChildren(user, false).Any(); } public override bool IsHidden diff --git a/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs b/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs deleted file mode 100644 index 1c01fa9e0..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs +++ /dev/null @@ -1,393 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// - /// Class for streaming data with throttling support. - /// - public class ThrottledStream : Stream - { - /// - /// A constant used to specify an infinite number of bytes that can be transferred per second. - /// - public const long Infinite = 0; - - #region Private members - /// - /// The base stream. - /// - private readonly Stream _baseStream; - - /// - /// The maximum bytes per second that can be transferred through the base stream. - /// - private long _maximumBytesPerSecond; - - /// - /// The number of bytes that has been transferred since the last throttle. - /// - private long _byteCount; - - /// - /// The start time in milliseconds of the last throttle. - /// - private long _start; - #endregion - - #region Properties - /// - /// Gets the current milliseconds. - /// - /// The current milliseconds. - protected long CurrentMilliseconds - { - get - { - return Environment.TickCount; - } - } - - /// - /// Gets or sets the maximum bytes per second that can be transferred through the base stream. - /// - /// The maximum bytes per second. - public long MaximumBytesPerSecond - { - get - { - return _maximumBytesPerSecond; - } - set - { - if (MaximumBytesPerSecond != value) - { - _maximumBytesPerSecond = value; - Reset(); - } - } - } - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get - { - return _baseStream.CanRead; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get - { - return _baseStream.CanSeek; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get - { - return _baseStream.CanWrite; - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// - /// A long value representing the length of the stream in bytes. - /// The base stream does not support seeking. - /// Methods were called after the stream was closed. - public override long Length - { - get - { - return _baseStream.Length; - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// - /// The current position within the stream. - /// An I/O error occurs. - /// The base stream does not support seeking. - /// Methods were called after the stream was closed. - public override long Position - { - get - { - return _baseStream.Position; - } - set - { - _baseStream.Position = value; - } - } - #endregion - - public long MinThrottlePosition; - - #region Ctor - /// - /// Initializes a new instance of the class. - /// - /// The base stream. - /// The maximum bytes per second that can be transferred through the base stream. - /// Thrown when is a null reference. - /// Thrown when is a negative value. - public ThrottledStream(Stream baseStream, long maximumBytesPerSecond) - { - if (baseStream == null) - { - throw new ArgumentNullException("baseStream"); - } - - if (maximumBytesPerSecond < 0) - { - throw new ArgumentOutOfRangeException("maximumBytesPerSecond", - maximumBytesPerSecond, "The maximum number of bytes per second can't be negative."); - } - - _baseStream = baseStream; - _maximumBytesPerSecond = maximumBytesPerSecond; - _start = CurrentMilliseconds; - _byteCount = 0; - } - #endregion - - #region Public methods - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// An I/O error occurs. - public override void Flush() - { - _baseStream.Flush(); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The base stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. - public override int Read(byte[] buffer, int offset, int count) - { - Throttle(count); - - return _baseStream.Read(buffer, offset, count); - } - - /// - /// Sets the position within the current stream. - /// - /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// The base stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) - { - return _baseStream.Seek(offset, origin); - } - - /// - /// Sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - /// The base stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. - public override void SetLength(long value) - { - _baseStream.SetLength(value); - } - - private long _bytesWritten; - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The base stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. - public override void Write(byte[] buffer, int offset, int count) - { - Throttle(count); - - _baseStream.Write(buffer, offset, count); - - _bytesWritten += count; - } - - public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await ThrottleAsync(count, cancellationToken).ConfigureAwait(false); - - await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - - _bytesWritten += count; - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return _baseStream.ToString(); - } - #endregion - - private bool ThrottleCheck(int bufferSizeInBytes) - { - if (_bytesWritten < MinThrottlePosition) - { - return false; - } - - // Make sure the buffer isn't empty. - if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0) - { - return false; - } - - return true; - } - - #region Protected methods - /// - /// Throttles for the specified buffer size in bytes. - /// - /// The buffer size in bytes. - protected void Throttle(int bufferSizeInBytes) - { - if (!ThrottleCheck(bufferSizeInBytes)) - { - return ; - } - - _byteCount += bufferSizeInBytes; - long elapsedMilliseconds = CurrentMilliseconds - _start; - - if (elapsedMilliseconds > 0) - { - // Calculate the current bps. - long bps = _byteCount * 1000L / elapsedMilliseconds; - - // If the bps are more then the maximum bps, try to throttle. - if (bps > _maximumBytesPerSecond) - { - // Calculate the time to sleep. - long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond; - int toSleep = (int)(wakeElapsed - elapsedMilliseconds); - - if (toSleep > 1) - { - try - { - // The time to sleep is more then a millisecond, so sleep. - Thread.Sleep(toSleep); - } - catch (ThreadAbortException) - { - // Eatup ThreadAbortException. - } - - // A sleep has been done, reset. - Reset(); - } - } - } - } - - protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken) - { - if (!ThrottleCheck(bufferSizeInBytes)) - { - return; - } - - _byteCount += bufferSizeInBytes; - long elapsedMilliseconds = CurrentMilliseconds - _start; - - if (elapsedMilliseconds > 0) - { - // Calculate the current bps. - long bps = _byteCount * 1000L / elapsedMilliseconds; - - // If the bps are more then the maximum bps, try to throttle. - if (bps > _maximumBytesPerSecond) - { - // Calculate the time to sleep. - long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond; - int toSleep = (int)(wakeElapsed - elapsedMilliseconds); - - if (toSleep > 1) - { - // The time to sleep is more then a millisecond, so sleep. - await Task.Delay(toSleep, cancellationToken).ConfigureAwait(false); - - // A sleep has been done, reset. - Reset(); - } - } - } - } - - /// - /// Will reset the bytecount to 0 and reset the start time to the current time. - /// - protected void Reset() - { - long difference = CurrentMilliseconds - _start; - - // Only reset counters when a known history is available of more then 1 second. - if (difference > 1000) - { - _byteCount = 0; - _start = CurrentMilliseconds; - } - } - #endregion - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 27a7d4ea9..01efe0ab1 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -137,21 +137,16 @@ namespace MediaBrowser.Server.Implementations.Library public async Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, CancellationToken cancellationToken) { var item = _libraryManager.GetItemById(id); - IEnumerable mediaSources; var hasMediaSources = (IHasMediaSources)item; User user = null; - if (string.IsNullOrWhiteSpace(userId)) - { - mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution); - } - else + if (!string.IsNullOrWhiteSpace(userId)) { user = _userManager.GetUserById(userId); - mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user); } + var mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user); var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false); var list = new List(); @@ -166,9 +161,11 @@ namespace MediaBrowser.Server.Implementations.Library } if (source.Protocol == MediaProtocol.File) { - source.SupportsDirectStream = File.Exists(source.Path); - // TODO: Path substitution + if (!File.Exists(source.Path)) + { + source.SupportsDirectStream = false; + } } else if (source.Protocol == MediaProtocol.Http) { @@ -183,6 +180,17 @@ namespace MediaBrowser.Server.Implementations.Library list.Add(source); } + foreach (var source in list) + { + if (user != null) + { + if (!user.Policy.EnableMediaPlaybackTranscoding) + { + source.SupportsTranscoding = false; + } + } + } + return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder); } @@ -343,6 +351,7 @@ namespace MediaBrowser.Server.Implementations.Library } var json = _jsonSerializer.SerializeToString(mediaSource); + _logger.Debug("Live stream opened: " + json); var clone = _jsonSerializer.DeserializeFromString(json); if (!string.IsNullOrWhiteSpace(request.UserId)) diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 71daf2b0c..f88293b2a 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) { - return ResolveVideos /// The options. void CreateImageCollage(ImageCollageOptions options); + /// + /// Gets the name. + /// + /// The name. + string Name { get; } } } diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index b4a00f027..3d6cdd03d 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs @@ -206,6 +206,11 @@ namespace Emby.Drawing.ImageMagick } } + public string Name + { + get { return "ImageMagick"; } + } + private bool _disposed; public void Dispose() { diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index bb6f74f36..8cb814b97 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -433,7 +433,7 @@ namespace MediaBrowser.Api.LiveTv var result = await _liveTvManager.GetPrograms(query, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task Get(GetRecommendedPrograms request) @@ -450,7 +450,7 @@ namespace MediaBrowser.Api.LiveTv var result = await _liveTvManager.GetRecommendedPrograms(query, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public object Post(GetPrograms request) @@ -473,7 +473,7 @@ namespace MediaBrowser.Api.LiveTv }, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task Get(GetRecording request) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 302ad7f29..62bee1f9b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1627,7 +1627,7 @@ namespace MediaBrowser.Api.Playback MediaSourceInfo mediaSource; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { - var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false)).ToList(); + var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index abd0278c2..6e5af261c 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Devices; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; @@ -59,23 +61,27 @@ namespace MediaBrowser.Api.Playback private readonly IMediaSourceManager _mediaSourceManager; private readonly IDeviceManager _deviceManager; private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _config; + private readonly INetworkManager _networkManager; - public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager) + public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; _libraryManager = libraryManager; + _config = config; + _networkManager = networkManager; } public async Task Get(GetPlaybackInfo request) { - var result = await GetPlaybackInfo(request.Id, request.UserId).ConfigureAwait(false); + var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false); return ToOptimizedResult(result); } public async Task Get(GetLiveMediaInfo request) { - var result = await GetPlaybackInfo(request.Id, request.UserId).ConfigureAwait(false); + var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -122,29 +128,38 @@ namespace MediaBrowser.Api.Playback public async Task Post(GetPostedPlaybackInfo request) { - var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false); var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); var profile = request.DeviceProfile; - if (profile == null) + + var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (caps != null) { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); - if (caps != null) + if (profile == null) { profile = caps.DeviceProfile; } } + var maxBitrate = request.MaxStreamingBitrate; + + if (_config.Configuration.RemoteClientBitrateLimit > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp)) + { + maxBitrate = Math.Min(maxBitrate ?? _config.Configuration.RemoteClientBitrateLimit, _config.Configuration.RemoteClientBitrateLimit); + } + + var info = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false); + if (profile != null) { var mediaSourceId = request.MediaSourceId; - SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); + SetDeviceSpecificData(request.Id, info, profile, authInfo, maxBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); } return ToOptimizedResult(info); } - private async Task GetPlaybackInfo(string id, string userId, string mediaSourceId = null, string liveStreamId = null) + private async Task GetPlaybackInfo(string id, string userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var result = new PlaybackInfoResponse(); @@ -153,7 +168,7 @@ namespace MediaBrowser.Api.Playback IEnumerable mediaSources; try { - mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false); + mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, supportedLiveMediaTypes, CancellationToken.None).ConfigureAwait(false); } catch (PlaybackException ex) { diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 73589d677..a70118d3c 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -136,11 +136,11 @@ namespace MediaBrowser.Api.Subtitles _providerManager = providerManager; } - public object Get(GetSubtitlePlaylist request) + public async Task Get(GetSubtitlePlaylist request) { var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); - var mediaSource = _mediaSourceManager.GetStaticMediaSource(item, request.MediaSourceId, false); + var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, false).ConfigureAwait(false); var builder = new StringBuilder(); diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index bc1b0e785..70ed5c319 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -101,12 +101,6 @@ namespace MediaBrowser.Common.Implementations /// The failed assemblies. public List FailedAssemblies { get; protected set; } - /// - /// Gets all types within all running assemblies - /// - /// All types. - public Type[] AllTypes { get; protected set; } - /// /// Gets all concrete types. /// @@ -438,9 +432,10 @@ namespace MediaBrowser.Common.Implementations Logger.Info("Loading {0}", assembly.FullName); } - AllTypes = assemblies.SelectMany(GetTypes).ToArray(); - - AllConcreteTypes = AllTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray(); + AllConcreteTypes = assemblies + .SelectMany(GetTypes) + .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType) + .ToArray(); } /// diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index 1762ed575..0fd4e2787 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -172,11 +172,11 @@ namespace MediaBrowser.Common.Implementations.Networking Uri uri; if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri)) { - var host = uri.DnsSafeHost; - Logger.Debug("Resolving host {0}", host); - try { + var host = uri.DnsSafeHost; + Logger.Debug("Resolving host {0}", host); + address = GetIpAddresses(host).FirstOrDefault(); if (address != null) @@ -186,9 +186,13 @@ namespace MediaBrowser.Common.Implementations.Networking return IsInLocalNetworkInternal(address.ToString(), false); } } + catch (InvalidOperationException) + { + // Can happen with reverse proxy or IIS url rewriting + } catch (Exception ex) { - Logger.ErrorException("Error resovling hostname {0}", ex, host); + Logger.ErrorException("Error resovling hostname", ex); } } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 5bcc5f313..a77d88049 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -43,18 +43,10 @@ namespace MediaBrowser.Controller.Library /// The identifier. /// The user identifier. /// if set to true [enable path substitution]. + /// The supported live media types. /// The cancellation token. /// IEnumerable<MediaSourceInfo>. - Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, CancellationToken cancellationToken); - - /// - /// Gets the playack media sources. - /// - /// The identifier. - /// if set to true [enable path substitution]. - /// The cancellation token. - /// Task<IEnumerable<MediaSourceInfo>>. - Task> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken); + Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken); /// /// Gets the static media sources. @@ -64,7 +56,7 @@ namespace MediaBrowser.Controller.Library /// The user. /// IEnumerable<MediaSourceInfo>. IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null); - + /// /// Gets the static media source. /// @@ -72,7 +64,7 @@ namespace MediaBrowser.Controller.Library /// The media source identifier. /// if set to true [enable path substitution]. /// MediaSourceInfo. - MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); + Task GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); /// /// Opens the media source. diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs index e5a51a56e..a43941607 100644 --- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs +++ b/MediaBrowser.Controller/Providers/IImageEnhancer.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; using System.Threading.Tasks; diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index d88adc8c6..9df69b115 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -892,7 +892,7 @@ namespace MediaBrowser.Dlna.PlayTo request.MediaSource = hasMediaSources == null ? null : - mediaSourceManager.GetStaticMediaSource(hasMediaSources, request.MediaSourceId, false); + mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, false).Result; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 8d8201074..a0f5d928d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.MediaEncoding.Encoder state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 14d3e7284..60b70ad08 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -132,7 +132,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles int subtitleStreamIndex, CancellationToken cancellationToken) { - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, false, cancellationToken).ConfigureAwait(false); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId)); diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index bc167333a..ac9bd6b08 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -208,6 +208,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableAudioArchiveFiles { get; set; } public bool EnableVideoArchiveFiles { get; set; } + public int RemoteClientBitrateLimit { get; set; } /// /// Initializes a new instance of the class. diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index 9361a60ea..25438a811 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -19,12 +19,14 @@ namespace MediaBrowser.Model.Session public bool SupportsOfflineAccess { get; set; } public DeviceProfile DeviceProfile { get; set; } + public List SupportedLiveMediaTypes { get; set; } public ClientCapabilities() { PlayableMediaTypes = new List(); SupportedCommands = new List(); SupportsPersistentIdentifier = true; + SupportedLiveMediaTypes = new List(); } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index b3c599496..d7c894587 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -39,8 +39,9 @@ namespace MediaBrowser.Model.Users public bool EnableLiveTvAccess { get; set; } public bool EnableMediaPlayback { get; set; } - public bool EnableMediaPlaybackTranscoding { get; set; } - + public bool EnableAudioPlaybackTranscoding { get; set; } + public bool EnableVideoPlaybackTranscoding { get; set; } + public bool EnableContentDeletion { get; set; } public bool EnableContentDownloading { get; set; } @@ -68,8 +69,9 @@ namespace MediaBrowser.Model.Users EnableSyncTranscoding = true; EnableMediaPlayback = true; - EnableMediaPlaybackTranscoding = true; - + EnableAudioPlaybackTranscoding = true; + EnableVideoPlaybackTranscoding = true; + EnableLiveTvManagement = true; EnableLiveTvAccess = true; diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 01efe0ab1..71fd4127b 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -129,12 +129,7 @@ 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) + public async Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken) { var item = _libraryManager.GetItemById(id); @@ -184,9 +179,19 @@ namespace MediaBrowser.Server.Implementations.Library { if (user != null) { - if (!user.Policy.EnableMediaPlaybackTranscoding) + if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { - source.SupportsTranscoding = false; + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + source.SupportsTranscoding = false; + } + } + else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + if (!user.Policy.EnableVideoPlaybackTranscoding) + { + source.SupportsTranscoding = false; + } } } } @@ -238,9 +243,12 @@ namespace MediaBrowser.Server.Implementations.Library } } - public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) + public async Task GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) { - return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + var sources = await GetPlayackMediaSources(item.Id.ToString("N"), null, enablePathSubstitution, new[] { MediaType.Audio, MediaType.Video }, + CancellationToken.None).ConfigureAwait(false); + + return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } public IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null) diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index f782a1f68..4c6689bc6 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -122,7 +122,7 @@ "LabelFree": "Free", "HeaderPlaybackError": "Playback Error", "MessagePlaybackErrorNotAllowed": "You're currently not authorized to play this content. Please contact your system administrator for details.", - "MessagePlaybackErrorNoCompatibleStream": "No compatible streams are currently available. Please try again later.", + "MessagePlaybackErrorNoCompatibleStream": "No compatible streams are currently available. Please try again later or contact your system administrator for details.", "MessagePlaybackErrorRateLimitExceeded": "Your playback rate limit has been exceeded. Please contact your system administrator for details.", "MessagePlaybackErrorPlaceHolder": "The content chosen is not playable from this device.", "HeaderSelectAudio": "Select Audio", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index bf03498db..b651b72da 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1410,6 +1410,10 @@ "LabelUploadSpeedLimit": "Upload speed limit (mbps):", "OptionAllowSyncTranscoding": "Allow syncing that requires transcoding", "HeaderPlayback": "Media Playback", - "OptionAllowMediaPlaybackTranscoding": "Allow media playback that requires transcoding", - "OptionAllowMediaPlaybackTranscodingHelp": "Users will receive friendly messages when content is unplayable based on policy." + "OptionAllowAudioPlaybackTranscoding": "Allow audio playback that requires transcoding", + "OptionAllowVideoPlaybackTranscoding": "Allow video playback that requires transcoding", + "OptionAllowMediaPlaybackTranscodingHelp": "Users will receive friendly messages when content is unplayable based on policy.", + "TabStreaming": "Streaming", + "LabelRemoteClientBitrateLimit": "Remote client bitrate limit (mbps):", + "LabelRemoteClientBitrateLimitHelp": "An optional streaming bitrate limit for all remote clients. This is useful to prevent clients from requesting a higher bitrate than your connection can handle." } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 7f5033b98..112778ec8 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -305,12 +305,9 @@ namespace MediaBrowser.Server.Implementations.Session } } - private async Task GetMediaSource(IHasMediaSources item, string mediaSourceId) + private Task GetMediaSource(IHasMediaSources item, string mediaSourceId) { - var sources = await _mediaSourceManager.GetPlayackMediaSources(item.Id.ToString("N"), false, CancellationToken.None) - .ConfigureAwait(false); - - return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + return _mediaSourceManager.GetMediaSource(item, mediaSourceId, false); } /// diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 73400f834..f881a2055 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -195,17 +195,39 @@ namespace MediaBrowser.Server.Implementations.Sync } }; - var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2"; codecProfiles.Add(new CodecProfile { Type = CodecType.VideoAudio, + Codec = "ac3", Conditions = new[] { new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, - Value = maxAudioChannels, + Value = "5", + IsRequired = true + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsSecondaryAudio, + Value = "false", + IsRequired = false + } + } + }); + codecProfiles.Add(new CodecProfile + { + Type = CodecType.VideoAudio, + Codec = "ac3", + Conditions = new[] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.AudioChannels, + Value = "2", IsRequired = true }, new ProfileCondition diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 5138b157f..90af13b57 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -482,6 +482,7 @@ namespace MediaBrowser.WebDashboard.Api "selectserver.js", "serversecurity.js", "songs.js", + "streamingsettings.js", "supporterkeypage.js", "supporterpage.js", "syncactivity.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 4ad2ab685..7abe2ded9 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -150,6 +150,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -174,6 +177,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest -- cgit v1.2.3 From 3cb2043028875667ee1074266cc9b30eb05a9b09 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 9 Apr 2015 13:30:18 -0400 Subject: added sync cpu settings --- MediaBrowser.Api/ApiEntryPoint.cs | 6 ++++++ .../MediaEncoding/EncodingJobOptions.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs | 19 ++----------------- .../Encoder/EncodingJobFactory.cs | 7 +++++-- MediaBrowser.Model/Sync/SyncOptions.cs | 7 +++++++ MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs | 1 + .../Localization/Server/server.json | 6 +++++- .../Sync/SyncJobProcessor.cs | 16 ++++++++++------ 8 files changed, 38 insertions(+), 26 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index ed5fa5bfd..ef5191df1 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -334,6 +334,12 @@ namespace MediaBrowser.Api 1000 : 1800000; + // We can really reduce the timeout for apps that are using the newer api + if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type == TranscodingJobType.Hls) + { + timerDuration = 40000; + } + if (job.KillTimer == null) { if (startTimerIfNeeded) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index fe0fb3295..bb8841222 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -41,6 +41,8 @@ namespace MediaBrowser.Controller.MediaEncoding public int? SubtitleStreamIndex { get; set; } public int? MaxRefFrames { get; set; } public int? MaxVideoBitDepth { get; set; } + public int? CpuCoreLimit { get; set; } + public bool ReadInputAtNativeFramerate { get; set; } public SubtitleDeliveryMethod SubtitleMethod { get; set; } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 78ac92f25..c30ceb62d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -70,10 +70,7 @@ namespace MediaBrowser.MediaEncoding.Encoder encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); Directory.CreateDirectory(Path.GetDirectoryName(encodingJob.OutputFilePath)); - if (options.Context == EncodingContext.Static && encodingJob.IsInputVideo) - { - encodingJob.ReadInputAtNativeFramerate = true; - } + encodingJob.ReadInputAtNativeFramerate = options.ReadInputAtNativeFramerate; await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false); @@ -305,19 +302,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// System.Int32. protected int GetNumberOfThreads(EncodingJob job, bool isWebm) { - // Only need one thread for sync - if (job.Options.Context == EncodingContext.Static) - { - return 1; - } - - if (isWebm) - { - // Recommended per docs - return Math.Max(Environment.ProcessorCount - 1, 2); - } - - return 0; + return job.Options.CpuCoreLimit ?? 0; } protected EncodingQuality GetQualitySetting() diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index a0f5d928d..ea3cc6d89 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -124,10 +124,14 @@ namespace MediaBrowser.MediaEncoding.Encoder 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.ReadAtNativeFramerate) + { + state.ReadInputAtNativeFramerate = true; + } + if (mediaSource.VideoType.HasValue) { state.VideoType = mediaSource.VideoType.Value; @@ -148,7 +152,6 @@ namespace MediaBrowser.MediaEncoding.Encoder 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)) diff --git a/MediaBrowser.Model/Sync/SyncOptions.cs b/MediaBrowser.Model/Sync/SyncOptions.cs index 765dea86b..7f0c17b37 100644 --- a/MediaBrowser.Model/Sync/SyncOptions.cs +++ b/MediaBrowser.Model/Sync/SyncOptions.cs @@ -5,5 +5,12 @@ namespace MediaBrowser.Model.Sync { public string TemporaryPath { get; set; } public long UploadSpeedLimitBytes { get; set; } + public int TranscodingCpuCoreLimit { get; set; } + public bool EnableFullSpeedTranscoding { get; set; } + + public SyncOptions() + { + TranscodingCpuCoreLimit = 1; + } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 3e816802e..d78f1b48e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -147,6 +147,7 @@ namespace MediaBrowser.Providers.MediaInfo audio.Artists = data.Artists; audio.AlbumArtists = data.AlbumArtists; audio.IndexNumber = data.IndexNumber; + audio.ParentIndexNumber = data.ParentIndexNumber; audio.ProductionYear = data.ProductionYear; audio.PremiereDate = data.PremiereDate; diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index b651b72da..ea64b2780 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1415,5 +1415,9 @@ "OptionAllowMediaPlaybackTranscodingHelp": "Users will receive friendly messages when content is unplayable based on policy.", "TabStreaming": "Streaming", "LabelRemoteClientBitrateLimit": "Remote client bitrate limit (mbps):", - "LabelRemoteClientBitrateLimitHelp": "An optional streaming bitrate limit for all remote clients. This is useful to prevent clients from requesting a higher bitrate than your connection can handle." + "LabelRemoteClientBitrateLimitHelp": "An optional streaming bitrate limit for all remote clients. This is useful to prevent clients from requesting a higher bitrate than your connection can handle.", + "LabelConversionCpuCoreLimit": "CPU core limit:", + "LabelConversionCpuCoreLimitHelp": "Limit the number of CPU cores that will be used during sync conversion.", + "OptionEnableFullSpeedConversion": "Enable full speed conversion", + "OptionEnableFullSpeedConversionHelp": "By default sync conversion is performed at a low speed to reduce resource consumption." } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index d8fc425d1..fd4092974 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -456,17 +456,18 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.Progress = 0; + var syncOptions = _config.GetSyncOptions(); var user = _userManager.GetUserById(job.UserId); var video = item as Video; if (video != null) { - await Sync(jobItem, job, video, user, enableConversion, progress, cancellationToken).ConfigureAwait(false); + await Sync(jobItem, job, video, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false); } else if (item is Audio) { - await Sync(jobItem, job, (Audio)item, user, enableConversion, progress, cancellationToken).ConfigureAwait(false); + await Sync(jobItem, job, (Audio)item, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false); } else if (item is Photo) @@ -480,7 +481,7 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private async Task Sync(SyncJobItem jobItem, SyncJob job, Video item, User user, bool enableConversion, IProgress progress, CancellationToken cancellationToken) + private async Task Sync(SyncJobItem jobItem, SyncJob job, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress progress, CancellationToken cancellationToken) { var jobOptions = _syncManager.GetVideoOptions(jobItem, job); var conversionOptions = new VideoOptions @@ -542,7 +543,9 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, conversionOptions.Profile) { - OutputDirectory = jobItem.TemporaryPath + OutputDirectory = jobItem.TemporaryPath, + CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit, + ReadInputAtNativeFramerate = !syncOptions.EnableFullSpeedTranscoding }, innerProgress, cancellationToken); } @@ -677,7 +680,7 @@ namespace MediaBrowser.Server.Implementations.Sync private const int DatabaseProgressUpdateIntervalSeconds = 2; - private async Task Sync(SyncJobItem jobItem, SyncJob job, Audio item, User user, bool enableConversion, IProgress progress, CancellationToken cancellationToken) + private async Task Sync(SyncJobItem jobItem, SyncJob job, Audio item, User user, bool enableConversion, SyncOptions syncOptions, IProgress progress, CancellationToken cancellationToken) { var jobOptions = _syncManager.GetAudioOptions(jobItem, job); var conversionOptions = new AudioOptions @@ -725,7 +728,8 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, conversionOptions.Profile) { - OutputDirectory = jobItem.TemporaryPath + OutputDirectory = jobItem.TemporaryPath, + CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit }, innerProgress, cancellationToken); } -- cgit v1.2.3 From 4f7e8fee24ead08d114f41e9fd9c1c7b4dd55359 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Apr 2015 10:01:16 -0400 Subject: improve ffmpeg cleanup --- MediaBrowser.Controller/Entities/BaseItem.cs | 8 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 133 +++++++++++++-------- MediaBrowser.Model/Dto/BaseItemDto.cs | 1 + MediaBrowser.Model/Users/UserPolicy.cs | 3 + .../Connect/ConnectManager.cs | 2 +- .../Library/LibraryManager.cs | 2 +- .../Localization/JavaScript/javascript.json | 1 + .../Localization/Server/server.json | 8 +- README.md | 2 +- 9 files changed, 96 insertions(+), 64 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 27bd09ec7..f8384ea1c 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1161,12 +1161,10 @@ namespace MediaBrowser.Controller.Entities return true; } - var locations = user.RootFolder - .GetChildren(user, true) - .OfType() - .SelectMany(i => i.PhysicalLocations); + var folders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList(); + var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id).ToList(); - return locations.Any(l => FileSystem.ContainsSubPath(l, topParent.Path)); + return itemCollectionFolders.Any(folders.Contains); } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index be636c0ba..7bcf60fd9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -74,6 +75,8 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly Func SubtitleEncoder; protected readonly Func MediaSourceManager; + private List _runningProcesses = new List(); + public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func subtitleEncoder, Func mediaSourceManager) { _logger = logger; @@ -192,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Encoder try { - process.Start(); + StartProcess(process); } catch (Exception ex) { @@ -375,7 +378,7 @@ namespace MediaBrowser.MediaEncoding.Encoder await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - process.Start(); + StartProcess(process); var memoryStream = new MemoryStream(); @@ -391,18 +394,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!ranToCompletion) { - try - { - _logger.Info("Killing ffmpeg process"); - - process.StandardInput.WriteLine("q"); - - process.WaitForExit(1000); - } - catch (Exception ex) - { - _logger.ErrorException("Error killing process", ex); - } + StopProcess(process, 1000, false); } resourcePool.Release(); @@ -426,31 +418,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return memoryStream; } - public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - } - - /// - /// 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) - { - _videoImageResourcePool.Dispose(); - } - } - public string GetTimeParameter(long ticks) { var time = TimeSpan.FromTicks(ticks); @@ -519,7 +486,7 @@ namespace MediaBrowser.MediaEncoding.Encoder try { - process.Start(); + StartProcess(process); // Need to give ffmpeg enough time to make all the thumbnails, which could be a while, // but we still need to detect if the process hangs. @@ -543,18 +510,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!ranToCompletion) { - try - { - _logger.Info("Killing ffmpeg process"); - - process.StandardInput.WriteLine("q"); - - process.WaitForExit(1000); - } - catch (Exception ex) - { - _logger.ErrorException("Error killing process", ex); - } + StopProcess(process, 1000, false); } } finally @@ -615,5 +571,78 @@ namespace MediaBrowser.MediaEncoding.Encoder return job.OutputFilePath; } + + private void StartProcess(Process process) + { + process.Start(); + + lock (_runningProcesses) + { + _runningProcesses.Add(process); + } + } + private void StopProcess(Process process, int waitTimeMs, bool enableForceKill) + { + try + { + _logger.Info("Killing ffmpeg process"); + + process.StandardInput.WriteLine("q"); + + if (!process.WaitForExit(1000)) + { + if (enableForceKill) + { + process.Kill(); + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error killing process", ex); + } + finally + { + lock (_runningProcesses) + { + _runningProcesses.Remove(process); + } + } + } + + private void StopProcesses() + { + List proceses; + lock (_runningProcesses) + { + proceses = _runningProcesses.ToList(); + } + + foreach (var process in proceses) + { + StopProcess(process, 500, true); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// 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) + { + _videoImageResourcePool.Dispose(); + StopProcesses(); + } + } } } diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index c49a1f77e..d984fe89e 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -63,6 +63,7 @@ namespace MediaBrowser.Model.Dto public string PreferredMetadataCountryCode { get; set; } public string AwardSummary { get; set; } + public string ShareUrl { get; set; } public float? Metascore { get; set; } diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index d7c894587..05c1b9de5 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -62,6 +62,8 @@ namespace MediaBrowser.Model.Users public bool EnableAllFolders { get; set; } public int InvalidLoginAttemptCount { get; set; } + + public bool EnablePublicSharing { get; set; } public UserPolicy() { @@ -94,6 +96,7 @@ namespace MediaBrowser.Model.Users EnableAllDevices = true; EnableContentDownloading = true; + EnablePublicSharing = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index b1b6cd899..2d39f760e 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -517,7 +517,7 @@ namespace MediaBrowser.Server.Implementations.Connect if (!connectUser.IsActive) { - throw new ArgumentException("The Emby account has been disabled."); + throw new ArgumentException("The Emby account is not active. Please ensure the account has been activated by following the instructions within the email confirmation."); } connectUserId = connectUser.Id; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 2e28f1a94..02c16d0d4 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1513,7 +1513,7 @@ namespace MediaBrowser.Server.Implementations.Library return GetUserRootFolder().Children .OfType() - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path)); + .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)); } public string GetContentType(BaseItem item) diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 4c6689bc6..49ca737fe 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -40,6 +40,7 @@ "TitleLiveTV": "Live TV", "TitleSync": "Sync", "ButtonDonate": "Donate", + "HeaderMyMedia": "My Media", "TitleNotifications": "Notifications", "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.", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index cb65b3a4d..a686cface 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -552,7 +552,7 @@ "LabelPublicHttpsPort": "Public https port number:", "LabelPublicHttpsPortHelp": "The public port number that should be mapped to the local https port.", "LabelEnableHttps": "Report https as external address", - "LabelEnableHttpsHelp": "If enabled, the server will report an https url to clients as it's external address. This may break clients that do not yet support https.", + "LabelEnableHttpsHelp": "If enabled, the server will report an https url to clients as it's external address.", "LabelHttpsPort": "Local https port number:", "LabelHttpsPortHelp": "The tcp port number that Emby's https server should bind to.", "LabelWebSocketPortNumber": "Web socket port number:", @@ -896,9 +896,9 @@ "LabelHomePageSection2": "Home page section 2:", "LabelHomePageSection3": "Home page section 3:", "LabelHomePageSection4": "Home page section 4:", - "OptionMyViewsButtons": "My views (buttons)", - "OptionMyViews": "My views", - "OptionMyViewsSmall": "My views (small)", + "OptionMyMediaButtons": "My media (buttons)", + "OptionMyMedia": "My media", + "OptionMyMediaSmall": "My media (small)", "OptionResumablemedia": "Resume", "OptionLatestMedia": "Latest media", "OptionLatestChannelMedia": "Latest channel items", diff --git a/README.md b/README.md index f024d1e2a..d5cdcf06f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ We have several client apps released and in production: - [iPad](https://itunes.apple.com/us/app/media-browser-client/id879475585 "iPad") - [iPhone](https://itunes.apple.com/us/app/media-browser-for-ios/id705058087?mt=8 "iPhone") - [Media Portal](http://www.team-mediaportal.com/ "Media Portal") -- [Roku](http://www.roku.com/channels/#!details/44191/media-browser-for-roku "Roku") +- [Roku](https://www.roku.com/channels#!details/44191/emby "Roku") - Windows 7/8 Desktop - Windows Media Center - [Windows Phone](http://www.windowsphone.com/s?appid=f4971ed9-f651-4bf6-84bb-94fd98613b86 "Windows Phone") -- cgit v1.2.3 From 2a681f205aba211d9eaec7866ea2f39f469fba90 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Apr 2015 15:08:09 -0400 Subject: capture key frame info --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 96 +++++++++ .../Playback/Progressive/VideoService.cs | 1 - .../MediaEncoding/MediaInfoRequest.cs | 1 + MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 224 +++++++++++++++++---- MediaBrowser.Model/Entities/MediaStream.cs | 9 +- .../MediaInfo/FFProbeVideoInfo.cs | 7 +- .../Persistence/SqliteMediaStreamsRepository.cs | 55 ++++- 7 files changed, 340 insertions(+), 53 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 62bee1f9b..728fea0e0 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1705,6 +1705,102 @@ namespace MediaBrowser.Api.Playback { state.OutputAudioCodec = "copy"; } + + if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + { + var segmentLength = GetSegmentLength(state); + if (segmentLength.HasValue) + { + state.SegmentLength = segmentLength.Value; + } + } + } + + private int? GetSegmentLength(StreamState state) + { + var stream = state.VideoStream; + + if (stream == null) + { + return null; + } + + var frames = stream.KeyFrames; + + if (frames == null || frames.Count < 2) + { + return null; + } + + Logger.Debug("Found keyframes at {0}", string.Join(",", frames.ToArray())); + + var intervals = new List(); + for (var i = 1; i < frames.Count; i++) + { + var start = frames[i - 1]; + var end = frames[i]; + intervals.Add(end - start); + } + + Logger.Debug("Found keyframes intervals {0}", string.Join(",", intervals.ToArray())); + + var results = new List>(); + + for (var i = 1; i <= 10; i++) + { + var idealMs = i*1000; + + if (intervals.Max() < idealMs - 1000) + { + break; + } + + var segments = PredictStreamCopySegments(intervals, idealMs); + var variance = segments.Select(s => Math.Abs(idealMs - s)).Sum(); + + results.Add(new Tuple(i, variance)); + } + + if (results.Count == 0) + { + return null; + } + + return results.OrderBy(i => i.Item2).ThenBy(i => i.Item1).Select(i => i.Item1).First(); + } + + private List PredictStreamCopySegments(List intervals, int idealMs) + { + var segments = new List(); + var currentLength = 0; + + foreach (var interval in intervals) + { + if (currentLength == 0 || (currentLength + interval) <= idealMs) + { + currentLength += interval; + } + + else + { + // The segment will either be above or below the ideal. + // Need to figure out which is preferable + var offset1 = Math.Abs(idealMs - currentLength); + var offset2 = Math.Abs(idealMs - (currentLength + interval)); + + if (offset1 <= offset2) + { + segments.Add(currentLength); + currentLength = interval; + } + else + { + currentLength += interval; + } + } + } + Logger.Debug("Predicted actual segment lengths for length {0}: {1}", idealMs, string.Join(",", segments.ToArray())); + return segments; } private void AttachMediaSourceInfo(StreamState state, diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 540c39a0c..0ded108b1 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.IO; using ServiceStack; diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs index ca0c2fdbb..24df7b885 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding public IIsoMount MountedIso { get; set; } public VideoType VideoType { get; set; } public List PlayableStreamFileNames { get; set; } + public bool ExtractKeyFrameInterval { get; set; } public MediaInfoRequest() { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7bcf60fd9..06e3016b9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -14,6 +13,7 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; @@ -75,7 +75,7 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly Func SubtitleEncoder; protected readonly Func MediaSourceManager; - private List _runningProcesses = new List(); + private readonly List _runningProcesses = new List(); public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func subtitleEncoder, Func mediaSourceManager) { @@ -116,7 +116,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); - return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, + var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile; + + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, extractKeyFrameInterval, GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, cancellationToken); } @@ -150,12 +152,17 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The primary path. /// The protocol. /// if set to true [extract chapters]. + /// if set to true [extract key frame interval]. /// The probe size argument. /// if set to true [is audio]. /// The cancellation token. /// Task{MediaInfoResult}. /// - private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, + private async Task GetMediaInfoInternal(string inputPath, + string primaryPath, + MediaProtocol protocol, + bool extractChapters, + bool extractKeyFrameInterval, string probeSizeArgument, bool isAudio, CancellationToken cancellationToken) @@ -174,6 +181,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Must consume both or ffmpeg may hang due to deadlocks. See comments below. RedirectStandardOutput = true, RedirectStandardError = true, + RedirectStandardInput = true, FileName = FFProbePath, Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(), @@ -187,12 +195,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); - process.Exited += ProcessExited; - await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - InternalMediaInfoResult result; - try { StartProcess(process); @@ -210,19 +214,55 @@ namespace MediaBrowser.MediaEncoding.Encoder { process.BeginErrorReadLine(); - result = _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); + var result = _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); + + if (result != null) + { + if (result.streams != null) + { + // Normalize aspect ratio if invalid + foreach (var stream in result.streams) + { + if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase)) + { + stream.display_aspect_ratio = string.Empty; + } + if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase)) + { + stream.sample_aspect_ratio = string.Empty; + } + } + } + + var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, isAudio, primaryPath, protocol); + + if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue) + { + foreach (var stream in mediaInfo.MediaStreams.Where(i => i.Type == MediaStreamType.Video) + .ToList()) + { + try + { + stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + { + + } + catch (Exception ex) + { + _logger.ErrorException("Error getting key frame interval", ex); + } + } + } + + return mediaInfo; + } } catch { - // Hate having to do this - try - { - process.Kill(); - } - catch (Exception ex1) - { - _logger.ErrorException("Error killing ffprobe", ex1); - } + StopProcess(process, 100, true); throw; } @@ -231,30 +271,108 @@ namespace MediaBrowser.MediaEncoding.Encoder _ffProbeResourcePool.Release(); } - if (result == null) + throw new ApplicationException(string.Format("FFProbe failed for {0}", inputPath)); + } + + private async Task> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken) + { + const string args = "-i {0} -select_streams v:{1} -show_frames -print_format compact"; + + var process = new Process { - throw new ApplicationException(string.Format("FFProbe failed for {0}", inputPath)); + StartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + + // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + FileName = FFProbePath, + Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(), + + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }, + + EnableRaisingEvents = true + }; + + _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + + StartProcess(process); + + var lines = new List(); + var outputCancellationSource = new CancellationTokenSource(4000); + + try + { + process.BeginErrorReadLine(); + + var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(outputCancellationSource.Token, cancellationToken); + + await StartReadingOutput(process.StandardOutput.BaseStream, lines, 120000, outputCancellationSource, linkedCancellationTokenSource.Token).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (cancellationToken.IsCancellationRequested) + { + throw; + } + } + finally + { + StopProcess(process, 100, true); } - cancellationToken.ThrowIfCancellationRequested(); + return lines; + } - if (result.streams != null) + private async Task StartReadingOutput(Stream source, List lines, int timeoutMs, CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken) + { + try { - // Normalize aspect ratio if invalid - foreach (var stream in result.streams) + using (var reader = new StreamReader(source)) { - if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase)) - { - stream.display_aspect_ratio = string.Empty; - } - if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase)) + while (!reader.EndOfStream) { - stream.sample_aspect_ratio = string.Empty; + cancellationToken.ThrowIfCancellationRequested(); + + var line = await reader.ReadLineAsync().ConfigureAwait(false); + + var values = (line ?? string.Empty).Split('|') + .Where(i => !string.IsNullOrWhiteSpace(i)) + .Select(i => i.Split('=')) + .Where(i => i.Length == 2) + .ToDictionary(i => i[0], i => i[1]); + + string pktDts; + int frameMs; + if (values.TryGetValue("pkt_dts", out pktDts) && int.TryParse(pktDts, NumberStyles.Any, CultureInfo.InvariantCulture, out frameMs)) + { + string keyFrame; + if (values.TryGetValue("key_frame", out keyFrame) && string.Equals(keyFrame, "1", StringComparison.OrdinalIgnoreCase)) + { + lines.Add(frameMs); + } + + if (frameMs > timeoutMs) + { + cancellationTokenSource.Cancel(); + } + } } } } - - return new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, isAudio, primaryPath, protocol); + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + _logger.ErrorException("Error reading ffprobe output", ex); + } } /// @@ -269,7 +387,14 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The instance containing the event data. private void ProcessExited(object sender, EventArgs e) { - ((Process)sender).Dispose(); + var process = (Process) sender; + + lock (_runningProcesses) + { + _runningProcesses.Remove(process); + } + + process.Dispose(); } public Task ExtractAudioImage(string path, CancellationToken cancellationToken) @@ -574,6 +699,8 @@ namespace MediaBrowser.MediaEncoding.Encoder private void StartProcess(Process process) { + process.Exited += ProcessExited; + process.Start(); lock (_runningProcesses) @@ -587,27 +714,36 @@ namespace MediaBrowser.MediaEncoding.Encoder { _logger.Info("Killing ffmpeg process"); - process.StandardInput.WriteLine("q"); + try + { + process.StandardInput.WriteLine("q"); + } + catch (Exception) + { + _logger.Error("Error sending q command to process"); + } - if (!process.WaitForExit(1000)) + try { - if (enableForceKill) + if (process.WaitForExit(waitTimeMs)) { - process.Kill(); + return; } } + catch (Exception ex) + { + _logger.Error("Error in WaitForExit", ex); + } + + if (enableForceKill) + { + process.Kill(); + } } catch (Exception ex) { _logger.ErrorException("Error killing process", ex); } - finally - { - lock (_runningProcesses) - { - _runningProcesses.Remove(process); - } - } } private void StopProcesses() diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 0f3435174..11eb31c27 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Dlna; +using System.Collections.Generic; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Extensions; using System.Diagnostics; @@ -58,6 +59,12 @@ namespace MediaBrowser.Model.Entities /// The length of the packet. public int? PacketLength { get; set; } + /// + /// Gets or sets the key frames. + /// + /// The key frames. + public List KeyFrames { get; set; } + /// /// Gets or sets the channels. /// diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index c433018c0..7950a8d5c 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -130,7 +130,7 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } - private const string SchemaVersion = "2"; + private const string SchemaVersion = "3"; private async Task GetMediaInfo(Video item, IIsoMount isoMount, @@ -145,7 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo try { - return _json.DeserializeFromFile(cachePath); + //return _json.DeserializeFromFile(cachePath); } catch (FileNotFoundException) { @@ -167,7 +167,8 @@ namespace MediaBrowser.Providers.MediaInfo VideoType = item.VideoType, MediaType = DlnaProfileType.Video, InputPath = item.Path, - Protocol = protocol + Protocol = protocol, + ExtractKeyFrameInterval = true }, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs index e9d7f44ec..76c1274b2 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Persistence; +using System.Globalization; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; @@ -40,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Persistence // Add PixelFormat column - createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, KeyFrames TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { @@ -61,6 +62,7 @@ namespace MediaBrowser.Server.Implementations.Persistence AddIsAnamorphicColumn(); AddIsCabacColumn(); AddRefFramesCommand(); + AddKeyFramesCommand(); PrepareStatements(); @@ -160,6 +162,37 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.RunQueries(new[] { builder.ToString() }, _logger); } + private void AddKeyFramesCommand() + { + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "PRAGMA table_info(mediastreams)"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + if (!reader.IsDBNull(1)) + { + var name = reader.GetString(1); + + if (string.Equals(name, "KeyFrames", StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + } + } + } + + var builder = new StringBuilder(); + + builder.AppendLine("alter table mediastreams"); + builder.AppendLine("add column KeyFrames TEXT NULL"); + + _connection.RunQueries(new[] { builder.ToString() }, _logger); + } + private void AddIsCabacColumn() { using (var cmd = _connection.CreateCommand()) @@ -249,6 +282,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "BitDepth", "IsAnamorphic", "RefFrames", + "KeyFrames", "IsCabac" }; @@ -430,7 +464,12 @@ namespace MediaBrowser.Server.Implementations.Persistence if (!reader.IsDBNull(25)) { - item.IsCabac = reader.GetBoolean(25); + item.KeyFrames = reader.GetString(25).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => int.Parse(i, CultureInfo.InvariantCulture)).ToList(); + } + + if (!reader.IsDBNull(26)) + { + item.IsCabac = reader.GetBoolean(26); } return item; @@ -498,7 +537,15 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(22).Value = stream.BitDepth; _saveStreamCommand.GetParameter(23).Value = stream.IsAnamorphic; _saveStreamCommand.GetParameter(24).Value = stream.RefFrames; - _saveStreamCommand.GetParameter(25).Value = stream.IsCabac; + if (stream.KeyFrames != null) + { + _saveStreamCommand.GetParameter(25).Value = string.Join(",", stream.KeyFrames.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()); + } + else + { + _saveStreamCommand.GetParameter(25).Value = null; + } + _saveStreamCommand.GetParameter(26).Value = stream.IsCabac; _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery(); -- cgit v1.2.3 From 3a00f003f52f94ecc74634a6ad395fb38d019a88 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Apr 2015 15:10:00 -0400 Subject: extract key frame info --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 06e3016b9..7c4160e1b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -238,21 +238,23 @@ namespace MediaBrowser.MediaEncoding.Encoder if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue) { - foreach (var stream in mediaInfo.MediaStreams.Where(i => i.Type == MediaStreamType.Video) - .ToList()) + foreach (var stream in mediaInfo.MediaStreams) { - try + if (stream.Type == MediaStreamType.Video && string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase)) { - stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken) - .ConfigureAwait(false); - } - catch (OperationCanceledException) - { - - } - catch (Exception ex) - { - _logger.ErrorException("Error getting key frame interval", ex); + try + { + stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + { + + } + catch (Exception ex) + { + _logger.ErrorException("Error getting key frame interval", ex); + } } } } -- cgit v1.2.3 From 17f5ae811817b0d891b2884d7a4adefc18bfaf3f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Apr 2015 18:16:41 -0400 Subject: improve ffmpeg killing --- MediaBrowser.Api/ApiEntryPoint.cs | 45 ++++++++++------------ MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 9 ++++- MediaBrowser.Api/Playback/TranscodingThrottler.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 +- .../MediaInfo/FFProbeVideoInfo.cs | 4 +- 6 files changed, 35 insertions(+), 31 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index ef5191df1..fd7ad2cf5 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -284,16 +284,19 @@ namespace MediaBrowser.Api { job.ActiveRequestCount++; - job.DisposeKillTimer(); + if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive) + { + job.DisposeKillTimer(); + } } public void OnTranscodeEndRequest(TranscodingJob job) { job.ActiveRequestCount--; - - if (job.ActiveRequestCount == 0) + Logger.Debug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount); + if (job.ActiveRequestCount <= 0) { - PingTimer(job, true); + PingTimer(job, false); } } internal void PingTranscodingJob(string deviceId, string playSessionId) @@ -323,11 +326,11 @@ namespace MediaBrowser.Api foreach (var job in jobs) { - PingTimer(job, false); + PingTimer(job, true); } } - private void PingTimer(TranscodingJob job, bool startTimerIfNeeded) + private void PingTimer(TranscodingJob job, bool isProgressCheckIn) { // TODO: Lower this hls timeout var timerDuration = job.Type == TranscodingJobType.Progressive ? @@ -335,20 +338,23 @@ namespace MediaBrowser.Api 1800000; // We can really reduce the timeout for apps that are using the newer api - if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type == TranscodingJobType.Hls) + if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive) { - timerDuration = 40000; + timerDuration = 35000; } if (job.KillTimer == null) { - if (startTimerIfNeeded) + // Don't start the timer for playback checkins with progressive streaming + if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn) { + Logger.Debug("Starting kill timer at {0}ms", timerDuration); job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite); } } else { + Logger.Debug("Changing kill timer to {0}ms", timerDuration); job.KillTimer.Change(timerDuration, Timeout.Infinite); } } @@ -439,28 +445,19 @@ namespace MediaBrowser.Api lock (job.ProcessLock) { - var process = job.Process; - - var hasExited = true; - - try - { - hasExited = process.HasExited; - } - catch (Exception ex) + if (job.TranscodingThrottler != null) { - Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path); + job.TranscodingThrottler.Stop(); } + var process = job.Process; + + var hasExited = job.HasExited; + if (!hasExited) { try { - if (job.TranscodingThrottler != null) - { - job.TranscodingThrottler.Stop(); - } - Logger.Info("Killing ffmpeg process for {0}", job.Path); //process.Kill(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 728fea0e0..24ee17943 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1706,7 +1706,7 @@ namespace MediaBrowser.Api.Playback state.OutputAudioCodec = "copy"; } - if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && TranscodingJobType == TranscodingJobType.Hls) { var segmentLength = GetSegmentLength(state); if (segmentLength.HasValue) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 98d6c9a76..455113da9 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -144,7 +144,6 @@ namespace MediaBrowser.Api.Playback.Hls request.StartTimeTicks = GetSeekPositionTicks(state, requestedIndex); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false); - ApiEntryPoint.Instance.OnTranscodeBeginRequest(job); } catch { @@ -154,6 +153,14 @@ namespace MediaBrowser.Api.Playback.Hls await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); } + else + { + job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + if (job.TranscodingThrottler != null) + { + job.TranscodingThrottler.UnpauseTranscoding(); + } + } } } finally diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index 58cfa086e..ece455009 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -70,7 +70,7 @@ namespace MediaBrowser.Api.Playback } } - private void UnpauseTranscoding() + public void UnpauseTranscoding() { if (_isPaused) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7c4160e1b..c309739d0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -244,8 +244,8 @@ namespace MediaBrowser.MediaEncoding.Encoder { try { - stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken) - .ConfigureAwait(false); + //stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken) + // .ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 7950a8d5c..f4d8ddb1a 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -130,7 +130,7 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } - private const string SchemaVersion = "3"; + private const string SchemaVersion = "4"; private async Task GetMediaInfo(Video item, IIsoMount isoMount, @@ -145,7 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo try { - //return _json.DeserializeFromFile(cachePath); + return _json.DeserializeFromFile(cachePath); } catch (FileNotFoundException) { -- cgit v1.2.3 From a639d32454ed95c476c649473e3b43678b65e44f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 11 Apr 2015 13:59:55 -0400 Subject: fix chapter image extraction --- MediaBrowser.Controller/Entities/BaseItem.cs | 5 ++ MediaBrowser.Dlna/Ssdp/SsdpHandler.cs | 12 ++- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 98 ++++++++++++++-------- .../EntryPoints/LibraryChangedNotifier.cs | 19 ++++- .../MediaEncoder/EncodingManager.cs | 3 +- 5 files changed, 92 insertions(+), 45 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index ea8510b53..dbc0fc282 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1651,6 +1651,11 @@ namespace MediaBrowser.Controller.Entities return AddImages(imageType, images.Cast().ToList()); } + public bool AddImages(ImageType imageType, IEnumerable images) + { + return AddImages(imageType, images.ToList()); + } + /// /// Adds the images. /// diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 8ca16832d..73bc4984c 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -62,16 +62,22 @@ namespace MediaBrowser.Dlna.Ssdp { if (string.Equals(args.Method, "M-SEARCH", StringComparison.OrdinalIgnoreCase)) { - TimeSpan delay = GetSearchDelay(args.Headers); + var headers = args.Headers; + + TimeSpan delay = GetSearchDelay(headers); if (_config.GetDlnaConfiguration().EnableDebugLogging) { _logger.Debug("Delaying search response by {0} seconds", delay.TotalSeconds); } - await Task.Delay(delay).ConfigureAwait(false); + await Task.Delay(delay).ConfigureAwait(false); - RespondToSearch(args.EndPoint, args.Headers["st"]); + string st; + if (headers.TryGetValue("st", out st)) + { + RespondToSearch(args.EndPoint, st); + } } EventHelper.FireEventIfNotNull(MessageReceived, this, args, _logger); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c309739d0..0bf007572 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -75,7 +75,7 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly Func SubtitleEncoder; protected readonly Func MediaSourceManager; - private readonly List _runningProcesses = new List(); + private readonly List _runningProcesses = new List(); public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func subtitleEncoder, Func mediaSourceManager) { @@ -197,9 +197,11 @@ namespace MediaBrowser.MediaEncoding.Encoder await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + var processWrapper = new ProcessWrapper(process, this); + try { - StartProcess(process); + StartProcess(processWrapper); } catch (Exception ex) { @@ -264,7 +266,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } catch { - StopProcess(process, 100, true); + StopProcess(processWrapper, 100, true); throw; } @@ -303,7 +305,9 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); - StartProcess(process); + var processWrapper = new ProcessWrapper(process, this); + + StartProcess(processWrapper); var lines = new List(); var outputCancellationSource = new CancellationTokenSource(4000); @@ -325,7 +329,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } finally { - StopProcess(process, 100, true); + StopProcess(processWrapper, 100, true); } return lines; @@ -382,23 +386,6 @@ namespace MediaBrowser.MediaEncoding.Encoder /// protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// - /// Processes the exited. - /// - /// The sender. - /// The instance containing the event data. - private void ProcessExited(object sender, EventArgs e) - { - var process = (Process) sender; - - lock (_runningProcesses) - { - _runningProcesses.Remove(process); - } - - process.Dispose(); - } - public Task ExtractAudioImage(string path, CancellationToken cancellationToken) { return ExtractImage(new[] { path }, MediaProtocol.File, true, null, null, cancellationToken); @@ -423,6 +410,10 @@ namespace MediaBrowser.MediaEncoding.Encoder { return await ExtractImageInternal(inputArgument, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false); } + catch (ArgumentException) + { + throw; + } catch { _logger.Error("I-frame image extraction failed, will attempt standard way. Input: {0}", inputArgument); @@ -505,7 +496,9 @@ namespace MediaBrowser.MediaEncoding.Encoder await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - StartProcess(process); + var processWrapper = new ProcessWrapper(process, this); + + StartProcess(processWrapper); var memoryStream = new MemoryStream(); @@ -521,12 +514,12 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!ranToCompletion) { - StopProcess(process, 1000, false); + StopProcess(processWrapper, 1000, false); } resourcePool.Release(); - var exitCode = ranToCompletion ? process.ExitCode : -1; + var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1; process.Dispose(); @@ -611,9 +604,11 @@ namespace MediaBrowser.MediaEncoding.Encoder bool ranToCompletion; + var processWrapper = new ProcessWrapper(process, this); + try { - StartProcess(process); + StartProcess(processWrapper); // Need to give ffmpeg enough time to make all the thumbnails, which could be a while, // but we still need to detect if the process hangs. @@ -637,7 +632,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!ranToCompletion) { - StopProcess(process, 1000, false); + StopProcess(processWrapper, 1000, false); } } finally @@ -645,7 +640,7 @@ namespace MediaBrowser.MediaEncoding.Encoder resourcePool.Release(); } - var exitCode = ranToCompletion ? process.ExitCode : -1; + var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1; process.Dispose(); @@ -699,18 +694,16 @@ namespace MediaBrowser.MediaEncoding.Encoder return job.OutputFilePath; } - private void StartProcess(Process process) + private void StartProcess(ProcessWrapper process) { - process.Exited += ProcessExited; - - process.Start(); + process.Process.Start(); lock (_runningProcesses) { _runningProcesses.Add(process); } } - private void StopProcess(Process process, int waitTimeMs, bool enableForceKill) + private void StopProcess(ProcessWrapper process, int waitTimeMs, bool enableForceKill) { try { @@ -718,7 +711,7 @@ namespace MediaBrowser.MediaEncoding.Encoder try { - process.StandardInput.WriteLine("q"); + process.Process.StandardInput.WriteLine("q"); } catch (Exception) { @@ -727,7 +720,7 @@ namespace MediaBrowser.MediaEncoding.Encoder try { - if (process.WaitForExit(waitTimeMs)) + if (process.Process.WaitForExit(waitTimeMs)) { return; } @@ -739,7 +732,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (enableForceKill) { - process.Kill(); + process.Process .Kill(); } } catch (Exception ex) @@ -750,7 +743,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private void StopProcesses() { - List proceses; + List proceses; lock (_runningProcesses) { proceses = _runningProcesses.ToList(); @@ -782,5 +775,36 @@ namespace MediaBrowser.MediaEncoding.Encoder StopProcesses(); } } + + private class ProcessWrapper + { + public readonly Process Process; + public bool HasExited; + public int? ExitCode; + private readonly MediaEncoder _mediaEncoder; + + public ProcessWrapper(Process process, MediaEncoder mediaEncoder) + { + Process = process; + this._mediaEncoder = mediaEncoder; + Process.Exited += Process_Exited; + } + + void Process_Exited(object sender, EventArgs e) + { + var process = (Process)sender; + + HasExited = true; + + ExitCode = process.ExitCode; + + lock (_mediaEncoder._runningProcesses) + { + _mediaEncoder._runningProcesses.Remove(this); + } + + process.Dispose(); + } + } } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index b1bc5dbe5..03daa4c2a 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; @@ -69,7 +70,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints /// The instance containing the event data. void libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) { - if (e.Item.LocationType == LocationType.Virtual) + if (!FilterItem(e.Item)) { return; } @@ -102,7 +103,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints /// The instance containing the event data. void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e) { - if (e.Item.LocationType == LocationType.Virtual) + if (!FilterItem(e.Item)) { return; } @@ -130,7 +131,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints /// The instance containing the event data. void libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e) { - if (e.Item.LocationType == LocationType.Virtual) + if (!FilterItem(e.Item)) { return; } @@ -257,6 +258,16 @@ namespace MediaBrowser.Server.Implementations.EntryPoints }; } + private bool FilterItem(BaseItem item) + { + if (item.LocationType == LocationType.Virtual) + { + return false; + } + + return !(item is IChannelItem); + } + /// /// Translates the physical item to user library. /// diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 56557d6e1..6b99883a5 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -151,8 +151,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder chapter.ImagePath = path; changesMade = true; } - catch + catch (Exception ex) { + _logger.ErrorException("Error extraching chapter images for {0}", ex, string.Join(",", inputPath)); success = false; break; } -- cgit v1.2.3 From 65fb01bbe2fb95876217d2374a72b175fe235e60 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Apr 2015 01:12:02 -0400 Subject: rework server sync --- .../Entities/UserViewBuilder.cs | 53 ++++++++++++------ .../MediaBrowser.Controller.csproj | 4 ++ MediaBrowser.Controller/Sync/IHasDynamicAccess.cs | 4 +- .../Sync/IServerSyncProvider.cs | 34 +++++------- MediaBrowser.Controller/Sync/ISyncDataProvider.cs | 14 ++--- MediaBrowser.Controller/Sync/SyncedFileInfo.cs | 5 ++ MediaBrowser.Controller/packages.config | 1 + MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 12 +++-- MediaBrowser.Model/Entities/CollectionType.cs | 1 + MediaBrowser.Model/Session/ClientCapabilities.cs | 3 ++ MediaBrowser.Model/Sync/LocalItem.cs | 5 ++ .../Localization/Server/server.json | 1 + .../MediaBrowser.Server.Implementations.csproj | 4 ++ .../Sync/MediaSync.cs | 63 +++++++++++++++------- .../Sync/SyncManager.cs | 6 +-- .../Sync/SyncedMediaSourceProvider.cs | 7 ++- .../Sync/TargetDataProvider.cs | 35 +++++------- .../UserViews/DynamicImageProvider.cs | 1 + .../packages.config | 1 + Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 5 +- 23 files changed, 162 insertions(+), 105 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 562ae9f62..63ce223af 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -50,8 +50,8 @@ namespace MediaBrowser.Controller.Entities { var user = query.User; - if (query.IncludeItemTypes != null && - query.IncludeItemTypes.Length == 1 && + if (query.IncludeItemTypes != null && + query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase)) { if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) @@ -117,9 +117,7 @@ namespace MediaBrowser.Controller.Entities case CollectionType.LiveTv: { - var result = await GetLiveTvFolders(user).ConfigureAwait(false); - - return GetResult(result, queryParent, query); + return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false); } case CollectionType.Books: @@ -215,6 +213,9 @@ namespace MediaBrowser.Controller.Entities case SpecialFolder.MusicLatest: return GetMusicLatest(queryParent, user, query); + case SpecialFolder.MusicPlaylists: + return await GetMusicPlaylists(queryParent, user, query).ConfigureAwait(false); + case SpecialFolder.MusicAlbums: return GetMusicAlbums(queryParent, user, query); @@ -277,12 +278,13 @@ namespace MediaBrowser.Controller.Entities var list = new List(); list.Add(await GetUserView(SpecialFolder.MusicLatest, user, "0", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "1", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false)); - //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "3", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "4", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "5", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "6", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicPlaylists, user, "1", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "2", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "3", parent).ConfigureAwait(false)); + //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "5", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "6", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "7", parent).ConfigureAwait(false)); return GetResult(list, parent, query); } @@ -423,6 +425,14 @@ namespace MediaBrowser.Controller.Entities return GetResult(artists, parent, query); } + private Task> GetMusicPlaylists(Folder parent, User user, InternalItemsQuery query) + { + query.IncludeItemTypes = new[] { "Playlist" }; + query.Recursive = true; + + return parent.GetItems(query); + } + private QueryResult GetMusicAlbums(Folder parent, User user, InternalItemsQuery query) { var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => (i is MusicAlbum) && FilterItem(i, query)); @@ -1769,17 +1779,26 @@ namespace MediaBrowser.Controller.Entities return parent.GetRecursiveChildren(user, filter); } - private async Task> GetLiveTvFolders(User user) + private async Task> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query) { - var list = new List(); + if (query.Recursive) + { + return await _liveTvManager.GetInternalRecordings(new RecordingQuery + { + IsInProgress = false, + Status = RecordingStatus.Completed, + UserId = user.Id.ToString("N") - var parent = user.RootFolder; + }, CancellationToken.None).ConfigureAwait(false); + } + + var list = new List(); //list.Add(await GetUserSubView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, user.RootFolder).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, user.RootFolder).ConfigureAwait(false)); - return list; + return GetResult(list, queryParent, query); } private async Task GetUserView(string name, string type, User user, string sortName, BaseItem parent) diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ba0c266b2..b9a161a9f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -52,6 +52,10 @@ False ..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll + + False + ..\packages\Patterns.IO.1.0.0.3\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.IO.dll + diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs index f907de729..cf868a381 100644 --- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs +++ b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs @@ -9,10 +9,10 @@ namespace MediaBrowser.Controller.Sync /// /// Gets the synced file information. /// - /// The remote path. + /// The identifier. /// The target. /// The cancellation token. /// Task<SyncedFileInfo>. - Task GetSyncedFileInfo(string remotePath, SyncTarget target, CancellationToken cancellationToken); + Task GetSyncedFileInfo(string id, SyncTarget target, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 46bbbd329..6b694d26d 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -1,6 +1,7 @@ -using MediaBrowser.Model.Sync; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Sync; +using Patterns.IO; using System; -using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -13,46 +14,39 @@ namespace MediaBrowser.Controller.Sync /// Transfers the file. /// /// The stream. - /// The remote path. + /// The path parts. /// The target. /// The progress. /// The cancellation token. /// Task. - Task SendFile(Stream stream, string remotePath, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + Task SendFile(Stream stream, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); /// /// Deletes the file. /// - /// The path. + /// The identifier. /// The target. /// The cancellation token. /// Task. - Task DeleteFile(string path, SyncTarget target, CancellationToken cancellationToken); + Task DeleteFile(string id, SyncTarget target, CancellationToken cancellationToken); /// /// Gets the file. /// - /// The path. + /// The identifier. /// The target. /// The progress. /// The cancellation token. /// Task<Stream>. - Task GetFile(string path, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + Task GetFile(string id, SyncTarget target, IProgress progress, CancellationToken cancellationToken); /// - /// Gets the full path. + /// Gets the files. /// - /// The path. + /// The query. /// The target. - /// System.String. - string GetFullPath(IEnumerable path, SyncTarget target); - - /// - /// Gets the parent directory path. - /// - /// The path. - /// The target. - /// System.String. - string GetParentDirectoryPath(string path, SyncTarget target); + /// The cancellation token. + /// Task<QueryResult<FileMetadata>>. + Task> GetFiles(FileQuery query, SyncTarget target, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs index dc3edc4e4..ebff50cb0 100644 --- a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs @@ -7,20 +7,12 @@ namespace MediaBrowser.Controller.Sync public interface ISyncDataProvider { /// - /// Gets the server item ids. + /// Gets the local items. /// /// The target. /// The server identifier. - /// 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); + /// Task<List<LocalItem>>. + Task> GetLocalItems(SyncTarget target, string serverId); /// /// Adds the or update. diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs index 550af2d55..844e7d890 100644 --- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs +++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs @@ -20,6 +20,11 @@ namespace MediaBrowser.Controller.Sync /// /// The required HTTP headers. public Dictionary RequiredHttpHeaders { get; set; } + /// + /// Gets or sets the identifier. + /// + /// The identifier. + public string Id { get; set; } public SyncedFileInfo() { diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config index 6df166204..8d10de2f1 100644 --- a/MediaBrowser.Controller/packages.config +++ b/MediaBrowser.Controller/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 0bf007572..846d54a5f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -732,7 +732,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (enableForceKill) { - process.Process .Kill(); + process.Process.Kill(); } } catch (Exception ex) @@ -748,10 +748,14 @@ namespace MediaBrowser.MediaEncoding.Encoder { proceses = _runningProcesses.ToList(); } + _runningProcesses.Clear(); foreach (var process in proceses) { - StopProcess(process, 500, true); + if (!process.HasExited) + { + StopProcess(process, 500, true); + } } } @@ -801,8 +805,8 @@ namespace MediaBrowser.MediaEncoding.Encoder lock (_mediaEncoder._runningProcesses) { _mediaEncoder._runningProcesses.Remove(this); - } - + } + process.Dispose(); } } diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs index e51b2d311..a259f4c07 100644 --- a/MediaBrowser.Model/Entities/CollectionType.cs +++ b/MediaBrowser.Model/Entities/CollectionType.cs @@ -61,6 +61,7 @@ public const string MusicGenres = "MusicGenres"; public const string MusicGenre = "MusicGenre"; public const string MusicLatest = "MusicLatest"; + public const string MusicPlaylists = "MusicPlaylists"; public const string MusicSongs = "MusicSongs"; public const string MusicFavorites = "MusicFavorites"; public const string MusicFavoriteArtists = "MusicFavoriteArtists"; diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index 25438a811..095ad472a 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -21,6 +21,9 @@ namespace MediaBrowser.Model.Session public DeviceProfile DeviceProfile { get; set; } public List SupportedLiveMediaTypes { get; set; } + public string AppUrl { get; set; } + public string AppImageUrl { get; set; } + public ClientCapabilities() { PlayableMediaTypes = new List(); diff --git a/MediaBrowser.Model/Sync/LocalItem.cs b/MediaBrowser.Model/Sync/LocalItem.cs index dbbecaf05..c5728ac97 100644 --- a/MediaBrowser.Model/Sync/LocalItem.cs +++ b/MediaBrowser.Model/Sync/LocalItem.cs @@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Sync /// The unique identifier. public string Id { get; set; } /// + /// Gets or sets the file identifier. + /// + /// The file identifier. + public string FileId { get; set; } + /// /// Gets or sets the item identifier. /// /// The item identifier. diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 6c46f9009..bfd23243a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -964,6 +964,7 @@ "ViewTypeMovieFavorites": "Favorites", "ViewTypeMovieGenres": "Genres", "ViewTypeMusicLatest": "Latest", + "ViewTypeMusicPlaylists": "Playlists", "ViewTypeMusicAlbums": "Albums", "ViewTypeMusicAlbumArtists": "Album Artists", "HeaderOtherDisplaySettings": "Display Settings", diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 6f95686e1..f887a3c24 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -57,6 +57,10 @@ False ..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll + + False + ..\packages\Patterns.IO.1.0.0.3\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.IO.dll + ..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll True diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index ad313670a..1a8b55a25 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; @@ -12,12 +11,14 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Sync; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; +using Patterns.IO; namespace MediaBrowser.Server.Implementations.Sync { @@ -29,6 +30,9 @@ namespace MediaBrowser.Server.Implementations.Sync private readonly IFileSystem _fileSystem; private readonly IConfigurationManager _config; + public const string PathSeparatorString = "/"; + public const char PathSeparatorChar = '/'; + public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config) { _logger = logger; @@ -71,7 +75,24 @@ namespace MediaBrowser.Server.Implementations.Sync SyncTarget target, CancellationToken cancellationToken) { - var jobItemIds = await dataProvider.GetSyncJobItemIds(target, serverId).ConfigureAwait(false); + var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false); + var remoteFiles = await provider.GetFiles(new FileQuery(), target, cancellationToken).ConfigureAwait(false); + var remoteIds = remoteFiles.Items.Select(i => i.Id).ToList(); + + var jobItemIds = new List(); + + foreach (var localItem in localItems) + { + // TODO: Remove this after a while + if (string.IsNullOrWhiteSpace(localItem.FileId)) + { + jobItemIds.Add(localItem.SyncJobItemId); + } + else if (remoteIds.Contains(localItem.FileId, StringComparer.OrdinalIgnoreCase)) + { + jobItemIds.Add(localItem.SyncJobItemId); + } + } var result = await _syncManager.SyncData(new SyncDataRequest { @@ -163,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.Sync var fileTransferProgress = new ActionableProgress(); fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92)); - var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath, target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false); + var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath.Split(PathSeparatorChar), target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false); if (localItem.Item.MediaSources != null) { @@ -177,6 +198,8 @@ namespace MediaBrowser.Server.Implementations.Sync } } + localItem.FileId = sendFileResult.Id; + // Create db record await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false); @@ -228,10 +251,10 @@ namespace MediaBrowser.Server.Implementations.Sync var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, options, new Progress(), cancellationToken).ConfigureAwait(false); // This is the path that will be used when talking to the provider - mediaStream.ExternalId = remotePath; + mediaStream.ExternalId = sendFileResult.Id; // Keep track of all additional files for cleanup later. - localItem.AdditionalFiles.Add(remotePath); + localItem.AdditionalFiles.Add(sendFileResult.Id); // This is the public path clients will use mediaStream.Path = sendFileResult.Path; @@ -256,17 +279,15 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private string GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target) + private string[] GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target) { - var path = item.LocalPath; - var filename = GetSubtitleSaveFileName(item, stream.Language, stream.IsForced) + "." + stream.Codec.ToLower(); - var parentPath = provider.GetParentDirectoryPath(path, target); - - path = Path.Combine(parentPath, filename); + var pathParts = item.LocalPath.Split(PathSeparatorChar); + var list = pathParts.Take(pathParts.Length - 1).ToList(); + list.Add(filename); - return path; + return list.ToArray(); } private string GetSubtitleSaveFileName(LocalItem item, string language, bool isForced) @@ -300,12 +321,16 @@ namespace MediaBrowser.Server.Implementations.Sync foreach (var localItem in localItems) { var files = localItem.AdditionalFiles.ToList(); - files.Insert(0, localItem.LocalPath); + + // TODO: Remove this. Have to check it for now since this is a new property + if (!string.IsNullOrWhiteSpace(localItem.FileId)) + { + files.Insert(0, localItem.FileId); + } foreach (var file in files) { _logger.Debug("Removing {0} from {1}.", file, target.Name); - await provider.DeleteFile(file, target, cancellationToken).ConfigureAwait(false); } @@ -313,9 +338,9 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private async Task SendFile(IServerSyncProvider provider, string inputPath, string remotePath, SyncTarget target, SyncOptions options, IProgress progress, CancellationToken cancellationToken) + private async Task SendFile(IServerSyncProvider provider, string inputPath, string[] pathParts, SyncTarget target, SyncOptions options, IProgress progress, CancellationToken cancellationToken) { - _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, remotePath); + _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, string.Join("/", pathParts)); using (var fileStream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { Stream stream = fileStream; @@ -325,7 +350,7 @@ namespace MediaBrowser.Server.Implementations.Sync stream = new ThrottledStream(stream, options.UploadSpeedLimitBytes); } - return await provider.SendFile(stream, remotePath, target, progress, cancellationToken).ConfigureAwait(false); + return await provider.SendFile(stream, pathParts, target, progress, cancellationToken).ConfigureAwait(false); } } @@ -349,7 +374,7 @@ namespace MediaBrowser.Server.Implementations.Sync var path = GetDirectoryPath(provider, job, syncedItem, libraryItem, serverName); path.Add(GetLocalFileName(provider, libraryItem, originalFileName)); - var localPath = provider.GetFullPath(path, target); + var localPath = string.Join(PathSeparatorString, path.ToArray()); foreach (var mediaSource in libraryItem.MediaSources) { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index dbe43c2d1..6f0310e3c 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -793,8 +793,6 @@ namespace MediaBrowser.Server.Implementations.Sync } else { - _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.ItemId); - // Content is no longer on the device if (jobItem.IsMarkedForRemoval) { @@ -802,6 +800,7 @@ namespace MediaBrowser.Server.Implementations.Sync } else { + _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.ItemId); jobItem.Status = SyncJobItemStatus.Queued; } requiresSaving = true; @@ -902,8 +901,6 @@ namespace MediaBrowser.Server.Implementations.Sync } else { - _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.Id); - // Content is no longer on the device if (jobItem.IsMarkedForRemoval) { @@ -911,6 +908,7 @@ namespace MediaBrowser.Server.Implementations.Sync } else { + _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.Id); jobItem.Status = SyncJobItemStatus.Queued; } requiresSaving = true; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index 40d40d415..f7f320741 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -109,8 +109,13 @@ namespace MediaBrowser.Server.Implementations.Sync var dataProvider = _syncManager.GetDataProvider(provider, target); var localItem = await dataProvider.Get(target, openKeys[2]).ConfigureAwait(false); + var fileId = localItem.FileId; + if (string.IsNullOrWhiteSpace(fileId)) + { + } + var requiresDynamicAccess = (IHasDynamicAccess)provider; - var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(localItem.LocalPath, target, cancellationToken).ConfigureAwait(false); + var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(fileId, target, cancellationToken).ConfigureAwait(false); var mediaSource = localItem.Item.MediaSources.First(); mediaSource.LiveStreamId = Guid.NewGuid().ToString(); diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs index 3323ae148..dc95ee316 100644 --- a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Sync; @@ -12,6 +11,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Patterns.IO; namespace MediaBrowser.Server.Implementations.Sync { @@ -29,8 +29,6 @@ namespace MediaBrowser.Server.Implementations.Sync private readonly IApplicationPaths _appPaths; private readonly IServerApplicationHost _appHost; - private readonly SemaphoreSlim _cacheFileLock = new SemaphoreSlim(1, 1); - public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths) { _logger = logger; @@ -42,7 +40,7 @@ namespace MediaBrowser.Server.Implementations.Sync _appHost = appHost; } - private string GetRemotePath() + private string[] GetRemotePath() { var parts = new List { @@ -52,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.Sync parts = parts.Select(i => GetValidFilename(_provider, i)).ToList(); - return _provider.GetFullPath(parts, _target); + return parts.ToArray(); } private string GetValidFilename(IServerSyncProvider provider, string filename) @@ -65,22 +63,22 @@ namespace MediaBrowser.Server.Implementations.Sync { if (_items == null) { - try + _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name); + + var fileResult = await _provider.GetFiles(new FileQuery { - var path = GetRemotePath(); + FullPath = GetRemotePath().ToArray() - _logger.Debug("Getting {0} from {1}", path, _provider.Name); + }, _target, cancellationToken).ConfigureAwait(false); - using (var stream = await _provider.GetFile(path, _target, new Progress(), cancellationToken)) + if (fileResult.Items.Length > 0) + { + using (var stream = await _provider.GetFile(fileResult.Items[0].Id, _target, new Progress(), cancellationToken)) { _items = _json.DeserializeFromStream>(stream); } } - catch (FileNotFoundException) - { - _items = new List(); - } - catch (DirectoryNotFoundException) + else { _items = new List(); } @@ -133,14 +131,9 @@ namespace MediaBrowser.Server.Implementations.Sync } } - public Task> GetServerItemIds(SyncTarget target, string serverId) - { - 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) + public Task> GetLocalItems(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()); + return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).ToList()); } public Task AddOrUpdate(SyncTarget target, LocalItem item) diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs index ad8c6e414..47487bf4c 100644 --- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -203,6 +203,7 @@ namespace MediaBrowser.Server.Implementations.UserViews SpecialFolder.MusicGenres, SpecialFolder.MusicGenre, SpecialFolder.MusicLatest, + SpecialFolder.MusicPlaylists, SpecialFolder.MusicSongs, SpecialFolder.MusicFavorites, SpecialFolder.MusicFavoriteArtists, diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index c4806bb1b..b0e455dbe 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 0e9ab6001..029e13b68 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.616 + 3.0.619 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 e1019e2e4..f2c9f87fa 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.616 + 3.0.619 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 24f43a75b..9d14d3a0b 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.616 + 3.0.619 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 2b8b9fc37..35163a659 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.616 + 3.0.619 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,8 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + + -- cgit v1.2.3 From 5fe9b054c8c1c87261009f3aba997c4d594fcd6d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Apr 2015 23:45:17 -0400 Subject: 3.0.5582.0 --- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 16 +++++++++++++++- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 14 +++----------- MediaBrowser.Model/Dto/BaseItemDto.cs | 6 ++++++ MediaBrowser.Model/Querying/ItemFields.cs | 5 +++++ MediaBrowser.Server.Implementations/Dto/DtoService.cs | 2 ++ SharedVersion.cs | 4 ++-- 7 files changed, 34 insertions(+), 15 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index c06bbe143..1b11f1f33 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -736,7 +736,7 @@ namespace MediaBrowser.Api.Playback.Hls ).Trim(); } - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -copyts -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3a43fa2ca..b7322494d 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using System.Globalization; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; @@ -1892,5 +1893,18 @@ namespace MediaBrowser.Controller.Entities return video.RefreshMetadata(newOptions, cancellationToken); } + + public string GetEtag() + { + return string.Join("|", GetEtagValues().ToArray()).GetMD5().ToString("N"); + } + + protected virtual List GetEtagValues() + { + return new List + { + DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture) + }; + } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 846d54a5f..df7351ad1 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -280,7 +280,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private async Task> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken) { - const string args = "-i {0} -select_streams v:{1} -show_frames -print_format compact"; + const string args = "-i {0} -select_streams v:{1} -show_frames -show_entries frame=pkt_dts,key_frame -print_format compact"; var process = new Process { @@ -310,15 +310,12 @@ namespace MediaBrowser.MediaEncoding.Encoder StartProcess(processWrapper); var lines = new List(); - var outputCancellationSource = new CancellationTokenSource(4000); try { process.BeginErrorReadLine(); - var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(outputCancellationSource.Token, cancellationToken); - - await StartReadingOutput(process.StandardOutput.BaseStream, lines, 120000, outputCancellationSource, linkedCancellationTokenSource.Token).ConfigureAwait(false); + await StartReadingOutput(process.StandardOutput.BaseStream, lines, 120000, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -335,7 +332,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return lines; } - private async Task StartReadingOutput(Stream source, List lines, int timeoutMs, CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken) + private async Task StartReadingOutput(Stream source, List lines, int timeoutMs, CancellationToken cancellationToken) { try { @@ -362,11 +359,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { lines.Add(frameMs); } - - if (frameMs > timeoutMs) - { - cancellationTokenSource.Cancel(); - } } } } diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 6b223ea06..c772692a2 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -37,6 +37,12 @@ namespace MediaBrowser.Model.Dto /// The id. public string Id { get; set; } + /// + /// Gets or sets the etag. + /// + /// The etag. + public string Etag { get; set; } + /// /// Gets or sets the playlist item identifier. /// diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 54c83ca15..77b3dc0ee 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -75,6 +75,11 @@ /// DisplayMediaType, + /// + /// The etag + /// + Etag, + /// /// The external urls /// diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 6a7323fde..c4501564f 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -357,6 +357,8 @@ namespace MediaBrowser.Server.Implementations.Dto : item.CanDownload(user); } + + return dto; } diff --git a/SharedVersion.cs b/SharedVersion.cs index 405e1c708..c2890cb34 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("3.0.*")] -//[assembly: AssemblyVersion("3.0.5572.0")] +//[assembly: AssemblyVersion("3.0.*")] +[assembly: AssemblyVersion("3.0.5582.0")] -- cgit v1.2.3