mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-27 18:10:54 +01:00
de-normalize item by name data. create counts during library scan for fast access.
This commit is contained in:
@@ -98,13 +98,13 @@ namespace MediaBrowser.Providers
|
||||
var args = GetResolveArgsContainingImages(item);
|
||||
|
||||
// Make sure current image paths still exist
|
||||
ValidateImages(item);
|
||||
item.ValidateImages();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Make sure current backdrop paths still exist
|
||||
ValidateBackdrops(item);
|
||||
ValidateScreenshots(item, args);
|
||||
item.ValidateBackdrops();
|
||||
item.ValidateScreenshots();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -128,74 +128,6 @@ namespace MediaBrowser.Providers
|
||||
return item.ResolveArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that images within the item are still on the file system
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
internal static void ValidateImages(BaseItem item)
|
||||
{
|
||||
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
|
||||
var deletedKeys = item.Images
|
||||
.ToList()
|
||||
.Where(image => !File.Exists(image.Value))
|
||||
.Select(i => i.Key)
|
||||
.ToList();
|
||||
|
||||
// Now remove them from the dictionary
|
||||
foreach (var key in deletedKeys)
|
||||
{
|
||||
item.Images.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that backdrops within the item are still on the file system
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
internal static void ValidateBackdrops(BaseItem item)
|
||||
{
|
||||
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
|
||||
var deletedImages = item.BackdropImagePaths
|
||||
.Where(path => !File.Exists(path))
|
||||
.ToList();
|
||||
|
||||
// Now remove them from the dictionary
|
||||
foreach (var path in deletedImages)
|
||||
{
|
||||
item.BackdropImagePaths.Remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the screenshots.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="args">The args.</param>
|
||||
private void ValidateScreenshots(BaseItem item, ItemResolveArgs args)
|
||||
{
|
||||
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
|
||||
var deletedImages = item.ScreenshotImagePaths
|
||||
.Where(path => !File.Exists(path))
|
||||
.ToList();
|
||||
|
||||
// Now remove them from the dictionary
|
||||
foreach (var path in deletedImages)
|
||||
{
|
||||
item.ScreenshotImagePaths.Remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is in same directory] [the specified item].
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if [is in same directory] [the specified item]; otherwise, <c>false</c>.</returns>
|
||||
private bool IsInMetaLocation(BaseItem item, string path)
|
||||
{
|
||||
return string.Equals(Path.GetDirectoryName(path), item.MetaLocation, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image.
|
||||
/// </summary>
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
<Compile Include="Music\AlbumInfoFromSongProvider.cs" />
|
||||
<Compile Include="Music\ArtistInfoFromSongProvider.cs" />
|
||||
<Compile Include="Music\ArtistProviderFromXml.cs" />
|
||||
<Compile Include="Music\ArtistsPostScanTask.cs" />
|
||||
<Compile Include="Music\FanArtAlbumProvider.cs" />
|
||||
<Compile Include="Music\FanArtArtistByNameProvider.cs" />
|
||||
<Compile Include="Music\FanArtArtistProvider.cs" />
|
||||
@@ -81,6 +80,7 @@
|
||||
<Compile Include="Music\LastfmArtistProvider.cs" />
|
||||
<Compile Include="Music\LastfmBaseProvider.cs" />
|
||||
<Compile Include="Music\LastfmHelper.cs" />
|
||||
<Compile Include="Music\MusicAlbumDynamicInfoProvider.cs" />
|
||||
<Compile Include="Music\MusicVideoXmlParser.cs" />
|
||||
<Compile Include="Music\SoundtrackPostScanTask.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
@@ -106,9 +106,9 @@
|
||||
<Compile Include="TV\RemoteSeasonProvider.cs" />
|
||||
<Compile Include="TV\RemoteSeriesProvider.cs" />
|
||||
<Compile Include="TV\SeasonProviderFromXml.cs" />
|
||||
<Compile Include="TV\SeriesPostScanTask.cs" />
|
||||
<Compile Include="TV\SeriesProviderFromXml.cs" />
|
||||
<Compile Include="TV\SeriesXmlParser.cs" />
|
||||
<Compile Include="TV\SeriesPostScanTask.cs" />
|
||||
<Compile Include="TV\TvdbPersonImageProvider.cs" />
|
||||
<Compile Include="TV\TvdbPrescanTask.cs" />
|
||||
<Compile Include="TV\TvdbSeriesImageProvider.cs" />
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
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.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 allSongs = allItems.OfType<Audio>().ToList();
|
||||
|
||||
var numComplete = 0;
|
||||
|
||||
foreach (var artist in allArtists)
|
||||
{
|
||||
var musicArtist = FindMusicArtist(artist, allMusicArtists);
|
||||
|
||||
if (musicArtist != null)
|
||||
{
|
||||
MergeImages(musicArtist.Images, artist.Images);
|
||||
|
||||
// Merge backdrops
|
||||
var backdrops = musicArtist.BackdropImagePaths.ToList();
|
||||
backdrops.InsertRange(0, artist.BackdropImagePaths);
|
||||
artist.BackdropImagePaths = backdrops.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
ImageFromMediaLocationProvider.ValidateImages(artist);
|
||||
ImageFromMediaLocationProvider.ValidateBackdrops(artist);
|
||||
}
|
||||
|
||||
if (!artist.LockedFields.Contains(MetadataFields.Genres))
|
||||
{
|
||||
// Avoid implicitly captured closure
|
||||
var artist1 = artist;
|
||||
|
||||
artist.Genres = allSongs.Where(i => i.HasArtist(artist1.Name))
|
||||
.SelectMany(i => i.Genres)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void MergeImages(Dictionary<ImageType, string> source, Dictionary<ImageType, string> target)
|
||||
{
|
||||
foreach (var key in source.Keys
|
||||
.ToList()
|
||||
.Where(k => !target.ContainsKey(k)))
|
||||
{
|
||||
string path;
|
||||
|
||||
if (source.TryGetValue(key, out path))
|
||||
{
|
||||
target[key] = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
list.AddRange(i.Artists);
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MusicAlbumDynamicInfoProvider
|
||||
/// </summary>
|
||||
public class MusicAlbumDynamicInfoProvider : BaseMetadataProvider, IDynamicInfoProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseMetadataProvider" /> class.
|
||||
/// </summary>
|
||||
/// <param name="logManager">The log manager.</param>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
public MusicAlbumDynamicInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager)
|
||||
: base(logManager, configurationManager)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supportses the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
public override bool Supports(BaseItem item)
|
||||
{
|
||||
return item is MusicAlbum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Needses the refresh internal.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="providerInfo">The provider info.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var album = (MusicAlbum)item;
|
||||
|
||||
var songs = album.RecursiveChildren
|
||||
.OfType<Audio>()
|
||||
.ToList();
|
||||
|
||||
album.AlbumArtist = songs
|
||||
.Select(i => i.AlbumArtist)
|
||||
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
||||
|
||||
album.Artists = songs.SelectMany(i => i.Artists)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
// Don't save to the db
|
||||
return FalseTaskResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public override MetadataProviderPriority Priority
|
||||
{
|
||||
get { return MetadataProviderPriority.Last; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user