浏览代码

fixes #280 - MB3 Local metadata fetcher for Music not seeing/using Artist Folder.jpg

Luke Pulverenti 12 年之前
父节点
当前提交
9a820efde3

+ 9 - 9
MediaBrowser.Api/LibraryService.cs

@@ -178,15 +178,15 @@ namespace MediaBrowser.Api
             var items = GetItems(request.UserId).ToList();
 
             var counts = new ItemCounts
-                {
-                    AlbumCount = items.OfType<MusicAlbum>().Count(),
-                    EpisodeCount = items.OfType<Episode>().Count(),
-                    GameCount = items.OfType<BaseGame>().Count(),
-                    MovieCount = items.OfType<Movie>().Count(),
-                    SeriesCount = items.OfType<Series>().Count(),
-                    SongCount = items.OfType<Audio>().Count(),
-                    TrailerCount = items.OfType<Trailer>().Count()
-                };
+            {
+                AlbumCount = items.OfType<MusicAlbum>().Count(),
+                EpisodeCount = items.OfType<Episode>().Count(),
+                GameCount = items.OfType<BaseGame>().Count(),
+                MovieCount = items.OfType<Movie>().Count(),
+                SeriesCount = items.OfType<Series>().Count(),
+                SongCount = items.OfType<Audio>().Count(),
+                TrailerCount = items.OfType<Trailer>().Count()
+            };
 
             return ToOptimizedResult(counts);
         }

+ 3 - 1
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -144,12 +144,14 @@ namespace MediaBrowser.Controller.Library
         /// <param name="introProviders">The intro providers.</param>
         /// <param name="itemComparers">The item comparers.</param>
         /// <param name="prescanTasks">The prescan tasks.</param>
+        /// <param name="postscanTasks">The postscan tasks.</param>
         void AddParts(IEnumerable<IResolverIgnoreRule> rules, 
             IEnumerable<IVirtualFolderCreator> pluginFolders, 
             IEnumerable<IItemResolver> resolvers, 
             IEnumerable<IIntroProvider> introProviders, 
             IEnumerable<IBaseItemComparer> itemComparers,
-            IEnumerable<ILibraryPrescanTask> prescanTasks);
+            IEnumerable<ILibraryPrescanTask> prescanTasks,
+            IEnumerable<ILibraryPostScanTask> postscanTasks);
 
         /// <summary>
         /// Sorts the specified items.

+ 20 - 0
MediaBrowser.Controller/Library/ILibraryPostScanTask.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Library
+{
+    /// <summary>
+    /// An interface for tasks that run after the media library scan
+    /// </summary>
+    public interface ILibraryPostScanTask
+    {
+        /// <summary>
+        /// Runs the specified progress.
+        /// </summary>
+        /// <param name="progress">The progress.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task Run(IProgress<double> progress, CancellationToken cancellationToken);
+    }
+}

+ 2 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -73,8 +73,10 @@
     <Compile Include="Configuration\IServerConfigurationManager.cs" />
     <Compile Include="Dto\SessionInfoDtoBuilder.cs" />
     <Compile Include="Entities\Audio\MusicAlbumDisc.cs" />
+    <Compile Include="Library\ILibraryPostScanTask.cs" />
     <Compile Include="Library\ILibraryPrescanTask.cs" />
     <Compile Include="Providers\Movies\MovieDbImagesProvider.cs" />
+    <Compile Include="Providers\Music\ArtistsPostScanTask.cs" />
     <Compile Include="Providers\Music\FanArtUpdatesPrescanTask.cs" />
     <Compile Include="Providers\TV\FanArtSeasonProvider.cs" />
     <Compile Include="Providers\TV\TvdbPrescanTask.cs" />

+ 8 - 1
MediaBrowser.Controller/Providers/ImagesByNameProvider.cs

@@ -155,7 +155,14 @@ namespace MediaBrowser.Controller.Providers
         {
             var location = GetLocation(item);
 
-            var files = new DirectoryInfo(location).EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
+            var directoryInfo = new DirectoryInfo(location);
+
+            if (!directoryInfo.Exists)
+            {
+                return null;
+            }
+
+            var files = directoryInfo.EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
 
             var file = files.FirstOrDefault(i => string.Equals(i.Name, filenameWithoutExtension + ".png", StringComparison.OrdinalIgnoreCase));
 

+ 132 - 0
MediaBrowser.Controller/Providers/Music/ArtistsPostScanTask.cs

@@ -0,0 +1,132 @@
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Providers.Music
+{
+    /// <summary>
+    /// Class ArtistsPostScanTask
+    /// </summary>
+    public class ArtistsPostScanTask : ILibraryPostScanTask
+    {
+        /// <summary>
+        /// The _library manager
+        /// </summary>
+        private readonly ILibraryManager _libraryManager;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ArtistsPostScanTask"/> class.
+        /// </summary>
+        /// <param name="libraryManager">The library manager.</param>
+        public ArtistsPostScanTask(ILibraryManager libraryManager)
+        {
+            _libraryManager = libraryManager;
+        }
+
+        /// <summary>
+        /// Runs the specified progress.
+        /// </summary>
+        /// <param name="progress">The progress.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var allItems = _libraryManager.RootFolder.RecursiveChildren.ToList();
+
+            var allArtists = await GetAllArtists(allItems).ConfigureAwait(false);
+
+            progress.Report(10);
+
+            var allMusicArtists = allItems.OfType<MusicArtist>().ToList();
+
+            var numComplete = 0;
+
+            foreach (var artist in allArtists)
+            {
+                var musicArtist = FindMusicArtist(artist, allMusicArtists);
+
+                if (musicArtist != null)
+                {
+                    artist.Images = new Dictionary<ImageType, string>(musicArtist.Images);
+
+                    artist.BackdropImagePaths = musicArtist.BackdropImagePaths.ToList();
+                    artist.ScreenshotImagePaths = musicArtist.ScreenshotImagePaths.ToList();
+                    artist.SetProviderId(MetadataProviders.Musicbrainz, musicArtist.GetProviderId(MetadataProviders.Musicbrainz));
+                }
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= allArtists.Length;
+                percent *= 5;
+
+                progress.Report(10 + percent);
+            }
+
+            var innerProgress = new ActionableProgress<double>();
+
+            innerProgress.RegisterAction(pct => progress.Report(15 + pct * .85));
+
+            await _libraryManager.ValidateArtists(cancellationToken, innerProgress).ConfigureAwait(false);
+        }
+
+        /// <summary>
+        /// Gets all artists.
+        /// </summary>
+        /// <param name="allItems">All items.</param>
+        /// <returns>Task{Artist[]}.</returns>
+        private Task<Artist[]> GetAllArtists(IEnumerable<BaseItem> allItems)
+        {
+            var itemsList = allItems.OfType<Audio>().ToList();
+
+            var tasks = itemsList
+                .SelectMany(i =>
+                {
+                    var list = new List<string>();
+
+                    if (!string.IsNullOrEmpty(i.AlbumArtist))
+                    {
+                        list.Add(i.AlbumArtist);
+                    }
+                    if (!string.IsNullOrEmpty(i.Artist))
+                    {
+                        list.Add(i.Artist);
+                    }
+
+                    return list;
+                })
+                .Distinct(StringComparer.OrdinalIgnoreCase)
+                .Select(i => _libraryManager.GetArtist(i));
+
+            return Task.WhenAll(tasks);
+        }
+
+        /// <summary>
+        /// Finds the music artist.
+        /// </summary>
+        /// <param name="artist">The artist.</param>
+        /// <param name="allMusicArtists">All music artists.</param>
+        /// <returns>MusicArtist.</returns>
+        private static MusicArtist FindMusicArtist(Artist artist, IEnumerable<MusicArtist> allMusicArtists)
+        {
+            var musicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz);
+
+            return allMusicArtists.FirstOrDefault(i =>
+            {
+                if (!string.IsNullOrWhiteSpace(musicBrainzId) && string.Equals(musicBrainzId, i.GetProviderId(MetadataProviders.Musicbrainz), StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+
+                return string.Compare(i.Name, artist.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0;
+            });
+        }
+    }
+}

+ 102 - 9
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -32,6 +32,15 @@ namespace MediaBrowser.Server.Implementations.Library
     /// </summary>
     public class LibraryManager : ILibraryManager
     {
+        /// <summary>
+        /// Gets or sets the postscan tasks.
+        /// </summary>
+        /// <value>The postscan tasks.</value>
+        private IEnumerable<ILibraryPostScanTask> PostscanTasks { get; set; }
+        /// <summary>
+        /// Gets or sets the prescan tasks.
+        /// </summary>
+        /// <value>The prescan tasks.</value>
         private IEnumerable<ILibraryPrescanTask> PrescanTasks { get; set; }
 
         /// <summary>
@@ -100,6 +109,9 @@ namespace MediaBrowser.Server.Implementations.Library
         /// </summary>
         private readonly IUserManager _userManager;
 
+        /// <summary>
+        /// The _user data repository
+        /// </summary>
         private readonly IUserDataRepository _userDataRepository;
 
         /// <summary>
@@ -113,11 +125,25 @@ namespace MediaBrowser.Server.Implementations.Library
         /// (typically, multiple user roots).  We store them here and be sure they all reference a
         /// single instance.
         /// </summary>
+        /// <value>The by reference items.</value>
         private ConcurrentDictionary<Guid, BaseItem> ByReferenceItems { get; set; }
 
+        /// <summary>
+        /// The _library items cache
+        /// </summary>
         private ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
+        /// <summary>
+        /// The _library items cache sync lock
+        /// </summary>
         private object _libraryItemsCacheSyncLock = new object();
+        /// <summary>
+        /// The _library items cache initialized
+        /// </summary>
         private bool _libraryItemsCacheInitialized;
+        /// <summary>
+        /// Gets the library items cache.
+        /// </summary>
+        /// <value>The library items cache.</value>
         private ConcurrentDictionary<Guid, BaseItem> LibraryItemsCache
         {
             get
@@ -127,6 +153,9 @@ namespace MediaBrowser.Server.Implementations.Library
             }
         }
 
+        /// <summary>
+        /// The _user root folders
+        /// </summary>
         private readonly ConcurrentDictionary<string, UserRootFolder> _userRootFolders =
             new ConcurrentDictionary<string, UserRootFolder>();
 
@@ -161,12 +190,14 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="introProviders">The intro providers.</param>
         /// <param name="itemComparers">The item comparers.</param>
         /// <param name="prescanTasks">The prescan tasks.</param>
+        /// <param name="postscanTasks">The postscan tasks.</param>
         public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
             IEnumerable<IVirtualFolderCreator> pluginFolders,
             IEnumerable<IItemResolver> resolvers,
             IEnumerable<IIntroProvider> introProviders,
             IEnumerable<IBaseItemComparer> itemComparers,
-            IEnumerable<ILibraryPrescanTask> prescanTasks)
+            IEnumerable<ILibraryPrescanTask> prescanTasks,
+            IEnumerable<ILibraryPostScanTask> postscanTasks)
         {
             EntityResolutionIgnoreRules = rules;
             PluginFolderCreators = pluginFolders;
@@ -174,6 +205,7 @@ namespace MediaBrowser.Server.Implementations.Library
             IntroProviders = introProviders;
             Comparers = itemComparers;
             PrescanTasks = prescanTasks;
+            PostscanTasks = postscanTasks;
         }
 
         /// <summary>
@@ -210,11 +242,27 @@ namespace MediaBrowser.Server.Implementations.Library
             }
         }
 
+        /// <summary>
+        /// The _internet providers enabled
+        /// </summary>
         private bool _internetProvidersEnabled;
+        /// <summary>
+        /// The _people image fetching enabled
+        /// </summary>
         private bool _peopleImageFetchingEnabled;
+        /// <summary>
+        /// The _items by name path
+        /// </summary>
         private string _itemsByNamePath;
+        /// <summary>
+        /// The _season zero display name
+        /// </summary>
         private string _seasonZeroDisplayName;
 
+        /// <summary>
+        /// Records the configuration values.
+        /// </summary>
+        /// <param name="configuration">The configuration.</param>
         private void RecordConfigurationValues(ServerConfiguration configuration)
         {
             _seasonZeroDisplayName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
@@ -227,7 +275,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// Configurations the updated.
         /// </summary>
         /// <param name="sender">The sender.</param>
-        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
+        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
         void ConfigurationUpdated(object sender, EventArgs e)
         {
             var config = ConfigurationManager.Configuration;
@@ -373,7 +421,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <summary>
         /// Ensure supplied item has only one instance throughout
         /// </summary>
-        /// <param name="item"></param>
+        /// <param name="item">The item.</param>
         /// <returns>The proper instance to the item</returns>
         public BaseItem GetOrAddByReferenceItem(BaseItem item)
         {
@@ -800,6 +848,12 @@ namespace MediaBrowser.Server.Implementations.Library
             _logger.Info("People validation complete");
         }
 
+        /// <summary>
+        /// Validates the artists.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <param name="progress">The progress.</param>
+        /// <returns>Task.</returns>
         public async Task ValidateArtists(CancellationToken cancellationToken, IProgress<double> progress)
         {
             const int maxTasks = 25;
@@ -924,11 +978,10 @@ namespace MediaBrowser.Server.Implementations.Library
             // Now validate the entire media library
             await RootFolder.ValidateChildren(innerProgress, cancellationToken, recursive: true).ConfigureAwait(false);
 
-            innerProgress = new ActionableProgress<double>();
-
-            innerProgress.RegisterAction(pct => progress.Report(80 + pct * .2));
-
-            await ValidateArtists(cancellationToken, innerProgress);
+            progress.Report(80);
+            
+            // Run post-scan tasks
+            await RunPostScanTasks(progress, cancellationToken).ConfigureAwait(false);
 
             progress.Report(100);
         }
@@ -971,7 +1024,47 @@ namespace MediaBrowser.Server.Implementations.Library
                 }
             }));
 
-            // Run prescan tasks
+            await Task.WhenAll(tasks).ConfigureAwait(false);
+        }
+
+        /// <summary>
+        /// Runs the post scan tasks.
+        /// </summary>
+        /// <param name="progress">The progress.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        private async Task RunPostScanTasks(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var postscanTasks = PostscanTasks.ToList();
+            var progressDictionary = new Dictionary<ILibraryPostScanTask, double>();
+
+            var tasks = postscanTasks.Select(i => Task.Run(async () =>
+            {
+                var innerProgress = new ActionableProgress<double>();
+
+                innerProgress.RegisterAction(pct =>
+                {
+                    lock (progressDictionary)
+                    {
+                        progressDictionary[i] = pct;
+
+                        double percent = progressDictionary.Values.Sum();
+                        percent /= postscanTasks.Count;
+
+                        progress.Report(80 + percent * .2);
+                    }
+                });
+
+                try
+                {
+                    await i.Run(innerProgress, cancellationToken);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error running postscan task", ex);
+                }
+            }));
+
             await Task.WhenAll(tasks).ConfigureAwait(false);
         }
 

+ 1 - 1
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -272,7 +272,7 @@
   <ItemGroup>
     <EmbeddedResource Include="MediaEncoder\fonts\ARIALUNI.TTF" />
     <EmbeddedResource Include="MediaEncoder\fonts\fonts.conf" />
-    <EmbeddedResource Include="MediaEncoder\ffmpeg20130509.zip" />
+    <EmbeddedResource Include="MediaEncoder\ffmpeg20130523.zip" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup />

+ 0 - 1
MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130509.zip.REMOVED.git-id

@@ -1 +0,0 @@
-2ecea48340becd42b1a3136c8ae551c96a2de324

+ 1 - 0
MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130523.zip.REMOVED.git-id

@@ -0,0 +1 @@
+4d70588f0da1095974027f09130938f318f865c5

+ 2 - 1
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -372,7 +372,8 @@ namespace MediaBrowser.ServerApplication
                     GetExports<IItemResolver>(), 
                     GetExports<IIntroProvider>(),
                     GetExports<IBaseItemComparer>(),
-                    GetExports<ILibraryPrescanTask>()),
+                    GetExports<ILibraryPrescanTask>(),
+                    GetExports<ILibraryPostScanTask>()),
 
                 () => ProviderManager.AddMetadataProviders(GetExports<BaseMetadataProvider>().ToArray())