diff options
Diffstat (limited to 'MediaBrowser.Controller')
12 files changed, 221 insertions, 121 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index e9f222016..7b64c0e85 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities.Audio { @@ -53,30 +54,6 @@ namespace MediaBrowser.Controller.Entities.Audio } /// <summary> - /// Override to point to first child (song) if not explicitly defined - /// </summary> - /// <value>The primary image path.</value> - [IgnoreDataMember] - public override string PrimaryImagePath - { - get - { - if (base.PrimaryImagePath == null) - { - var child = Children.FirstOrDefault(); - - return child == null ? base.PrimaryImagePath : child.PrimaryImagePath; - } - - return base.PrimaryImagePath; - } - set - { - base.PrimaryImagePath = value; - } - } - - /// <summary> /// Override to point to first child (song) /// </summary> /// <value>The production year.</value> @@ -131,6 +108,39 @@ namespace MediaBrowser.Controller.Entities.Audio } /// <summary> + /// Gets or sets the images. + /// </summary> + /// <value>The images.</value> + public override Dictionary<string, string> Images + { + get + { + var images = base.Images; + string primaryImagePath; + + if (images == null || !images.TryGetValue(ImageType.Primary.ToString(), out primaryImagePath)) + { + var image = Children.Select(c => c.PrimaryImagePath).FirstOrDefault(c => !string.IsNullOrEmpty(c)); + + if (!string.IsNullOrEmpty(image)) + { + if (images == null) + { + images = new Dictionary<string, string>(); + } + images[ImageType.Primary.ToString()] = image; + } + } + + return images; + } + set + { + base.Images = value; + } + } + + /// <summary> /// Creates the name of the sort. /// </summary> /// <returns>System.String.</returns> diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 411190d77..7587a8a5a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value>The primary image path.</value> [IgnoreDataMember] - public virtual string PrimaryImagePath + public string PrimaryImagePath { get { return GetImage(ImageType.Primary); } set { SetImage(ImageType.Primary, value); } @@ -79,7 +79,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the images. /// </summary> /// <value>The images.</value> - public Dictionary<string, string> Images { get; set; } + public virtual Dictionary<string, string> Images { get; set; } /// <summary> /// Gets or sets the date created. @@ -547,6 +547,12 @@ namespace MediaBrowser.Controller.Entities public virtual List<string> Studios { get; set; } /// <summary> + /// Gets or sets the publishers. + /// </summary> + /// <value>The publishers.</value> + public virtual List<string> Publishers { get; set; } + + /// <summary> /// Gets or sets the genres. /// </summary> /// <value>The genres.</value> @@ -996,7 +1002,7 @@ namespace MediaBrowser.Controller.Entities { if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentNullException(); + throw new ArgumentNullException("name"); } if (Studios == null) @@ -1011,6 +1017,47 @@ namespace MediaBrowser.Controller.Entities } /// <summary> + /// Adds the publishers. + /// </summary> + /// <param name="publishers">The publishers.</param> + /// <exception cref="System.ArgumentNullException"></exception> + public void AddPublishers(IEnumerable<string> publishers) + { + if (publishers == null) + { + throw new ArgumentNullException(); + } + + foreach (var name in publishers) + { + AddPublisher(name); + } + } + + /// <summary> + /// Adds the publisher. + /// </summary> + /// <param name="name">The name.</param> + /// <exception cref="System.ArgumentNullException">name</exception> + public void AddPublisher(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + if (Publishers == null) + { + Publishers = new List<string>(); + } + + if (!Publishers.Contains(name, StringComparer.OrdinalIgnoreCase)) + { + Publishers.Add(name); + } + } + + /// <summary> /// Adds a tagline to the item /// </summary> /// <param name="name">The name.</param> @@ -1019,7 +1066,7 @@ namespace MediaBrowser.Controller.Entities { if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentNullException(); + throw new ArgumentNullException("name"); } if (Taglines == null) @@ -1042,7 +1089,7 @@ namespace MediaBrowser.Controller.Entities { if (string.IsNullOrWhiteSpace(url)) { - throw new ArgumentNullException(); + throw new ArgumentNullException("url"); } if (TrailerUrls == null) diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 690f97605..954dfb05f 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -125,7 +125,7 @@ namespace MediaBrowser.Controller.Entities { get { - LazyInitializer.EnsureInitialized(ref _rootFolder, ref _userRootFolderInitialized, ref _userRootFolderSyncLock, () => (UserRootFolder)LibraryManager.ResolvePath(RootFolderPath)); + LazyInitializer.EnsureInitialized(ref _rootFolder, ref _userRootFolderInitialized, ref _userRootFolderSyncLock, () => LibraryManager.GetUserRootFolder(RootFolderPath)); return _rootFolder; } private set @@ -219,22 +219,6 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Validates only the collection folders for a User and goes no further - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="progress">The progress.</param> - /// <returns>Task.</returns> - public async Task ValidateCollectionFolders(IProgress<double> progress, CancellationToken cancellationToken) - { - Logger.Info("Validating collection folders for {0}", Name); - await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - - await RootFolder.ValidateChildren(progress, cancellationToken, recursive: false).ConfigureAwait(false); - } - - /// <summary> /// Renames the user. /// </summary> /// <param name="newName">The new name.</param> diff --git a/MediaBrowser.Controller/Library/DtoBuilder.cs b/MediaBrowser.Controller/Library/DtoBuilder.cs index f089b531c..1fde6a2d8 100644 --- a/MediaBrowser.Controller/Library/DtoBuilder.cs +++ b/MediaBrowser.Controller/Library/DtoBuilder.cs @@ -64,6 +64,11 @@ namespace MediaBrowser.Controller.Library dto.Studios = item.Studios; } + if (fields.Contains(ItemFields.Publishers)) + { + dto.Publishers = item.Publishers; + } + if (fields.Contains(ItemFields.People)) { tasks.Add(AttachPeople(dto, item)); diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index da2f46abb..36d39d530 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -163,5 +163,12 @@ namespace MediaBrowser.Controller.Library /// <param name="item"></param> /// <returns>The proper instance to the item</returns> BaseItem GetOrAddByReferenceItem(BaseItem item); + + /// <summary> + /// Gets the user root folder. + /// </summary> + /// <param name="userRootPath">The user root path.</param> + /// <returns>UserRootFolder.</returns> + UserRootFolder GetUserRootFolder(string userRootPath); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 1c03e11dd..8c2895701 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -146,7 +146,6 @@ <Compile Include="Providers\FanartBaseProvider.cs" /> <Compile Include="Providers\IImageEnhancer.cs" /> <Compile Include="Providers\ImagesByNameProvider.cs" /> - <Compile Include="Providers\MediaInfo\BaseFFMpegImageProvider.cs" /> <Compile Include="Providers\MediaInfo\BaseFFMpegProvider.cs" /> <Compile Include="Providers\MediaInfo\FFMpegAudioImageProvider.cs" /> <Compile Include="Providers\MediaInfo\BaseFFProbeProvider.cs" /> diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 3a4f3cae7..f0a960ce1 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -735,16 +735,16 @@ namespace MediaBrowser.Controller.MediaInfo /// <summary> /// Extracts an image from an Audio file and returns a Task whose result indicates whether it was successful or not /// </summary> - /// <param name="input">The input.</param> + /// <param name="inputPath">The input path.</param> /// <param name="outputPath">The output path.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{System.Boolean}.</returns> /// <exception cref="System.ArgumentNullException">input</exception> - public async Task<bool> ExtractImage(Audio input, string outputPath, CancellationToken cancellationToken) + public async Task<bool> ExtractAudioImage(string inputPath, string outputPath, CancellationToken cancellationToken) { - if (input == null) + if (string.IsNullOrEmpty(inputPath)) { - throw new ArgumentNullException("input"); + throw new ArgumentNullException("inputPath"); } if (string.IsNullOrEmpty(outputPath)) @@ -759,7 +759,7 @@ namespace MediaBrowser.Controller.MediaInfo CreateNoWindow = true, UseShellExecute = false, FileName = FFMpegPath, - Arguments = string.Format("-i {0} -threads 0 -v quiet -f image2 \"{1}\"", GetInputArgument(input), outputPath), + Arguments = string.Format("-i {0} -threads 0 -v quiet -f image2 \"{1}\"", GetFileInputArgument(inputPath), outputPath), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false } @@ -780,7 +780,7 @@ namespace MediaBrowser.Controller.MediaInfo return true; } - _logger.Error("ffmpeg audio image extraction failed for {0}", input.Path); + _logger.Error("ffmpeg audio image extraction failed for {0}", inputPath); return false; } @@ -1040,6 +1040,16 @@ namespace MediaBrowser.Controller.MediaInfo } /// <summary> + /// Gets the file input argument. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>System.String.</returns> + private string GetFileInputArgument(string path) + { + return string.Format("file:\"{0}\"", path); + } + + /// <summary> /// Gets the input argument. /// </summary> /// <param name="item">The item.</param> diff --git a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegImageProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegImageProvider.cs deleted file mode 100644 index 902fdca27..000000000 --- a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegImageProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers.MediaInfo -{ - public abstract class BaseFFMpegImageProvider<T> : BaseFFMpegProvider<T> - where T : BaseItem - { - protected BaseFFMpegImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) - { - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Last; } - } - } -} diff --git a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs index 20f59b22d..6edd28b24 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs @@ -2,9 +2,9 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; using System; using System.Threading.Tasks; -using MediaBrowser.Model.Logging; namespace MediaBrowser.Controller.Providers.MediaInfo { diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs index e752a863d..68e552d3c 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs @@ -1,21 +1,23 @@ -using MediaBrowser.Controller.Configuration; +using System.Collections.Concurrent; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Logging; namespace MediaBrowser.Controller.Providers.MediaInfo { /// <summary> /// Uses ffmpeg to create video images /// </summary> - public class FFMpegAudioImageProvider : BaseFFMpegImageProvider<Audio> + public class FFMpegAudioImageProvider : BaseFFMpegProvider<Audio> { - public FFMpegAudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) + public FFMpegAudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) { } @@ -25,67 +27,101 @@ namespace MediaBrowser.Controller.Providers.MediaInfo protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true); /// <summary> + /// Gets the priority. + /// </summary> + /// <value>The priority.</value> + public override MetadataProviderPriority Priority + { + get { return MetadataProviderPriority.Last; } + } + + /// <summary> + /// The _locks + /// </summary> + private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); + + /// <summary> + /// Gets the lock. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>System.Object.</returns> + private SemaphoreSlim GetLock(string filename) + { + return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); + } + + /// <summary> /// Fetches metadata and returns true or false indicating if any work that requires persistence was done /// </summary> /// <param name="item">The item.</param> /// <param name="force">if set to <c>true</c> [force].</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { - var audio = (Audio)item; + var success = ProviderRefreshStatus.Success; - if (string.IsNullOrEmpty(audio.PrimaryImagePath)) + if (force || string.IsNullOrEmpty(item.PrimaryImagePath)) { - // First try to use the parent's image - audio.PrimaryImagePath = audio.ResolveArgs.Parent.PrimaryImagePath; + var album = item.ResolveArgs.Parent as MusicAlbum; + + if (album != null) + { + // First try to use the parent's image + item.PrimaryImagePath = item.ResolveArgs.Parent.PrimaryImagePath; + } // If it's still empty see if there's an embedded image - if (string.IsNullOrEmpty(audio.PrimaryImagePath)) + if (force || string.IsNullOrEmpty(item.PrimaryImagePath)) { + var audio = (Audio)item; + if (audio.MediaStreams != null && audio.MediaStreams.Any(s => s.Type == MediaStreamType.Video)) { - var filename = item.Id + "_" + item.DateModified.Ticks + "_primary"; + var filename = album != null && string.IsNullOrEmpty(audio.Album + album.DateModified.Ticks) ? (audio.Id.ToString() + audio.DateModified.Ticks) : audio.Album; - var path = Kernel.Instance.FFMpegManager.AudioImageCache.GetResourcePath(filename, ".jpg"); + var path = Kernel.Instance.FFMpegManager.AudioImageCache.GetResourcePath(filename + "_primary", ".jpg"); if (!Kernel.Instance.FFMpegManager.AudioImageCache.ContainsFilePath(path)) { - return ExtractImage(audio, path, cancellationToken); - } - - // Image is already in the cache - audio.PrimaryImagePath = path; - } + var semaphore = GetLock(path); - } - } + // Acquire a lock + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } + // Check again + if (!Kernel.Instance.FFMpegManager.AudioImageCache.ContainsFilePath(path)) + { + try + { + var imageSucceeded = await Kernel.Instance.FFMpegManager.ExtractAudioImage(audio.Path, path, cancellationToken).ConfigureAwait(false); - /// <summary> - /// Extracts the image. - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="path">The path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - private async Task<bool> ExtractImage(Audio audio, string path, CancellationToken cancellationToken) - { - var success = await Kernel.Instance.FFMpegManager.ExtractImage(audio, path, cancellationToken).ConfigureAwait(false); + if (!imageSucceeded) + { + success = ProviderRefreshStatus.Failure; + } + } + finally + { + semaphore.Release(); + } + } + else + { + semaphore.Release(); + } + } - if (success) - { - audio.PrimaryImagePath = path; - SetLastRefreshed(audio, DateTime.UtcNow); - } - else - { - SetLastRefreshed(audio, DateTime.UtcNow, ProviderRefreshStatus.Failure); + if (success == ProviderRefreshStatus.Success) + { + // Image is already in the cache + audio.PrimaryImagePath = path; + } + } + } } + SetLastRefreshed(item, DateTime.UtcNow, success); return true; } } diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFMpegVideoImageProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFMpegVideoImageProvider.cs index 3d258fe66..9dec93a8c 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/FFMpegVideoImageProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/FFMpegVideoImageProvider.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo /// <summary> /// Uses ffmpeg to create video images /// </summary> - public class FfMpegVideoImageProvider : BaseFFMpegImageProvider<Video> + public class FfMpegVideoImageProvider : BaseFFMpegProvider<Video> { /// <summary> /// The _iso manager @@ -30,6 +30,15 @@ namespace MediaBrowser.Controller.Providers.MediaInfo : base(logManager, configurationManager) { _isoManager = isoManager; + } + + /// <summary> + /// Gets the priority. + /// </summary> + /// <value>The priority.</value> + public override MetadataProviderPriority Priority + { + get { return MetadataProviderPriority.Last; } } /// <summary> @@ -80,7 +89,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo /// <returns>Task{System.Boolean}.</returns> public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { - if (string.IsNullOrEmpty(item.PrimaryImagePath)) + if (force || string.IsNullOrEmpty(item.PrimaryImagePath)) { var video = (Video)item; diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs index a7cc4985b..e5f57d704 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs @@ -149,7 +149,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo // There's several values in tags may or may not be present FetchStudios(audio, tags, "organization"); FetchStudios(audio, tags, "ensemble"); - FetchStudios(audio, tags, "publisher"); + FetchPublishers(audio, tags, "publisher"); } /// <summary> @@ -169,6 +169,22 @@ namespace MediaBrowser.Controller.Providers.MediaInfo } /// <summary> + /// Fetches the publishers. + /// </summary> + /// <param name="audio">The audio.</param> + /// <param name="tags">The tags.</param> + /// <param name="tagName">Name of the tag.</param> + private void FetchPublishers(Audio audio, Dictionary<string, string> tags, string tagName) + { + var val = GetDictionaryValue(tags, tagName); + + if (!string.IsNullOrEmpty(val)) + { + audio.AddPublishers(val.Split(new[] { '/', '|' }, StringSplitOptions.RemoveEmptyEntries)); + } + } + + /// <summary> /// Gets the genres from the tags collection /// </summary> /// <param name="audio">The audio.</param> |
