de-normalize item by name data. create counts during library scan for fast access.

This commit is contained in:
Luke Pulverenti
2013-09-10 14:56:00 -04:00
parent d078edfb96
commit 740a10a4e3
63 changed files with 1923 additions and 971 deletions

View File

@@ -107,6 +107,15 @@ namespace MediaBrowser.Server.Implementations.Dto
.ToArray();
}
if (fields.Contains(ItemFields.ItemCounts))
{
var itemByName = item as IItemByName;
if (itemByName != null)
{
AttachItemByNameCounts(dto, itemByName, user);
}
}
// Make sure all the tasks we kicked off have completed.
if (tasks.Count > 0)
{
@@ -116,6 +125,41 @@ namespace MediaBrowser.Server.Implementations.Dto
return dto;
}
/// <summary>
/// Attaches the item by name counts.
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
private void AttachItemByNameCounts(BaseItemDto dto, IItemByName item, User user)
{
ItemByNameCounts counts;
if (user == null)
{
counts = item.ItemCounts;
}
else
{
if (!item.UserItemCounts.TryGetValue(user.Id, out counts))
{
counts = new ItemByNameCounts();
}
}
dto.ChildCount = counts.TotalCount;
dto.AdultVideoCount = counts.AdultVideoCount;
dto.AlbumCount = counts.AlbumCount;
dto.EpisodeCount = counts.EpisodeCount;
dto.GameCount = counts.GameCount;
dto.MovieCount = counts.MovieCount;
dto.MusicVideoCount = counts.MusicVideoCount;
dto.SeriesCount = counts.SeriesCount;
dto.SongCount = counts.SongCount;
dto.TrailerCount = counts.TrailerCount;
}
/// <summary>
/// Attaches the user specific info.
/// </summary>
@@ -380,7 +424,9 @@ namespace MediaBrowser.Server.Implementations.Dto
_logger.ErrorException("Error getting {0} image info for {1}", ex, type, path);
return null;
}
} /// <summary>
}
/// <summary>
/// Attaches People DTO's to a DTOBaseItem
/// </summary>
/// <param name="dto">The dto.</param>
@@ -913,12 +959,7 @@ namespace MediaBrowser.Server.Implementations.Dto
if (album != null)
{
var songs = album.RecursiveChildren.OfType<Audio>().ToList();
dto.Artists =
songs.SelectMany(i => i.Artists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
dto.Artists = album.Artists;
}
var hasAlbumArtist = item as IHasAlbumArtist;
@@ -935,7 +976,6 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.VideoType = video.VideoType;
dto.Video3DFormat = video.Video3DFormat;
dto.IsoType = video.IsoType;
dto.MainFeaturePlaylistName = video.MainFeaturePlaylistName;
dto.PartCount = video.AdditionalPartIds.Count + 1;

View File

@@ -81,6 +81,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
bytes = await ReceiveBytesAsync(CancellationToken.None).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
break;
}
catch (WebSocketException ex)
{
_logger.ErrorException("Error receiving web socket message", ex);

View File

@@ -13,6 +13,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.Library.Validators;
using MediaBrowser.Server.Implementations.ScheduledTasks;
using MoreLinq;
using System;
@@ -597,11 +598,11 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="forceCreation">if set to <c>true</c> [force creation].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [force creation].</param>
/// <returns>Task{Person}.</returns>
private Task<Person> GetPerson(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool forceCreation = false)
private Task<Person> GetPerson(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool refreshMetadata = false)
{
return GetItemByName<Person>(ConfigurationManager.ApplicationPaths.PeoplePath, name, cancellationToken, allowSlowProviders, forceCreation);
return GetItemByName<Person>(ConfigurationManager.ApplicationPaths.PeoplePath, name, cancellationToken, allowSlowProviders, refreshMetadata);
}
/// <summary>
@@ -612,7 +613,20 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{Studio}.</returns>
public Task<Studio> GetStudio(string name, bool allowSlowProviders = false)
{
return GetItemByName<Studio>(ConfigurationManager.ApplicationPaths.StudioPath, name, CancellationToken.None, allowSlowProviders);
return GetStudio(name, CancellationToken.None, allowSlowProviders);
}
/// <summary>
/// Gets the studio.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [refresh metadata].</param>
/// <returns>Task{Studio}.</returns>
internal Task<Studio> GetStudio(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool refreshMetadata = false)
{
return GetItemByName<Studio>(ConfigurationManager.ApplicationPaths.StudioPath, name, cancellationToken, allowSlowProviders, refreshMetadata);
}
/// <summary>
@@ -623,7 +637,20 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public Task<Genre> GetGenre(string name, bool allowSlowProviders = false)
{
return GetItemByName<Genre>(ConfigurationManager.ApplicationPaths.GenrePath, name, CancellationToken.None, allowSlowProviders);
return GetGenre(name, CancellationToken.None, allowSlowProviders);
}
/// <summary>
/// Gets the genre.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [refresh metadata].</param>
/// <returns>Task{Genre}.</returns>
internal Task<Genre> GetGenre(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool refreshMetadata = false)
{
return GetItemByName<Genre>(ConfigurationManager.ApplicationPaths.GenrePath, name, cancellationToken, allowSlowProviders, refreshMetadata);
}
/// <summary>
@@ -634,7 +661,20 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{MusicGenre}.</returns>
public Task<MusicGenre> GetMusicGenre(string name, bool allowSlowProviders = false)
{
return GetItemByName<MusicGenre>(ConfigurationManager.ApplicationPaths.MusicGenrePath, name, CancellationToken.None, allowSlowProviders);
return GetMusicGenre(name, CancellationToken.None, allowSlowProviders);
}
/// <summary>
/// Gets the music genre.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [refresh metadata].</param>
/// <returns>Task{MusicGenre}.</returns>
internal Task<MusicGenre> GetMusicGenre(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool refreshMetadata = false)
{
return GetItemByName<MusicGenre>(ConfigurationManager.ApplicationPaths.MusicGenrePath, name, cancellationToken, allowSlowProviders, refreshMetadata);
}
/// <summary>
@@ -645,7 +685,20 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{GameGenre}.</returns>
public Task<GameGenre> GetGameGenre(string name, bool allowSlowProviders = false)
{
return GetItemByName<GameGenre>(ConfigurationManager.ApplicationPaths.GameGenrePath, name, CancellationToken.None, allowSlowProviders);
return GetGameGenre(name, CancellationToken.None, allowSlowProviders);
}
/// <summary>
/// Gets the game genre.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [refresh metadata].</param>
/// <returns>Task{GameGenre}.</returns>
internal Task<GameGenre> GetGameGenre(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool refreshMetadata = false)
{
return GetItemByName<GameGenre>(ConfigurationManager.ApplicationPaths.GameGenrePath, name, cancellationToken, allowSlowProviders, refreshMetadata);
}
/// <summary>
@@ -665,11 +718,11 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="forceCreation">if set to <c>true</c> [force creation].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [force creation].</param>
/// <returns>Task{Artist}.</returns>
private Task<Artist> GetArtist(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool forceCreation = false)
internal Task<Artist> GetArtist(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool refreshMetadata = false)
{
return GetItemByName<Artist>(ConfigurationManager.ApplicationPaths.ArtistsPath, name, cancellationToken, allowSlowProviders, forceCreation);
return GetItemByName<Artist>(ConfigurationManager.ApplicationPaths.ArtistsPath, name, cancellationToken, allowSlowProviders, refreshMetadata);
}
/// <summary>
@@ -707,11 +760,11 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="forceCreation">if set to <c>true</c> [force creation].</param>
/// <param name="refreshMetadata">if set to <c>true</c> [force creation].</param>
/// <returns>Task{``0}.</returns>
/// <exception cref="System.ArgumentNullException">
/// </exception>
private async Task<T> GetItemByName<T>(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true, bool forceCreation = false)
private async Task<T> GetItemByName<T>(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true, bool refreshMetadata = false)
where T : BaseItem, new()
{
if (string.IsNullOrEmpty(path))
@@ -730,11 +783,25 @@ namespace MediaBrowser.Server.Implementations.Library
if (!_itemsByName.TryGetValue(key, out obj))
{
obj = await CreateItemByName<T>(path, name, cancellationToken, allowSlowProviders).ConfigureAwait(false);
var tuple = CreateItemByName<T>(path, name, cancellationToken);
obj = tuple.Item2;
_itemsByName.AddOrUpdate(key, obj, (keyName, oldValue) => obj);
try
{
await obj.RefreshMetadata(cancellationToken, tuple.Item1, allowSlowProviders: allowSlowProviders).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
BaseItem removed;
_itemsByName.TryRemove(key, out removed);
throw;
}
}
else if (forceCreation)
else if (refreshMetadata)
{
await obj.RefreshMetadata(cancellationToken, false, allowSlowProviders: allowSlowProviders).ConfigureAwait(false);
}
@@ -749,10 +816,9 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="path">The path.</param>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>Task{``0}.</returns>
/// <exception cref="System.IO.IOException">Path not created: + path</exception>
private async Task<T> CreateItemByName<T>(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true)
private Tuple<bool, T> CreateItemByName<T>(string path, string name, CancellationToken cancellationToken)
where T : BaseItem, new()
{
cancellationToken.ThrowIfCancellationRequested();
@@ -783,6 +849,7 @@ namespace MediaBrowser.Server.Implementations.Library
var id = path.GetMBId(type);
var item = RetrieveItem(id) as T;
if (item == null)
{
item = new T
@@ -796,16 +863,10 @@ namespace MediaBrowser.Server.Implementations.Library
isNew = true;
}
cancellationToken.ThrowIfCancellationRequested();
// Set this now so we don't cause additional file system access during provider executions
item.ResetResolveArgs(fileInfo);
await item.RefreshMetadata(cancellationToken, isNew, allowSlowProviders: allowSlowProviders).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
return item;
return new Tuple<bool,T>(isNew, item);
}
/// <summary>
@@ -884,75 +945,53 @@ namespace MediaBrowser.Server.Implementations.Library
/// <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 Task ValidateArtists(CancellationToken cancellationToken, IProgress<double> progress)
{
const int maxTasks = 25;
return new ArtistsValidator(this, _userManager, _logger).Run(progress, cancellationToken);
}
var tasks = new List<Task>();
/// <summary>
/// Validates the music genres.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public Task ValidateMusicGenres(CancellationToken cancellationToken, IProgress<double> progress)
{
return new MusicGenresValidator(this, _userManager, _logger).Run(progress, cancellationToken);
}
var artists = RootFolder.RecursiveChildren
.OfType<Audio>()
.SelectMany(c =>
{
var list = new List<string>();
/// <summary>
/// Validates the game genres.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public Task ValidateGameGenres(CancellationToken cancellationToken, IProgress<double> progress)
{
return new GameGenresValidator(this, _userManager, _logger).Run(progress, cancellationToken);
}
if (!string.IsNullOrEmpty(c.AlbumArtist))
{
list.Add(c.AlbumArtist);
}
list.AddRange(c.Artists);
/// <summary>
/// Validates the studios.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public Task ValidateStudios(CancellationToken cancellationToken, IProgress<double> progress)
{
return new StudiosValidator(this, _userManager, _logger).Run(progress, cancellationToken);
}
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var numComplete = 0;
foreach (var artist in artists)
{
if (tasks.Count > maxTasks)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
// Safe cancellation point, when there are no pending tasks
cancellationToken.ThrowIfCancellationRequested();
}
// Avoid accessing the foreach variable within the closure
var currentArtist = artist;
tasks.Add(Task.Run(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
try
{
await GetArtist(currentArtist, cancellationToken, true, true).ConfigureAwait(false);
}
catch (IOException ex)
{
_logger.ErrorException("Error validating Artist {0}", ex, currentArtist);
}
// Update progress
lock (progress)
{
numComplete++;
double percent = numComplete;
percent /= artists.Count;
progress.Report(100 * percent);
}
}));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
progress.Report(100);
_logger.Info("Artist validation complete");
/// <summary>
/// Validates the genres.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public Task ValidateGenres(CancellationToken cancellationToken, IProgress<double> progress)
{
return new GenresValidator(this, _userManager, _logger).Run(progress, cancellationToken);
}
/// <summary>
@@ -1000,12 +1039,12 @@ namespace MediaBrowser.Server.Implementations.Library
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(pct => progress.Report(15 + pct * .65));
innerProgress.RegisterAction(pct => progress.Report(15 + pct * .6));
// Now validate the entire media library
await RootFolder.ValidateChildren(innerProgress, cancellationToken, recursive: true).ConfigureAwait(false);
progress.Report(80);
progress.Report(75);
// Run post-scan tasks
await RunPostScanTasks(progress, cancellationToken).ConfigureAwait(false);
@@ -1078,7 +1117,7 @@ namespace MediaBrowser.Server.Implementations.Library
double percent = progressDictionary.Values.Sum();
percent /= postscanTasks.Count;
progress.Report(80 + percent * .2);
progress.Report(75 + percent * .25);
}
});

View File

@@ -155,7 +155,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
// Find genres, from non-audio items
var genres = items.Where(i => !(i is Audio) && !(i is MusicAlbum) && !(i is MusicArtist) && !(i is MusicVideo) && !(i is Game))
var genres = items.Where(i => !(i is IHasMusicGenres) && !(i is Game))
.SelectMany(i => i.Genres)
.Where(i => !string.IsNullOrEmpty(i))
.Distinct(StringComparer.OrdinalIgnoreCase)
@@ -181,7 +181,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
// Find music genres
var musicGenres = items.Where(i => (i is Audio) || (i is MusicAlbum) || (i is MusicArtist) || (i is MusicVideo))
var musicGenres = items.Where(i => i is IHasMusicGenres)
.SelectMany(i => i.Genres)
.Where(i => !string.IsNullOrEmpty(i))
.Distinct(StringComparer.OrdinalIgnoreCase)

View File

@@ -0,0 +1,38 @@
using MediaBrowser.Controller.Library;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
/// <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 Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidateArtists(cancellationToken, progress);
}
}
}

View File

@@ -0,0 +1,285 @@
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
/// <summary>
/// Class ArtistsValidator
/// </summary>
public class ArtistsValidator
{
/// <summary>
/// The _library manager
/// </summary>
private readonly LibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="logger">The logger.</param>
public ArtistsValidator(LibraryManager libraryManager, IUserManager userManager, ILogger logger)
{
_libraryManager = libraryManager;
_userManager = userManager;
_logger = logger;
}
/// <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 allMusicArtists = allItems.OfType<MusicArtist>().ToList();
var allSongs = allItems.OfType<Audio>().ToList();
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(pct => progress.Report(pct * .8));
var allArtists = await GetAllArtists(allSongs, cancellationToken, innerProgress).ConfigureAwait(false);
progress.Report(80);
var numComplete = 0;
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<IHasArtist>>(i.Id, i.RootFolder.GetRecursiveChildren(i).OfType<IHasArtist>().ToList()))
.ToList();
foreach (var artist in allArtists)
{
cancellationToken.ThrowIfCancellationRequested();
artist.ValidateImages();
artist.ValidateBackdrops();
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();
}
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();
}
// Populate counts of items
SetItemCounts(artist, null, allItems.OfType<IHasArtist>());
foreach (var lib in userLibraries)
{
SetItemCounts(artist, lib.Item1, lib.Item2);
}
numComplete++;
double percent = numComplete;
percent /= allArtists.Count;
percent *= 20;
progress.Report(80 + percent);
}
progress.Report(100);
}
/// <summary>
/// Sets the item counts.
/// </summary>
/// <param name="artist">The artist.</param>
/// <param name="userId">The user id.</param>
/// <param name="allItems">All items.</param>
private void SetItemCounts(Artist artist, Guid? userId, IEnumerable<IHasArtist> allItems)
{
var name = artist.Name;
var items = allItems
.Where(i => i.HasArtist(name))
.ToList();
var counts = new ItemByNameCounts
{
TotalCount = items.Count,
SongCount = items.OfType<Audio>().Count(),
AlbumCount = items.OfType<MusicAlbum>().Count(),
MusicVideoCount = items.OfType<MusicVideo>().Count()
};
if (userId.HasValue)
{
artist.UserItemCounts[userId.Value] = counts;
}
else
{
artist.ItemCounts = counts;
}
}
/// <summary>
/// Merges the images.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
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="allSongs">All songs.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task{Artist[]}.</returns>
private async Task<List<Artist>> GetAllArtists(IEnumerable<Audio> allSongs, CancellationToken cancellationToken, IProgress<double> progress)
{
var allArtists = allSongs
.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)
.ToList();
const int maxTasks = 5;
var tasks = new List<Task>();
var returnArtists = new ConcurrentBag<Artist>();
var numComplete = 0;
foreach (var artist in allArtists)
{
if (tasks.Count > maxTasks)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
// Safe cancellation point, when there are no pending tasks
cancellationToken.ThrowIfCancellationRequested();
}
// Avoid accessing the foreach variable within the closure
var currentArtist = artist;
tasks.Add(Task.Run(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var artistItem = await _libraryManager.GetArtist(currentArtist, cancellationToken, true, true)
.ConfigureAwait(false);
returnArtists.Add(artistItem);
}
catch (IOException ex)
{
_logger.ErrorException("Error validating Artist {0}", ex, currentArtist);
}
// Update progress
lock (progress)
{
numComplete++;
double percent = numComplete;
percent /= allArtists.Count;
progress.Report(100 * percent);
}
}));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
return returnArtists.ToList();
}
/// <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;
});
}
}
}

View File

@@ -0,0 +1,155 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
/// <summary>
/// Class CountHelpers
/// </summary>
internal static class CountHelpers
{
/// <summary>
/// Adds to dictionary.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="counts">The counts.</param>
internal static void AddToDictionary(BaseItem item, Dictionary<string, int> counts)
{
if (item is Movie)
{
IncrementCount(counts, "Movie");
}
else if (item is Trailer)
{
IncrementCount(counts, "Trailer");
}
else if (item is Series)
{
IncrementCount(counts, "Series");
}
else if (item is Game)
{
IncrementCount(counts, "Game");
}
else if (item is Audio)
{
IncrementCount(counts, "Audio");
}
else if (item is MusicAlbum)
{
IncrementCount(counts, "MusicAlbum");
}
else if (item is Episode)
{
IncrementCount(counts, "Episode");
}
else if (item is MusicVideo)
{
IncrementCount(counts, "MusicVideo");
}
else if (item is AdultVideo)
{
IncrementCount(counts, "AdultVideo");
}
IncrementCount(counts, "Total");
}
/// <summary>
/// Increments the count.
/// </summary>
/// <param name="counts">The counts.</param>
/// <param name="key">The key.</param>
internal static void IncrementCount(Dictionary<string, int> counts, string key)
{
int count;
if (counts.TryGetValue(key, out count))
{
count++;
counts[key] = count;
}
else
{
counts.Add(key, 1);
}
}
/// <summary>
/// Gets the counts.
/// </summary>
/// <param name="counts">The counts.</param>
/// <returns>ItemByNameCounts.</returns>
internal static ItemByNameCounts GetCounts(Dictionary<string, int> counts)
{
return new ItemByNameCounts
{
AdultVideoCount = GetCount(counts, "AdultVideo"),
AlbumCount = GetCount(counts, "MusicAlbum"),
EpisodeCount = GetCount(counts, "Episode"),
GameCount = GetCount(counts, "Game"),
MovieCount = GetCount(counts, "Movie"),
MusicVideoCount = GetCount(counts, "MusicVideo"),
SeriesCount = GetCount(counts, "Series"),
SongCount = GetCount(counts, "Audio"),
TrailerCount = GetCount(counts, "Trailer"),
TotalCount = GetCount(counts, "Total")
};
}
/// <summary>
/// Gets the count.
/// </summary>
/// <param name="counts">The counts.</param>
/// <param name="key">The key.</param>
/// <returns>System.Int32.</returns>
internal static int GetCount(Dictionary<string, int> counts, string key)
{
int count;
if (counts.TryGetValue(key, out count))
{
return count;
}
return 0;
}
/// <summary>
/// Sets the item counts.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="media">The media.</param>
/// <param name="names">The names.</param>
/// <param name="masterDictionary">The master dictionary.</param>
internal static void SetItemCounts(Guid? userId, BaseItem media, List<string> names, Dictionary<string, Dictionary<Guid, Dictionary<string, int>>> masterDictionary)
{
foreach (var name in names)
{
Dictionary<Guid, Dictionary<string, int>> libraryCounts;
if (!masterDictionary.TryGetValue(name, out libraryCounts))
{
libraryCounts = new Dictionary<Guid, Dictionary<string, int>>();
masterDictionary.Add(name, libraryCounts);
}
var userLibId = userId ?? Guid.Empty;
Dictionary<string, int> userDictionary;
if (!libraryCounts.TryGetValue(userLibId, out userDictionary))
{
userDictionary = new Dictionary<string, int>();
libraryCounts.Add(userLibId, userDictionary);
}
AddToDictionary(media, userDictionary);
}
}
}
}

View File

@@ -0,0 +1,45 @@
using MediaBrowser.Controller.Library;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
/// <summary>
/// Class GameGenresPostScanTask
/// </summary>
public class GameGenresPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="GameGenresPostScanTask"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
public GameGenresPostScanTask(ILibraryManager libraryManager, IUserManager userManager)
{
_libraryManager = libraryManager;
_userManager = userManager;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidateGameGenres(cancellationToken, progress);
}
}
}

View File

@@ -0,0 +1,132 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
class GameGenresValidator
{
/// <summary>
/// The _library manager
/// </summary>
private readonly LibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
public GameGenresValidator(LibraryManager libraryManager, IUserManager userManager, ILogger logger)
{
_libraryManager = libraryManager;
_userManager = userManager;
_logger = logger;
}
/// <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.OfType<Game>().ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<Game>>(i.Id, i.RootFolder.GetRecursiveChildren(i).OfType<Game>().ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<string, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
SetItemCounts(null, allLibraryItems, masterDictionary);
progress.Report(2);
var numComplete = 0;
foreach (var lib in userLibraries)
{
SetItemCounts(lib.Item1, lib.Item2, masterDictionary);
numComplete++;
double percent = numComplete;
percent /= userLibraries.Count;
percent *= 8;
progress.Report(percent);
}
progress.Report(10);
var names = masterDictionary.Keys.ToList();
numComplete = 0;
foreach (var name in names)
{
try
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
}
numComplete++;
double percent = numComplete;
percent /= names.Count;
percent *= 90;
progress.Report(percent + 10);
}
progress.Report(100);
}
private async Task UpdateItemByNameCounts(string name, CancellationToken cancellationToken, Dictionary<Guid, Dictionary<string, int>> counts)
{
var itemByName = await _libraryManager.GetGameGenre(name, cancellationToken, true, true).ConfigureAwait(false);
foreach (var libraryId in counts.Keys.ToList())
{
var itemCounts = CountHelpers.GetCounts(counts[libraryId]);
if (libraryId == Guid.Empty)
{
itemByName.ItemCounts = itemCounts;
}
else
{
itemByName.UserItemCounts[libraryId] = itemCounts;
}
}
}
private void SetItemCounts(Guid? userId, IEnumerable<BaseItem> allItems, Dictionary<string, Dictionary<Guid, Dictionary<string, int>>> masterDictionary)
{
foreach (var media in allItems)
{
var names = media
.Genres
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
CountHelpers.SetItemCounts(userId, media, names, masterDictionary);
}
}
}
}

View File

@@ -0,0 +1,42 @@
using MediaBrowser.Controller.Library;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
public class GenresPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
public GenresPostScanTask(ILibraryManager libraryManager, IUserManager userManager)
{
_libraryManager = libraryManager;
_userManager = userManager;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidateGenres(cancellationToken, progress);
}
}
}

View File

@@ -0,0 +1,135 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
class GenresValidator
{
/// <summary>
/// The _library manager
/// </summary>
private readonly LibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
public GenresValidator(LibraryManager libraryManager, IUserManager userManager, ILogger logger)
{
_libraryManager = libraryManager;
_userManager = userManager;
_logger = logger;
}
/// <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
.Where(i => !(i is IHasMusicGenres) && !(i is Game))
.ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).Where(m => !(m is IHasMusicGenres) && !(m is Game)).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<string, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
SetItemCounts(null, allLibraryItems, masterDictionary);
progress.Report(2);
var numComplete = 0;
foreach (var lib in userLibraries)
{
SetItemCounts(lib.Item1, lib.Item2, masterDictionary);
numComplete++;
double percent = numComplete;
percent /= userLibraries.Count;
percent *= 8;
progress.Report(percent);
}
progress.Report(10);
var names = masterDictionary.Keys.ToList();
numComplete = 0;
foreach (var name in names)
{
try
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
}
numComplete++;
double percent = numComplete;
percent /= names.Count;
percent *= 90;
progress.Report(percent + 10);
}
progress.Report(100);
}
private async Task UpdateItemByNameCounts(string name, CancellationToken cancellationToken, Dictionary<Guid, Dictionary<string, int>> counts)
{
var itemByName = await _libraryManager.GetGenre(name, cancellationToken, true, true).ConfigureAwait(false);
foreach (var libraryId in counts.Keys.ToList())
{
var itemCounts = CountHelpers.GetCounts(counts[libraryId]);
if (libraryId == Guid.Empty)
{
itemByName.ItemCounts = itemCounts;
}
else
{
itemByName.UserItemCounts[libraryId] = itemCounts;
}
}
}
private void SetItemCounts(Guid? userId, IEnumerable<BaseItem> allItems, Dictionary<string, Dictionary<Guid, Dictionary<string, int>>> masterDictionary)
{
foreach (var media in allItems)
{
var names = media
.Genres
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
CountHelpers.SetItemCounts(userId, media, names, masterDictionary);
}
}
}
}

View File

@@ -0,0 +1,45 @@
using MediaBrowser.Controller.Library;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
/// <summary>
/// Class MusicGenresPostScanTask
/// </summary>
public class MusicGenresPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
public MusicGenresPostScanTask(ILibraryManager libraryManager, IUserManager userManager)
{
_libraryManager = libraryManager;
_userManager = userManager;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidateMusicGenres(cancellationToken, progress);
}
}
}

View File

@@ -0,0 +1,135 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
class MusicGenresValidator
{
/// <summary>
/// The _library manager
/// </summary>
private readonly LibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
public MusicGenresValidator(LibraryManager libraryManager, IUserManager userManager, ILogger logger)
{
_libraryManager = libraryManager;
_userManager = userManager;
_logger = logger;
}
/// <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
.Where(i => i is IHasMusicGenres)
.ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).Where(m => m is IHasMusicGenres).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<string, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
SetItemCounts(null, allLibraryItems, masterDictionary);
progress.Report(2);
var numComplete = 0;
foreach (var lib in userLibraries)
{
SetItemCounts(lib.Item1, lib.Item2, masterDictionary);
numComplete++;
double percent = numComplete;
percent /= userLibraries.Count;
percent *= 8;
progress.Report(percent);
}
progress.Report(10);
var names = masterDictionary.Keys.ToList();
numComplete = 0;
foreach (var name in names)
{
try
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
}
numComplete++;
double percent = numComplete;
percent /= names.Count;
percent *= 90;
progress.Report(percent + 10);
}
progress.Report(100);
}
private async Task UpdateItemByNameCounts(string name, CancellationToken cancellationToken, Dictionary<Guid, Dictionary<string, int>> counts)
{
var itemByName = await _libraryManager.GetMusicGenre(name, cancellationToken, true, true).ConfigureAwait(false);
foreach (var libraryId in counts.Keys.ToList())
{
var itemCounts = CountHelpers.GetCounts(counts[libraryId]);
if (libraryId == Guid.Empty)
{
itemByName.ItemCounts = itemCounts;
}
else
{
itemByName.UserItemCounts[libraryId] = itemCounts;
}
}
}
private void SetItemCounts(Guid? userId, IEnumerable<BaseItem> allItems, Dictionary<string, Dictionary<Guid, Dictionary<string, int>>> masterDictionary)
{
foreach (var media in allItems)
{
var names = media
.Genres
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
CountHelpers.SetItemCounts(userId, media, names, masterDictionary);
}
}
}
}

View File

@@ -0,0 +1,137 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
class PeoplePostScanTask : ILibraryPostScanTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
public PeoplePostScanTask(ILibraryManager libraryManager, IUserManager userManager, ILogger logger)
{
_libraryManager = libraryManager;
_userManager = userManager;
_logger = logger;
}
/// <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 userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<string, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
SetItemCounts(null, allLibraryItems, masterDictionary);
progress.Report(2);
var numComplete = 0;
foreach (var lib in userLibraries)
{
cancellationToken.ThrowIfCancellationRequested();
SetItemCounts(lib.Item1, lib.Item2, masterDictionary);
numComplete++;
double percent = numComplete;
percent /= userLibraries.Count;
percent *= 8;
progress.Report(percent);
}
progress.Report(10);
var names = masterDictionary.Keys.ToList();
numComplete = 0;
foreach (var name in names)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
await UpdateItemByNameCounts(name, masterDictionary[name]).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
}
numComplete++;
double percent = numComplete;
percent /= names.Count;
percent *= 90;
progress.Report(percent + 10);
}
progress.Report(100);
}
private async Task UpdateItemByNameCounts(string name, Dictionary<Guid, Dictionary<string, int>> counts)
{
var itemByName = await _libraryManager.GetPerson(name).ConfigureAwait(false);
foreach (var libraryId in counts.Keys.ToList())
{
var itemCounts = CountHelpers.GetCounts(counts[libraryId]);
if (libraryId == Guid.Empty)
{
itemByName.ItemCounts = itemCounts;
}
else
{
itemByName.UserItemCounts[libraryId] = itemCounts;
}
}
}
private void SetItemCounts(Guid? userId, IEnumerable<BaseItem> allItems, Dictionary<string, Dictionary<Guid, Dictionary<string, int>>> masterDictionary)
{
foreach (var media in allItems)
{
var names = media
.People.Select(i => i.Name)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
CountHelpers.SetItemCounts(userId, media, names, masterDictionary);
}
}
}
}

View File

@@ -0,0 +1,45 @@
using MediaBrowser.Controller.Library;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
/// <summary>
/// Class MusicGenresPostScanTask
/// </summary>
public class StudiosPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
public StudiosPostScanTask(ILibraryManager libraryManager, IUserManager userManager)
{
_libraryManager = libraryManager;
_userManager = userManager;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidateStudios(cancellationToken, progress);
}
}
}

View File

@@ -0,0 +1,132 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
class StudiosValidator
{
/// <summary>
/// The _library manager
/// </summary>
private readonly LibraryManager _libraryManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
public StudiosValidator(LibraryManager libraryManager, IUserManager userManager, ILogger logger)
{
_libraryManager = libraryManager;
_userManager = userManager;
_logger = logger;
}
/// <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 userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<string, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
SetItemCounts(null, allLibraryItems, masterDictionary);
progress.Report(2);
var numComplete = 0;
foreach (var lib in userLibraries)
{
SetItemCounts(lib.Item1, lib.Item2, masterDictionary);
numComplete++;
double percent = numComplete;
percent /= userLibraries.Count;
percent *= 8;
progress.Report(percent);
}
progress.Report(10);
var names = masterDictionary.Keys.ToList();
numComplete = 0;
foreach (var name in names)
{
try
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
}
numComplete++;
double percent = numComplete;
percent /= names.Count;
percent *= 90;
progress.Report(percent + 10);
}
progress.Report(100);
}
private async Task UpdateItemByNameCounts(string name, CancellationToken cancellationToken, Dictionary<Guid, Dictionary<string, int>> counts)
{
var itemByName = await _libraryManager.GetStudio(name, cancellationToken, true, true).ConfigureAwait(false);
foreach (var libraryId in counts.Keys.ToList())
{
var itemCounts = CountHelpers.GetCounts(counts[libraryId]);
if (libraryId == Guid.Empty)
{
itemByName.ItemCounts = itemCounts;
}
else
{
itemByName.UserItemCounts[libraryId] = itemCounts;
}
}
}
private void SetItemCounts(Guid? userId, IEnumerable<BaseItem> allItems, Dictionary<string, Dictionary<Guid, Dictionary<string, int>>> masterDictionary)
{
foreach (var media in allItems)
{
var names = media
.Studios
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
CountHelpers.SetItemCounts(userId, media, names, masterDictionary);
}
}
}
}

View File

@@ -146,6 +146,18 @@
<Compile Include="Library\Resolvers\TV\SeriesResolver.cs" />
<Compile Include="Library\Resolvers\VideoResolver.cs" />
<Compile Include="Library\UserManager.cs" />
<Compile Include="Library\Validators\ArtistsPostScanTask.cs" />
<Compile Include="Library\Validators\ArtistsValidator.cs" />
<Compile Include="Library\Validators\CountHelpers.cs" />
<Compile Include="Library\Validators\GameGenresPostScanTask.cs" />
<Compile Include="Library\Validators\GameGenresValidator.cs" />
<Compile Include="Library\Validators\GenresPostScanTask.cs" />
<Compile Include="Library\Validators\GenresValidator.cs" />
<Compile Include="Library\Validators\MusicGenresPostScanTask.cs" />
<Compile Include="Library\Validators\MusicGenresValidator.cs" />
<Compile Include="Library\Validators\PeoplePostScanTask.cs" />
<Compile Include="Library\Validators\StudiosPostScanTask.cs" />
<Compile Include="Library\Validators\StudiosValidator.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
<Compile Include="MediaEncoder\MediaEncoder.cs" />
<Compile Include="Persistence\SqliteChapterRepository.cs" />
@@ -155,7 +167,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\ImageSaver.cs" />
<Compile Include="Providers\ProviderManager.cs" />
<Compile Include="ScheduledTasks\ArtistValidationTask.cs" />
<Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
<Compile Include="ScheduledTasks\ChapterImagesTask.cs" />
<Compile Include="ScheduledTasks\PluginUpdateTask.cs" />

View File

@@ -189,7 +189,11 @@ namespace MediaBrowser.Server.Implementations.Providers
cancellationToken.ThrowIfCancellationRequested();
_logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name ?? "--Unknown--");
// Don't clog up the log with these providers
if (!(provider is IDynamicInfoProvider))
{
_logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name ?? "--Unknown--");
}
// This provides the ability to cancel just this one provider
var innerCancellationTokenSource = new CancellationTokenSource();

View File

@@ -1,81 +0,0 @@
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Library;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.ScheduledTasks
{
public class ArtistValidationTask
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
public ArtistValidationTask(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Creates the triggers that define when the task will run
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
{
return new ITaskTrigger[]
{
new DailyTrigger { TimeOfDay = TimeSpan.FromHours(5) },
new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
};
}
/// <summary>
/// Returns the task to be executed
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
return _libraryManager.ValidateArtists(cancellationToken, progress);
}
/// <summary>
/// Gets the name of the task
/// </summary>
/// <value>The name.</value>
public string Name
{
get { return "Refresh music artists"; }
}
/// <summary>
/// Gets the description.
/// </summary>
/// <value>The description.</value>
public string Description
{
get { return "Updates metadata for music artists in your media library."; }
}
/// <summary>
/// Gets the category.
/// </summary>
/// <value>The category.</value>
public string Category
{
get
{
return "Library";
}
}
}
}

View File

@@ -85,11 +85,6 @@ namespace MediaBrowser.Server.Implementations.Session
get { return _activeConnections.Values.OrderByDescending(c => c.LastActivityDate).ToList(); }
}
/// <summary>
/// The _true task result
/// </summary>
private readonly Task _trueTaskResult = Task.FromResult(true);
/// <summary>
/// Logs the user activity.
/// </summary>
@@ -339,6 +334,7 @@ namespace MediaBrowser.Server.Implementations.Session
// If the client isn't able to report this, then we'll just have to make an assumption
data.PlayCount++;
data.Played = true;
data.PlaybackPositionTicks = 0;
}
await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);