diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-05-27 12:53:10 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-05-27 12:53:10 -0400 |
| commit | 9a820efde33b7a4cfe1dd3e5c37a2f1beaaec896 (patch) | |
| tree | 44905e755b26f13ab4815956763a08499a2f7c7d | |
| parent | 390f1653327e15248c0b0181b338c6a14d04732a (diff) | |
fixes #280 - MB3 Local metadata fetcher for Music not seeing/using Artist Folder.jpg
11 files changed, 280 insertions, 23 deletions
diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 681bbd851..4998a122d 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/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); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 90c96c1ee..f5abb8dcd 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/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. diff --git a/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs b/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs new file mode 100644 index 000000000..694422907 --- /dev/null +++ b/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); + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 921e16eed..d3f8ba927 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/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" /> diff --git a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs b/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs index 54a6f3a89..20305006e 100644 --- a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs +++ b/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)); diff --git a/MediaBrowser.Controller/Providers/Music/ArtistsPostScanTask.cs b/MediaBrowser.Controller/Providers/Music/ArtistsPostScanTask.cs new file mode 100644 index 000000000..67bb35b63 --- /dev/null +++ b/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; + }); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index c82ebad51..cf5d75bb7 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/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); } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 82d3fec46..8799f4db3 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/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 /> diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130509.zip.REMOVED.git-id b/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130509.zip.REMOVED.git-id deleted file mode 100644 index d05d7c224..000000000 --- a/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130509.zip.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -2ecea48340becd42b1a3136c8ae551c96a2de324
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130523.zip.REMOVED.git-id b/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130523.zip.REMOVED.git-id new file mode 100644 index 000000000..1bb538541 --- /dev/null +++ b/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130523.zip.REMOVED.git-id @@ -0,0 +1 @@ +4d70588f0da1095974027f09130938f318f865c5
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index f1350b8a5..eba92a57d 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/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()) |
