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

pull/702/head
Luke Pulverenti 11 years ago
parent 390f165332
commit 9a820efde3

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

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

@ -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);
}
}

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

@ -155,7 +155,14 @@ namespace MediaBrowser.Controller.Providers
{ {
var location = GetLocation(item); 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)); var file = files.FirstOrDefault(i => string.Equals(i.Name, filenameWithoutExtension + ".png", StringComparison.OrdinalIgnoreCase));

@ -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;
});
}
}
}

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

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

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

Loading…
Cancel
Save