Merge pull request #14028 from Shadowghost/cleanup-tasks

Cleanup Tasks and Validators
This commit is contained in:
Bond-009 2025-05-02 14:47:53 +02:00 committed by GitHub
commit b4a58ee13a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 2460 additions and 2502 deletions

View File

@ -5,45 +5,44 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class ArtistsPostScanTask.
/// </summary>
public class ArtistsPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// Class ArtistsPostScanTask.
/// The _library manager.
/// </summary>
public class ArtistsPostScanTask : ILibraryPostScanTask
private readonly ILibraryManager _libraryManager;
private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public ArtistsPostScanTask(
ILibraryManager libraryManager,
ILogger<ArtistsValidator> logger,
IItemRepository itemRepo)
{
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public ArtistsPostScanTask(
ILibraryManager libraryManager,
ILogger<ArtistsValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 new ArtistsValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
/// <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 new ArtistsValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}

View File

@ -10,102 +10,101 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class ArtistsValidator.
/// </summary>
public class ArtistsValidator
{
/// <summary>
/// Class ArtistsValidator.
/// The library manager.
/// </summary>
public class ArtistsValidator
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> logger, IItemRepository itemRepo)
{
/// <summary>
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <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 names = _itemRepo.GetAllArtistNames();
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> logger, IItemRepository itemRepo)
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 names = _itemRepo.GetAllArtistNames();
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
try
{
try
{
var item = _libraryManager.GetArtist(name);
var item = _libraryManager.GetArtist(name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {ArtistName}", name);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {ArtistName}", name);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.MusicArtist],
IsDeadArtist = true,
IsLocked = false
}).Cast<MusicArtist>().ToList();
foreach (var item in deadEntities)
{
if (!item.IsAccessedByName)
{
IncludeItemTypes = new[] { BaseItemKind.MusicArtist },
IsDeadArtist = true,
IsLocked = false
}).Cast<MusicArtist>().ToList();
foreach (var item in deadEntities)
{
if (!item.IsAccessedByName)
{
continue;
}
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
continue;
}
progress.Report(100);
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
}
}

View File

@ -9,149 +9,146 @@ using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class CollectionPostScanTask.
/// </summary>
public class CollectionPostScanTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
private readonly ICollectionManager _collectionManager;
private readonly ILogger<CollectionPostScanTask> _logger;
/// <summary>
/// Class CollectionPostScanTask.
/// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
/// </summary>
public class CollectionPostScanTask : ILibraryPostScanTask
/// <param name="libraryManager">The library manager.</param>
/// <param name="collectionManager">The collection manager.</param>
/// <param name="logger">The logger.</param>
public CollectionPostScanTask(
ILibraryManager libraryManager,
ICollectionManager collectionManager,
ILogger<CollectionPostScanTask> logger)
{
private readonly ILibraryManager _libraryManager;
private readonly ICollectionManager _collectionManager;
private readonly ILogger<CollectionPostScanTask> _logger;
_libraryManager = libraryManager;
_collectionManager = collectionManager;
_logger = logger;
}
/// <summary>
/// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="collectionManager">The collection manager.</param>
/// <param name="logger">The logger.</param>
public CollectionPostScanTask(
ILibraryManager libraryManager,
ICollectionManager collectionManager,
ILogger<CollectionPostScanTask> 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 collectionNameMoviesMap = new Dictionary<string, HashSet<Guid>>();
foreach (var library in _libraryManager.RootFolder.Children)
{
_libraryManager = libraryManager;
_collectionManager = collectionManager;
_logger = logger;
if (!_libraryManager.GetLibraryOptions(library).AutomaticallyAddToCollection)
{
continue;
}
var startIndex = 0;
var pagesize = 1000;
while (true)
{
var movies = _libraryManager.GetItemList(new InternalItemsQuery
{
MediaTypes = [MediaType.Video],
IncludeItemTypes = [BaseItemKind.Movie],
IsVirtualItem = false,
OrderBy = [(ItemSortBy.SortName, SortOrder.Ascending)],
Parent = library,
StartIndex = startIndex,
Limit = pagesize,
Recursive = true
});
foreach (var m in movies)
{
if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
{
if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
{
movieList.Add(movie.Id);
}
else
{
collectionNameMoviesMap[movie.CollectionName] = new HashSet<Guid> { movie.Id };
}
}
}
if (movies.Count < pagesize)
{
break;
}
startIndex += pagesize;
}
}
/// <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 numComplete = 0;
var count = collectionNameMoviesMap.Count;
if (count == 0)
{
var collectionNameMoviesMap = new Dictionary<string, HashSet<Guid>>();
foreach (var library in _libraryManager.RootFolder.Children)
{
if (!_libraryManager.GetLibraryOptions(library).AutomaticallyAddToCollection)
{
continue;
}
var startIndex = 0;
var pagesize = 1000;
while (true)
{
var movies = _libraryManager.GetItemList(new InternalItemsQuery
{
MediaTypes = new[] { MediaType.Video },
IncludeItemTypes = new[] { BaseItemKind.Movie },
IsVirtualItem = false,
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
Parent = library,
StartIndex = startIndex,
Limit = pagesize,
Recursive = true
});
foreach (var m in movies)
{
if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
{
if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
{
movieList.Add(movie.Id);
}
else
{
collectionNameMoviesMap[movie.CollectionName] = new HashSet<Guid> { movie.Id };
}
}
}
if (movies.Count < pagesize)
{
break;
}
startIndex += pagesize;
}
}
var numComplete = 0;
var count = collectionNameMoviesMap.Count;
if (count == 0)
{
progress.Report(100);
return;
}
var boxSets = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.BoxSet },
CollapseBoxSetItems = false,
Recursive = true
});
foreach (var (collectionName, movieIds) in collectionNameMoviesMap)
{
try
{
var boxSet = boxSets.FirstOrDefault(b => b?.Name == collectionName) as BoxSet;
if (boxSet is null)
{
// won't automatically create collection if only one movie in it
if (movieIds.Count >= 2)
{
boxSet = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
{
Name = collectionName,
IsLocked = true
});
await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
}
}
else
{
await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {CollectionName} with {@MovieIds}", collectionName, movieIds);
}
}
progress.Report(100);
return;
}
var boxSets = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.BoxSet],
CollapseBoxSetItems = false,
Recursive = true
});
foreach (var (collectionName, movieIds) in collectionNameMoviesMap)
{
try
{
var boxSet = boxSets.FirstOrDefault(b => b?.Name == collectionName) as BoxSet;
if (boxSet is null)
{
// won't automatically create collection if only one movie in it
if (movieIds.Count >= 2)
{
boxSet = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
{
Name = collectionName,
IsLocked = true
}).ConfigureAwait(false);
await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds).ConfigureAwait(false);
}
}
else
{
await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds).ConfigureAwait(false);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {CollectionName} with {@MovieIds}", collectionName, movieIds);
}
}
progress.Report(100);
}
}

View File

@ -5,45 +5,44 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class GenresPostScanTask.
/// </summary>
public class GenresPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// Class GenresPostScanTask.
/// The _library manager.
/// </summary>
public class GenresPostScanTask : ILibraryPostScanTask
private readonly ILibraryManager _libraryManager;
private readonly ILogger<GenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="GenresPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public GenresPostScanTask(
ILibraryManager libraryManager,
ILogger<GenresValidator> logger,
IItemRepository itemRepo)
{
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger<GenresValidator> _logger;
private readonly IItemRepository _itemRepo;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// Initializes a new instance of the <see cref="GenresPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public GenresPostScanTask(
ILibraryManager libraryManager,
ILogger<GenresValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
/// <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 new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}

View File

@ -8,97 +8,96 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class GenresValidator.
/// </summary>
public class GenresValidator
{
/// <summary>
/// Class GenresValidator.
/// The library manager.
/// </summary>
public class GenresValidator
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<GenresValidator> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="GenresValidator"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public GenresValidator(ILibraryManager libraryManager, ILogger<GenresValidator> logger, IItemRepository itemRepo)
{
/// <summary>
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<GenresValidator> _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 names = _itemRepo.GetGenreNames();
/// <summary>
/// Initializes a new instance of the <see cref="GenresValidator"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public GenresValidator(ILibraryManager libraryManager, ILogger<GenresValidator> logger, IItemRepository itemRepo)
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 names = _itemRepo.GetGenreNames();
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
try
{
try
{
var item = _libraryManager.GetGenre(name);
var item = _libraryManager.GetGenre(name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {GenreName}", name);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {GenreName}", name);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Genre, BaseItemKind.MusicGenre],
IsDeadGenre = true,
IsLocked = false
});
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
progress.Report(percent);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Genre, BaseItemKind.MusicGenre],
IsDeadGenre = true,
IsLocked = false
});
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
}
}

View File

@ -5,45 +5,44 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class MusicGenresPostScanTask.
/// </summary>
public class MusicGenresPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// Class MusicGenresPostScanTask.
/// The library manager.
/// </summary>
public class MusicGenresPostScanTask : ILibraryPostScanTask
private readonly ILibraryManager _libraryManager;
private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="MusicGenresPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public MusicGenresPostScanTask(
ILibraryManager libraryManager,
ILogger<MusicGenresValidator> logger,
IItemRepository itemRepo)
{
/// <summary>
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// Initializes a new instance of the <see cref="MusicGenresPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public MusicGenresPostScanTask(
ILibraryManager libraryManager,
ILogger<MusicGenresValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
/// <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 new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}

View File

@ -5,77 +5,76 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class MusicGenresValidator.
/// </summary>
public class MusicGenresValidator
{
/// <summary>
/// Class MusicGenresValidator.
/// The library manager.
/// </summary>
public class MusicGenresValidator
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="MusicGenresValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public MusicGenresValidator(ILibraryManager libraryManager, ILogger<MusicGenresValidator> logger, IItemRepository itemRepo)
{
/// <summary>
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <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 names = _itemRepo.GetMusicGenreNames();
/// <summary>
/// Initializes a new instance of the <see cref="MusicGenresValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public MusicGenresValidator(ILibraryManager libraryManager, ILogger<MusicGenresValidator> logger, IItemRepository itemRepo)
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 names = _itemRepo.GetMusicGenreNames();
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
try
{
try
{
var item = _libraryManager.GetMusicGenre(name);
var item = _libraryManager.GetMusicGenre(name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {GenreName}", name);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {GenreName}", name);
}
progress.Report(100);
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
}
progress.Report(100);
}
}

View File

@ -9,119 +9,114 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class PeopleValidator.
/// </summary>
public class PeopleValidator
{
/// <summary>
/// Class PeopleValidator.
/// The _library manager.
/// </summary>
public class PeopleValidator
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The _logger.
/// </summary>
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="fileSystem">The file system.</param>
public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
{
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
_libraryManager = libraryManager;
_logger = logger;
_fileSystem = fileSystem;
}
/// <summary>
/// The _logger.
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Validates the people.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{
var people = _libraryManager.GetPeopleNames(new InternalPeopleQuery());
private readonly IFileSystem _fileSystem;
var numComplete = 0;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="fileSystem">The file system.</param>
public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
var numPeople = people.Count;
_logger.LogDebug("Will refresh {Amount} people", numPeople);
foreach (var person in people)
{
_libraryManager = libraryManager;
_logger = logger;
_fileSystem = fileSystem;
}
cancellationToken.ThrowIfCancellationRequested();
/// <summary>
/// Validates the people.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{
var people = _libraryManager.GetPeopleNames(new InternalPeopleQuery());
var numComplete = 0;
var numPeople = people.Count;
_logger.LogDebug("Will refresh {0} people", numPeople);
foreach (var person in people)
try
{
cancellationToken.ThrowIfCancellationRequested();
try
var item = _libraryManager.GetPerson(person);
if (item is null)
{
var item = _libraryManager.GetPerson(person);
if (item is null)
{
_logger.LogWarning("Failed to get person: {Name}", person);
continue;
}
var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
ImageRefreshMode = MetadataRefreshMode.ValidationOnly,
MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
};
await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error validating IBN entry {Person}", person);
_logger.LogWarning("Failed to get person: {Name}", person);
continue;
}
// Update progress
numComplete++;
double percent = numComplete;
percent /= numPeople;
var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
ImageRefreshMode = MetadataRefreshMode.ValidationOnly,
MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
};
progress.Report(100 * percent);
await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error validating IBN entry {Person}", person);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Person],
IsDeadPerson = true,
IsLocked = false
});
// Update progress
numComplete++;
double percent = numComplete;
percent /= numPeople;
foreach (var item in deadEntities)
{
_logger.LogInformation(
"Deleting dead {2} {0} {1}.",
item.Id.ToString("N", CultureInfo.InvariantCulture),
item.Name,
item.GetType().Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
_logger.LogInformation("People validation complete");
progress.Report(100 * percent);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Person],
IsDeadPerson = true,
IsLocked = false
});
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
_logger.LogInformation("People validation complete");
}
}

View File

@ -5,46 +5,45 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class MusicGenresPostScanTask.
/// </summary>
public class StudiosPostScanTask : ILibraryPostScanTask
{
/// <summary>
/// Class MusicGenresPostScanTask.
/// The _library manager.
/// </summary>
public class StudiosPostScanTask : ILibraryPostScanTask
private readonly ILibraryManager _libraryManager;
private readonly ILogger<StudiosValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="StudiosPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public StudiosPostScanTask(
ILibraryManager libraryManager,
ILogger<StudiosValidator> logger,
IItemRepository itemRepo)
{
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
private readonly ILogger<StudiosValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="StudiosPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public StudiosPostScanTask(
ILibraryManager libraryManager,
ILogger<StudiosValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 new StudiosValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
/// <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 new StudiosValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}

View File

@ -8,98 +8,97 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators
namespace Emby.Server.Implementations.Library.Validators;
/// <summary>
/// Class StudiosValidator.
/// </summary>
public class StudiosValidator
{
/// <summary>
/// Class StudiosValidator.
/// The library manager.
/// </summary>
public class StudiosValidator
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<StudiosValidator> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="StudiosValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public StudiosValidator(ILibraryManager libraryManager, ILogger<StudiosValidator> logger, IItemRepository itemRepo)
{
/// <summary>
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
private readonly IItemRepository _itemRepo;
/// <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 names = _itemRepo.GetStudioNames();
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<StudiosValidator> _logger;
var numComplete = 0;
var count = names.Count;
/// <summary>
/// Initializes a new instance of the <see cref="StudiosValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
public StudiosValidator(ILibraryManager libraryManager, ILogger<StudiosValidator> logger, IItemRepository itemRepo)
foreach (var name in names)
{
_libraryManager = libraryManager;
_logger = logger;
_itemRepo = itemRepo;
}
/// <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 names = _itemRepo.GetStudioNames();
var numComplete = 0;
var count = names.Count;
foreach (var name in names)
try
{
try
{
var item = _libraryManager.GetStudio(name);
var item = _libraryManager.GetStudio(name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {StudioName}", name);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {StudioName}", name);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Studio },
IsDeadStudio = true,
IsLocked = false
});
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
progress.Report(percent);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Studio],
IsDeadStudio = true,
IsLocked = false
});
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
progress.Report(100);
}
}

View File

@ -8,255 +8,254 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks
namespace Emby.Server.Implementations.ScheduledTasks;
/// <summary>
/// Class TaskManager.
/// </summary>
public class TaskManager : ITaskManager
{
/// <summary>
/// Class TaskManager.
/// The _task queue.
/// </summary>
public class TaskManager : ITaskManager
private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
new ConcurrentQueue<Tuple<Type, TaskOptions>>();
private readonly IApplicationPaths _applicationPaths;
private readonly ILogger<TaskManager> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="TaskManager" /> class.
/// </summary>
/// <param name="applicationPaths">The application paths.</param>
/// <param name="logger">The logger.</param>
public TaskManager(
IApplicationPaths applicationPaths,
ILogger<TaskManager> logger)
{
/// <summary>
/// The _task queue.
/// </summary>
private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
new ConcurrentQueue<Tuple<Type, TaskOptions>>();
_applicationPaths = applicationPaths;
_logger = logger;
private readonly IApplicationPaths _applicationPaths;
private readonly ILogger<TaskManager> _logger;
ScheduledTasks = [];
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskManager" /> class.
/// </summary>
/// <param name="applicationPaths">The application paths.</param>
/// <param name="logger">The logger.</param>
public TaskManager(
IApplicationPaths applicationPaths,
ILogger<TaskManager> logger)
{
_applicationPaths = applicationPaths;
_logger = logger;
/// <inheritdoc />
public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
}
/// <inheritdoc />
public event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
/// <inheritdoc />
public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
/// <inheritdoc />
public IReadOnlyList<IScheduledTaskWorker> ScheduledTasks { get; private set; }
/// <inheritdoc />
public event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
/// <inheritdoc />
public void CancelIfRunningAndQueue<T>(TaskOptions options)
where T : IScheduledTask
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
((ScheduledTaskWorker)task).CancelIfRunning();
/// <inheritdoc />
public IReadOnlyList<IScheduledTaskWorker> ScheduledTasks { get; private set; }
QueueScheduledTask<T>(options);
}
/// <inheritdoc />
public void CancelIfRunningAndQueue<T>(TaskOptions options)
/// <inheritdoc />
public void CancelIfRunningAndQueue<T>()
where T : IScheduledTask
{
CancelIfRunningAndQueue<T>(new TaskOptions());
}
/// <inheritdoc />
public void CancelIfRunning<T>()
where T : IScheduledTask
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
((ScheduledTaskWorker)task).CancelIfRunning();
}
/// <inheritdoc />
public void QueueScheduledTask<T>(TaskOptions options)
where T : IScheduledTask
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
if (scheduledTask is null)
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
((ScheduledTaskWorker)task).CancelIfRunning();
QueueScheduledTask<T>(options);
_logger.LogError("Unable to find scheduled task of type {Type} in QueueScheduledTask.", typeof(T).Name);
}
/// <inheritdoc />
public void CancelIfRunningAndQueue<T>()
where T : IScheduledTask
else
{
CancelIfRunningAndQueue<T>(new TaskOptions());
QueueScheduledTask(scheduledTask, options);
}
}
/// <inheritdoc />
public void CancelIfRunning<T>()
where T : IScheduledTask
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
((ScheduledTaskWorker)task).CancelIfRunning();
}
/// <inheritdoc />
public void QueueScheduledTask<T>()
where T : IScheduledTask
{
QueueScheduledTask<T>(new TaskOptions());
}
/// <inheritdoc />
public void QueueScheduledTask<T>(TaskOptions options)
where T : IScheduledTask
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
/// <inheritdoc />
public void QueueIfNotRunning<T>()
where T : IScheduledTask
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
if (scheduledTask is null)
{
_logger.LogError("Unable to find scheduled task of type {0} in QueueScheduledTask.", typeof(T).Name);
}
else
{
QueueScheduledTask(scheduledTask, options);
}
}
/// <inheritdoc />
public void QueueScheduledTask<T>()
where T : IScheduledTask
if (task.State != TaskState.Running)
{
QueueScheduledTask<T>(new TaskOptions());
}
}
/// <inheritdoc />
public void QueueIfNotRunning<T>()
where T : IScheduledTask
/// <inheritdoc />
public void Execute<T>()
where T : IScheduledTask
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
if (scheduledTask is null)
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
if (task.State != TaskState.Running)
{
QueueScheduledTask<T>(new TaskOptions());
}
_logger.LogError("Unable to find scheduled task of type {Type} in Execute.", typeof(T).Name);
}
/// <inheritdoc />
public void Execute<T>()
where T : IScheduledTask
else
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
var type = scheduledTask.ScheduledTask.GetType();
if (scheduledTask is null)
{
_logger.LogError("Unable to find scheduled task of type {0} in Execute.", typeof(T).Name);
}
else
{
var type = scheduledTask.ScheduledTask.GetType();
_logger.LogDebug("Queuing task {0}", type.Name);
lock (_taskQueue)
{
if (scheduledTask.State == TaskState.Idle)
{
Execute(scheduledTask, new TaskOptions());
}
}
}
}
/// <inheritdoc />
public void QueueScheduledTask(IScheduledTask task, TaskOptions options)
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
if (scheduledTask is null)
{
_logger.LogError("Unable to find scheduled task of type {0} in QueueScheduledTask.", task.GetType().Name);
}
else
{
QueueScheduledTask(scheduledTask, options);
}
}
/// <summary>
/// Queues the scheduled task.
/// </summary>
/// <param name="task">The task.</param>
/// <param name="options">The task options.</param>
private void QueueScheduledTask(IScheduledTaskWorker task, TaskOptions options)
{
var type = task.ScheduledTask.GetType();
_logger.LogDebug("Queuing task {0}", type.Name);
_logger.LogDebug("Queuing task {Name}", type.Name);
lock (_taskQueue)
{
if (task.State == TaskState.Idle)
if (scheduledTask.State == TaskState.Idle)
{
Execute(task, options);
return;
Execute(scheduledTask, new TaskOptions());
}
_taskQueue.Enqueue(new Tuple<Type, TaskOptions>(type, options));
}
}
}
/// <inheritdoc />
public void AddTasks(IEnumerable<IScheduledTask> tasks)
/// <inheritdoc />
public void QueueScheduledTask(IScheduledTask task, TaskOptions options)
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
if (scheduledTask is null)
{
var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _logger));
ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
_logger.LogError("Unable to find scheduled task of type {Type} in QueueScheduledTask.", task.GetType().Name);
}
/// <inheritdoc />
public void Dispose()
else
{
Dispose(true);
GC.SuppressFinalize(this);
QueueScheduledTask(scheduledTask, options);
}
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
/// <summary>
/// Queues the scheduled task.
/// </summary>
/// <param name="task">The task.</param>
/// <param name="options">The task options.</param>
private void QueueScheduledTask(IScheduledTaskWorker task, TaskOptions options)
{
var type = task.ScheduledTask.GetType();
_logger.LogDebug("Queuing task {Name}", type.Name);
lock (_taskQueue)
{
foreach (var task in ScheduledTasks)
if (task.State == TaskState.Idle)
{
task.Dispose();
Execute(task, options);
return;
}
_taskQueue.Enqueue(new Tuple<Type, TaskOptions>(type, options));
}
}
/// <inheritdoc />
public void Cancel(IScheduledTaskWorker task)
/// <inheritdoc />
public void AddTasks(IEnumerable<IScheduledTask> tasks)
{
var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _logger));
ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
foreach (var task in ScheduledTasks)
{
((ScheduledTaskWorker)task).Cancel();
task.Dispose();
}
}
/// <inheritdoc />
public Task Execute(IScheduledTaskWorker task, TaskOptions options)
/// <inheritdoc />
public void Cancel(IScheduledTaskWorker task)
{
((ScheduledTaskWorker)task).Cancel();
}
/// <inheritdoc />
public Task Execute(IScheduledTaskWorker task, TaskOptions options)
{
return ((ScheduledTaskWorker)task).Execute(options);
}
/// <summary>
/// Called when [task executing].
/// </summary>
/// <param name="task">The task.</param>
internal void OnTaskExecuting(IScheduledTaskWorker task)
{
TaskExecuting?.Invoke(this, new GenericEventArgs<IScheduledTaskWorker>(task));
}
/// <summary>
/// Called when [task completed].
/// </summary>
/// <param name="task">The task.</param>
/// <param name="result">The result.</param>
internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result)
{
TaskCompleted?.Invoke(task, new TaskCompletionEventArgs(task, result));
ExecuteQueuedTasks();
}
/// <summary>
/// Executes the queued tasks.
/// </summary>
private void ExecuteQueuedTasks()
{
lock (_taskQueue)
{
return ((ScheduledTaskWorker)task).Execute(options);
}
var list = new List<Tuple<Type, TaskOptions>>();
/// <summary>
/// Called when [task executing].
/// </summary>
/// <param name="task">The task.</param>
internal void OnTaskExecuting(IScheduledTaskWorker task)
{
TaskExecuting?.Invoke(this, new GenericEventArgs<IScheduledTaskWorker>(task));
}
/// <summary>
/// Called when [task completed].
/// </summary>
/// <param name="task">The task.</param>
/// <param name="result">The result.</param>
internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result)
{
TaskCompleted?.Invoke(task, new TaskCompletionEventArgs(task, result));
ExecuteQueuedTasks();
}
/// <summary>
/// Executes the queued tasks.
/// </summary>
private void ExecuteQueuedTasks()
{
lock (_taskQueue)
while (_taskQueue.TryDequeue(out var item))
{
var list = new List<Tuple<Type, TaskOptions>>();
while (_taskQueue.TryDequeue(out var item))
if (list.All(i => i.Item1 != item.Item1))
{
if (list.All(i => i.Item1 != item.Item1))
{
list.Add(item);
}
list.Add(item);
}
}
foreach (var enqueuedType in list)
foreach (var enqueuedType in list)
{
var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == enqueuedType.Item1);
if (scheduledTask.State == TaskState.Idle)
{
var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == enqueuedType.Item1);
if (scheduledTask.State == TaskState.Idle)
{
Execute(scheduledTask, enqueuedType.Item2);
}
Execute(scheduledTask, enqueuedType.Item2);
}
}
}

View File

@ -156,14 +156,11 @@ public partial class AudioNormalizationTask : IScheduledTask
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return
[
new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
}
];
yield return new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
private async Task<float?> CalculateLUFSAsync(string inputArgs, bool waitForExit, CancellationToken cancellationToken)
@ -194,7 +191,7 @@ public partial class AudioNormalizationTask : IScheduledTask
using var reader = process.StandardError;
float? lufs = null;
await foreach (var line in reader.ReadAllLinesAsync(cancellationToken))
await foreach (var line in reader.ReadAllLinesAsync(cancellationToken).ConfigureAwait(false))
{
Match match = LUFSRegex().Match(line);
if (match.Success)

View File

@ -17,155 +17,151 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Class ChapterImagesTask.
/// </summary>
public class ChapterImagesTask : IScheduledTask
{
private readonly ILogger<ChapterImagesTask> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IApplicationPaths _appPaths;
private readonly IChapterManager _chapterManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
/// <summary>
/// Class ChapterImagesTask.
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary>
public class ChapterImagesTask : IScheduledTask
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public ChapterImagesTask(
ILogger<ChapterImagesTask> logger,
ILibraryManager libraryManager,
IApplicationPaths appPaths,
IChapterManager chapterManager,
IFileSystem fileSystem,
ILocalizationManager localization)
{
private readonly ILogger<ChapterImagesTask> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IApplicationPaths _appPaths;
private readonly IChapterManager _chapterManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
_logger = logger;
_libraryManager = libraryManager;
_appPaths = appPaths;
_chapterManager = chapterManager;
_fileSystem = fileSystem;
_localization = localization;
}
/// <summary>
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public ChapterImagesTask(
ILogger<ChapterImagesTask> logger,
ILibraryManager libraryManager,
IApplicationPaths appPaths,
IChapterManager chapterManager,
IFileSystem fileSystem,
ILocalizationManager localization)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
/// <inheritdoc />
public string Key => "RefreshChapterImages";
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_logger = logger;
_libraryManager = libraryManager;
_appPaths = appPaths;
_chapterManager = chapterManager;
_fileSystem = fileSystem;
_localization = localization;
}
Type = TaskTriggerInfoType.DailyTrigger,
TimeOfDayTicks = TimeSpan.FromHours(2).Ticks,
MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks
};
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
/// <inheritdoc />
public string Key => "RefreshChapterImages";
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var videos = _libraryManager.GetItemList(new InternalItemsQuery
{
return
[
new TaskTriggerInfo
{
Type = TaskTriggerInfoType.DailyTrigger,
TimeOfDayTicks = TimeSpan.FromHours(2).Ticks,
MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks
}
];
}
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var videos = _libraryManager.GetItemList(new InternalItemsQuery
MediaTypes = [MediaType.Video],
IsFolder = false,
Recursive = true,
DtoOptions = new DtoOptions(false)
{
MediaTypes = [MediaType.Video],
IsFolder = false,
Recursive = true,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
},
SourceTypes = [SourceType.Library],
IsVirtualItem = false
})
.OfType<Video>()
.ToList();
EnableImages = false
},
SourceTypes = [SourceType.Library],
IsVirtualItem = false
})
.OfType<Video>()
.ToList();
var numComplete = 0;
var numComplete = 0;
var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt");
var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt");
List<string> previouslyFailedImages;
List<string> previouslyFailedImages;
if (File.Exists(failHistoryPath))
if (File.Exists(failHistoryPath))
{
try
{
try
{
previouslyFailedImages = (await File.ReadAllTextAsync(failHistoryPath, cancellationToken).ConfigureAwait(false))
.Split('|', StringSplitOptions.RemoveEmptyEntries)
.ToList();
}
catch (IOException)
{
previouslyFailedImages = [];
}
previouslyFailedImages = (await File.ReadAllTextAsync(failHistoryPath, cancellationToken).ConfigureAwait(false))
.Split('|', StringSplitOptions.RemoveEmptyEntries)
.ToList();
}
else
catch (IOException)
{
previouslyFailedImages = [];
}
}
else
{
previouslyFailedImages = [];
}
var directoryService = new DirectoryService(_fileSystem);
var directoryService = new DirectoryService(_fileSystem);
foreach (var video in videos)
foreach (var video in videos)
{
cancellationToken.ThrowIfCancellationRequested();
var key = video.Path + video.DateModified.Ticks;
var extract = !previouslyFailedImages.Contains(key, StringComparison.OrdinalIgnoreCase);
try
{
cancellationToken.ThrowIfCancellationRequested();
var chapters = _chapterManager.GetChapters(video.Id);
var key = video.Path + video.DateModified.Ticks;
var success = await _chapterManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false);
var extract = !previouslyFailedImages.Contains(key, StringComparison.OrdinalIgnoreCase);
try
if (!success)
{
var chapters = _chapterManager.GetChapters(video.Id);
previouslyFailedImages.Add(key);
var success = await _chapterManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false);
if (!success)
var parentPath = Path.GetDirectoryName(failHistoryPath);
if (parentPath is not null)
{
previouslyFailedImages.Add(key);
var parentPath = Path.GetDirectoryName(failHistoryPath);
if (parentPath is not null)
{
Directory.CreateDirectory(parentPath);
}
string text = string.Join('|', previouslyFailedImages);
await File.WriteAllTextAsync(failHistoryPath, text, cancellationToken).ConfigureAwait(false);
Directory.CreateDirectory(parentPath);
}
numComplete++;
double percent = numComplete;
percent /= videos.Count;
string text = string.Join('|', previouslyFailedImages);
await File.WriteAllTextAsync(failHistoryPath, text, cancellationToken).ConfigureAwait(false);
}
progress.Report(100 * percent);
}
catch (ObjectDisposedException ex)
{
// TODO Investigate and properly fix.
_logger.LogError(ex, "Object Disposed");
break;
}
numComplete++;
double percent = numComplete;
percent /= videos.Count;
progress.Report(100 * percent);
}
catch (ObjectDisposedException ex)
{
// TODO Investigate and properly fix.
_logger.LogError(ex, "Object Disposed");
break;
}
}
}

View File

@ -7,71 +7,70 @@ using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Deletes old activity log entries.
/// </summary>
public class CleanActivityLogTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILocalizationManager _localization;
private readonly IActivityManager _activityManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
/// <summary>
/// Deletes old activity log entries.
/// Initializes a new instance of the <see cref="CleanActivityLogTask"/> class.
/// </summary>
public class CleanActivityLogTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
public CleanActivityLogTask(
ILocalizationManager localization,
IActivityManager activityManager,
IServerConfigurationManager serverConfigurationManager)
{
private readonly ILocalizationManager _localization;
private readonly IActivityManager _activityManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
_localization = localization;
_activityManager = activityManager;
_serverConfigurationManager = serverConfigurationManager;
}
/// <summary>
/// Initializes a new instance of the <see cref="CleanActivityLogTask"/> class.
/// </summary>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
public CleanActivityLogTask(
ILocalizationManager localization,
IActivityManager activityManager,
IServerConfigurationManager serverConfigurationManager)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanActivityLog");
/// <inheritdoc />
public string Key => "CleanActivityLog";
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanActivityLogDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays;
if (!retentionDays.HasValue || retentionDays < 0)
{
_localization = localization;
_activityManager = activityManager;
_serverConfigurationManager = serverConfigurationManager;
throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanActivityLog");
var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
return _activityManager.CleanAsync(startDate);
}
/// <inheritdoc />
public string Key => "CleanActivityLog";
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanActivityLogDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays;
if (!retentionDays.HasValue || retentionDays < 0)
{
throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
}
var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
return _activityManager.CleanAsync(startDate);
}
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return [];
}
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return [];
}
}

View File

@ -27,7 +27,6 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
private readonly IPlaylistManager _playlistManager;
private readonly ILogger<CleanupCollectionAndPlaylistPathsTask> _logger;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="CleanupCollectionAndPlaylistPathsTask"/> class.
@ -37,21 +36,18 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
/// <param name="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
public CleanupCollectionAndPlaylistPathsTask(
ILocalizationManager localization,
ICollectionManager collectionManager,
IPlaylistManager playlistManager,
ILogger<CleanupCollectionAndPlaylistPathsTask> logger,
IProviderManager providerManager,
IFileSystem fileSystem)
IProviderManager providerManager)
{
_localization = localization;
_collectionManager = collectionManager;
_playlistManager = playlistManager;
_logger = logger;
_providerManager = providerManager;
_fileSystem = fileSystem;
}
/// <inheritdoc />
@ -135,6 +131,9 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return [new TaskTriggerInfo() { Type = TaskTriggerInfoType.StartupTrigger }];
yield return new TaskTriggerInfo
{
Type = TaskTriggerInfoType.StartupTrigger,
};
}
}

View File

@ -11,134 +11,133 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Deletes old cache files.
/// </summary>
public class DeleteCacheFileTask : IScheduledTask, IConfigurableScheduledTask
{
/// <summary>
/// Deletes old cache files.
/// Gets or sets the application paths.
/// </summary>
public class DeleteCacheFileTask : IScheduledTask, IConfigurableScheduledTask
/// <value>The application paths.</value>
private readonly IApplicationPaths _applicationPaths;
private readonly ILogger<DeleteCacheFileTask> _logger;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
/// </summary>
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteCacheFileTask(
IApplicationPaths appPaths,
ILogger<DeleteCacheFileTask> logger,
IFileSystem fileSystem,
ILocalizationManager localization)
{
/// <summary>
/// Gets or sets the application paths.
/// </summary>
/// <value>The application paths.</value>
private readonly IApplicationPaths _applicationPaths;
private readonly ILogger<DeleteCacheFileTask> _logger;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
_applicationPaths = appPaths;
_logger = logger;
_fileSystem = fileSystem;
_localization = localization;
}
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
/// </summary>
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteCacheFileTask(
IApplicationPaths appPaths,
ILogger<DeleteCacheFileTask> logger,
IFileSystem fileSystem,
ILocalizationManager localization)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanCache");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "DeleteCacheFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_applicationPaths = appPaths;
_logger = logger;
_fileSystem = fileSystem;
_localization = localization;
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var minDateModified = DateTime.UtcNow.AddDays(-30);
try
{
DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken);
}
catch (DirectoryNotFoundException)
{
// No biggie here. Nothing to delete
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanCache");
progress.Report(90);
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription");
minDateModified = DateTime.UtcNow.AddDays(-1);
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "DeleteCacheFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
try
{
return
[
// Every so often
new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks }
];
DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken);
}
catch (DirectoryNotFoundException)
{
// No biggie here. Nothing to delete
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
return Task.CompletedTask;
}
/// <summary>
/// Deletes the cache files from directory with a last write time less than a given date.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="minDateModified">The min date modified.</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The task cancellation token.</param>
private void DeleteCacheFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
{
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
var minDateModified = DateTime.UtcNow.AddDays(-30);
double percent = index;
percent /= filesToDelete.Count;
try
{
DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken);
}
catch (DirectoryNotFoundException)
{
// No biggie here. Nothing to delete
}
progress.Report(100 * percent);
progress.Report(90);
cancellationToken.ThrowIfCancellationRequested();
minDateModified = DateTime.UtcNow.AddDays(-1);
FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
try
{
DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken);
}
catch (DirectoryNotFoundException)
{
// No biggie here. Nothing to delete
}
return Task.CompletedTask;
index++;
}
/// <summary>
/// Deletes the cache files from directory with a last write time less than a given date.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="minDateModified">The min date modified.</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The task cancellation token.</param>
private void DeleteCacheFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
{
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
var index = 0;
foreach (var file in filesToDelete)
{
double percent = index;
percent /= filesToDelete.Count;
progress.Report(100 * percent);
cancellationToken.ThrowIfCancellationRequested();
FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
index++;
}
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
progress.Report(100);
}
progress.Report(100);
}
}

View File

@ -9,93 +9,93 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Deletes old log files.
/// </summary>
public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
/// <summary>
/// Deletes old log files.
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
/// </summary>
public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
}
/// <summary>
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
/// </summary>
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
/// <inheritdoc />
public string Description => string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("TaskCleanLogsDescription"),
_configurationManager.CommonConfiguration.LogFileRetentionDays);
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "CleanLogFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
// Only delete files that serilog doesn't manage (anything that doesn't start with 'log_'
var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, true)
.Where(f => !f.Name.StartsWith("log_", StringComparison.Ordinal)
&& _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
double percent = index / (double)filesToDelete.Count;
progress.Report(100 * percent);
cancellationToken.ThrowIfCancellationRequested();
_fileSystem.DeleteFile(file.FullName);
index++;
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
progress.Report(100);
/// <inheritdoc />
public string Description => string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("TaskCleanLogsDescription"),
_configurationManager.CommonConfiguration.LogFileRetentionDays);
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "CleanLogFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return
[
new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks }
];
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
// Only delete files that serilog doesn't manage (anything that doesn't start with 'log_'
var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, true)
.Where(f => !f.Name.StartsWith("log_", StringComparison.Ordinal)
&& _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
double percent = index / (double)filesToDelete.Count;
progress.Report(100 * percent);
cancellationToken.ThrowIfCancellationRequested();
_fileSystem.DeleteFile(file.FullName);
index++;
}
progress.Report(100);
return Task.CompletedTask;
}
return Task.CompletedTask;
}
}

View File

@ -10,118 +10,115 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Deletes all transcoding temp files.
/// </summary>
public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILogger<DeleteTranscodeFileTask> _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
/// <summary>
/// Deletes all transcoding temp files.
/// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class.
/// </summary>
public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="logger">Instance of the <see cref="ILogger{DeleteTranscodeFileTask}"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteTranscodeFileTask(
ILogger<DeleteTranscodeFileTask> logger,
IFileSystem fileSystem,
IConfigurationManager configurationManager,
ILocalizationManager localization)
{
private readonly ILogger<DeleteTranscodeFileTask> _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
_logger = logger;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_localization = localization;
}
/// <summary>
/// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger{DeleteTranscodeFileTask}"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteTranscodeFileTask(
ILogger<DeleteTranscodeFileTask> logger,
IFileSystem fileSystem,
IConfigurationManager configurationManager,
ILocalizationManager localization)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "DeleteTranscodeFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_logger = logger;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_localization = localization;
Type = TaskTriggerInfoType.StartupTrigger
};
yield return new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var minDateModified = DateTime.UtcNow.AddDays(-1);
progress.Report(50);
DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, progress, cancellationToken);
return Task.CompletedTask;
}
/// <summary>
/// Deletes the transcoded temp files from directory with a last write time less than a given date.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="minDateModified">The min date modified.</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The task cancellation token.</param>
private void DeleteTempFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
{
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
double percent = index;
percent /= filesToDelete.Count;
progress.Report(100 * percent);
cancellationToken.ThrowIfCancellationRequested();
FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
index++;
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "DeleteTranscodeFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return
[
new TaskTriggerInfo
{
Type = TaskTriggerInfoType.StartupTrigger
},
new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
}
];
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var minDateModified = DateTime.UtcNow.AddDays(-1);
progress.Report(50);
DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, progress, cancellationToken);
return Task.CompletedTask;
}
/// <summary>
/// Deletes the transcoded temp files from directory with a last write time less than a given date.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="minDateModified">The min date modified.</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The task cancellation token.</param>
private void DeleteTempFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
{
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
double percent = index;
percent /= filesToDelete.Count;
progress.Report(100 * percent);
cancellationToken.ThrowIfCancellationRequested();
FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
index++;
}
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
progress.Report(100);
}
progress.Report(100);
}
}

View File

@ -62,11 +62,11 @@ public class MediaSegmentExtractionTask : IScheduledTask
var query = new InternalItemsQuery
{
MediaTypes = new[] { MediaType.Video, MediaType.Audio },
MediaTypes = [MediaType.Video, MediaType.Audio],
IsVirtualItem = false,
IncludeItemTypes = _itemTypes,
DtoOptions = new DtoOptions(true),
SourceTypes = new[] { SourceType.Library },
SourceTypes = [SourceType.Library],
Recursive = true,
Limit = pagesize
};

View File

@ -5,84 +5,78 @@ using System.Threading.Tasks;
using Jellyfin.Database.Implementations;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Optimizes Jellyfin's database by issuing a VACUUM command.
/// </summary>
public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILogger<OptimizeDatabaseTask> _logger;
private readonly ILocalizationManager _localization;
private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider;
/// <summary>
/// Optimizes Jellyfin's database by issuing a VACUUM command.
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
/// </summary>
public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="jellyfinDatabaseProvider">Instance of the JellyfinDatabaseProvider that can be used for provider specific operations.</param>
public OptimizeDatabaseTask(
ILogger<OptimizeDatabaseTask> logger,
ILocalizationManager localization,
IJellyfinDatabaseProvider jellyfinDatabaseProvider)
{
private readonly ILogger<OptimizeDatabaseTask> _logger;
private readonly ILocalizationManager _localization;
private readonly IDbContextFactory<JellyfinDbContext> _provider;
private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider;
_logger = logger;
_localization = localization;
_jellyfinDatabaseProvider = jellyfinDatabaseProvider;
}
/// <summary>
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="provider">Instance of the <see cref="IDbContextFactory{JellyfinDbContext}"/> interface.</param>
/// <param name="jellyfinDatabaseProvider">Instance of the JellyfinDatabaseProvider that can be used for provider specific operations.</param>
public OptimizeDatabaseTask(
ILogger<OptimizeDatabaseTask> logger,
ILocalizationManager localization,
IDbContextFactory<JellyfinDbContext> provider,
IJellyfinDatabaseProvider jellyfinDatabaseProvider)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskOptimizeDatabase");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskOptimizeDatabaseDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "OptimizeDatabaseTask";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_logger = logger;
_localization = localization;
_provider = provider;
_jellyfinDatabaseProvider = jellyfinDatabaseProvider;
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
_logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
try
{
await _jellyfinDatabaseProvider.RunScheduledOptimisation(cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskOptimizeDatabase");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskOptimizeDatabaseDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "OptimizeDatabaseTask";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
catch (Exception e)
{
return
[
// Every so often
new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks }
];
}
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
_logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
try
{
await _jellyfinDatabaseProvider.RunScheduledOptimisation(cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.LogError(e, "Error while optimizing jellyfin.db");
}
_logger.LogError(e, "Error while optimizing jellyfin.db");
}
}
}

View File

@ -6,68 +6,64 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Class PeopleValidationTask.
/// </summary>
public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
/// <summary>
/// Class PeopleValidationTask.
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
/// </summary>
public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization)
{
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
_libraryManager = libraryManager;
_localization = localization;
}
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
/// </summary>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshPeople");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
/// <inheritdoc />
public string Key => "RefreshPeople";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <summary>
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>An <see cref="IEnumerable{TaskTriggerInfo}"/> containing the default trigger infos for this task.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_libraryManager = libraryManager;
_localization = localization;
}
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromDays(7).Ticks
};
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshPeople");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
/// <inheritdoc />
public string Key => "RefreshPeople";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <summary>
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>An <see cref="IEnumerable{TaskTriggerInfo}"/> containing the default trigger infos for this task.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[]
{
new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromDays(7).Ticks
}
};
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidatePeopleAsync(progress, cancellationToken);
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
return _libraryManager.ValidatePeopleAsync(progress, cancellationToken);
}
}

View File

@ -10,111 +10,115 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Plugin Update Task.
/// </summary>
public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILogger<PluginUpdateTask> _logger;
private readonly IInstallationManager _installationManager;
private readonly ILocalizationManager _localization;
/// <summary>
/// Plugin Update Task.
/// Initializes a new instance of the <see cref="PluginUpdateTask" /> class.
/// </summary>
public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization)
{
private readonly ILogger<PluginUpdateTask> _logger;
_logger = logger;
_installationManager = installationManager;
_localization = localization;
}
private readonly IInstallationManager _installationManager;
private readonly ILocalizationManager _localization;
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskUpdatePlugins");
/// <summary>
/// Initializes a new instance of the <see cref="PluginUpdateTask" /> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization)
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksApplicationCategory");
/// <inheritdoc />
public string Key => "PluginUpdates";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_logger = logger;
_installationManager = installationManager;
_localization = localization;
}
Type = TaskTriggerInfoType.StartupTrigger
};
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskUpdatePlugins");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksApplicationCategory");
/// <inheritdoc />
public string Key => "PluginUpdates";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
yield return new TaskTriggerInfo
{
// At startup
yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.StartupTrigger };
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
// Every so often
yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks };
}
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
progress.Report(0);
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken);
var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList();
progress.Report(10);
var numComplete = 0;
foreach (var package in packagesToInstall)
{
progress.Report(0);
cancellationToken.ThrowIfCancellationRequested();
var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken);
var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList();
progress.Report(10);
var numComplete = 0;
foreach (var package in packagesToInstall)
try
{
cancellationToken.ThrowIfCancellationRequested();
try
await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// InstallPackage has its own inner cancellation token, so only throw this if it's ours
if (cancellationToken.IsCancellationRequested)
{
await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// InstallPackage has its own inner cancellation token, so only throw this if it's ours
if (cancellationToken.IsCancellationRequested)
{
throw;
}
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Error downloading {0}", package.Name);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error updating {0}", package.Name);
}
catch (InvalidDataException ex)
{
_logger.LogError(ex, "Error updating {0}", package.Name);
}
// Update progress
lock (progress)
{
progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10);
throw;
}
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Error downloading {Name}", package.Name);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error updating {Name}", package.Name);
}
catch (InvalidDataException ex)
{
_logger.LogError(ex, "Error updating {Name}", package.Name);
}
progress.Report(100);
// Update progress
lock (progress)
{
progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10);
}
}
progress.Report(100);
}
}

View File

@ -7,60 +7,59 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// <summary>
/// Class RefreshMediaLibraryTask.
/// </summary>
public class RefreshMediaLibraryTask : IScheduledTask
{
/// <summary>
/// Class RefreshMediaLibraryTask.
/// The _library manager.
/// </summary>
public class RefreshMediaLibraryTask : IScheduledTask
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
/// </summary>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization)
{
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
_libraryManager = libraryManager;
_localization = localization;
}
/// <summary>
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
/// </summary>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization)
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshLibrary");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
/// <inheritdoc />
public string Key => "RefreshLibrary";
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
_libraryManager = libraryManager;
_localization = localization;
}
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(12).Ticks
};
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshLibrary");
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription");
progress.Report(0);
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
/// <inheritdoc />
public string Key => "RefreshLibrary";
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(12).Ticks
};
}
/// <inheritdoc />
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
progress.Report(0);
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
}
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
}
}

View File

@ -3,85 +3,84 @@ using System.Threading;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Triggers
namespace Emby.Server.Implementations.ScheduledTasks.Triggers;
/// <summary>
/// Represents a task trigger that fires everyday.
/// </summary>
public sealed class DailyTrigger : ITaskTrigger, IDisposable
{
private readonly TimeSpan _timeOfDay;
private Timer? _timer;
private bool _disposed;
/// <summary>
/// Represents a task trigger that fires everyday.
/// Initializes a new instance of the <see cref="DailyTrigger"/> class.
/// </summary>
public sealed class DailyTrigger : ITaskTrigger, IDisposable
/// <param name="timeOfDay">The time of day to trigger the task to run.</param>
/// <param name="taskOptions">The options of this task.</param>
public DailyTrigger(TimeSpan timeOfDay, TaskOptions taskOptions)
{
private readonly TimeSpan _timeOfDay;
private Timer? _timer;
private bool _disposed = false;
_timeOfDay = timeOfDay;
TaskOptions = taskOptions;
}
/// <summary>
/// Initializes a new instance of the <see cref="DailyTrigger"/> class.
/// </summary>
/// <param name="timeofDay">The time of day to trigger the task to run.</param>
/// <param name="taskOptions">The options of this task.</param>
public DailyTrigger(TimeSpan timeofDay, TaskOptions taskOptions)
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
DisposeTimer();
var now = DateTime.Now;
var triggerDate = now.TimeOfDay > _timeOfDay ? now.Date.AddDays(1) : now.Date;
triggerDate = triggerDate.Add(_timeOfDay);
var dueTime = triggerDate - now;
logger.LogInformation("Daily trigger for {Task} set to fire at {TriggerDate:yyyy-MM-dd HH:mm:ss.fff zzz}, which is {DueTime:c} from now.", taskName, triggerDate, dueTime);
_timer = new Timer(_ => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
}
/// <inheritdoc />
public void Stop()
{
DisposeTimer();
}
/// <summary>
/// Disposes the timer.
/// </summary>
private void DisposeTimer()
{
_timer?.Dispose();
_timer = null;
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
Triggered?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
_timeOfDay = timeofDay;
TaskOptions = taskOptions;
return;
}
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
DisposeTimer();
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
DisposeTimer();
var now = DateTime.Now;
var triggerDate = now.TimeOfDay > _timeOfDay ? now.Date.AddDays(1) : now.Date;
triggerDate = triggerDate.Add(_timeOfDay);
var dueTime = triggerDate - now;
logger.LogInformation("Daily trigger for {Task} set to fire at {TriggerDate:yyyy-MM-dd HH:mm:ss.fff zzz}, which is {DueTime:c} from now.", taskName, triggerDate, dueTime);
_timer = new Timer(_ => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
}
/// <inheritdoc />
public void Stop()
{
DisposeTimer();
}
/// <summary>
/// Disposes the timer.
/// </summary>
private void DisposeTimer()
{
_timer?.Dispose();
_timer = null;
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
Triggered?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
DisposeTimer();
_disposed = true;
}
_disposed = true;
}
}

View File

@ -4,104 +4,103 @@ using System.Threading;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Triggers
namespace Emby.Server.Implementations.ScheduledTasks.Triggers;
/// <summary>
/// Represents a task trigger that runs repeatedly on an interval.
/// </summary>
public sealed class IntervalTrigger : ITaskTrigger, IDisposable
{
private readonly TimeSpan _interval;
private DateTime _lastStartDate;
private Timer? _timer;
private bool _disposed;
/// <summary>
/// Represents a task trigger that runs repeatedly on an interval.
/// Initializes a new instance of the <see cref="IntervalTrigger"/> class.
/// </summary>
public sealed class IntervalTrigger : ITaskTrigger, IDisposable
/// <param name="interval">The interval.</param>
/// <param name="taskOptions">The options of this task.</param>
public IntervalTrigger(TimeSpan interval, TaskOptions taskOptions)
{
private readonly TimeSpan _interval;
private DateTime _lastStartDate;
private Timer? _timer;
private bool _disposed = false;
_interval = interval;
TaskOptions = taskOptions;
}
/// <summary>
/// Initializes a new instance of the <see cref="IntervalTrigger"/> class.
/// </summary>
/// <param name="interval">The interval.</param>
/// <param name="taskOptions">The options of this task.</param>
public IntervalTrigger(TimeSpan interval, TaskOptions taskOptions)
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
DisposeTimer();
DateTime now = DateTime.UtcNow;
DateTime triggerDate;
if (lastResult is null)
{
_interval = interval;
TaskOptions = taskOptions;
// Task has never been completed before
triggerDate = now.AddHours(1);
}
else
{
triggerDate = new[] { lastResult.EndTimeUtc, _lastStartDate, now.AddMinutes(1) }.Max().Add(_interval);
}
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
var dueTime = triggerDate - now;
var maxDueTime = TimeSpan.FromDays(7);
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
if (dueTime > maxDueTime)
{
DisposeTimer();
DateTime now = DateTime.UtcNow;
DateTime triggerDate;
if (lastResult is null)
{
// Task has never been completed before
triggerDate = now.AddHours(1);
}
else
{
triggerDate = new[] { lastResult.EndTimeUtc, _lastStartDate, now.AddMinutes(1) }.Max().Add(_interval);
}
var dueTime = triggerDate - now;
var maxDueTime = TimeSpan.FromDays(7);
if (dueTime > maxDueTime)
{
dueTime = maxDueTime;
}
_timer = new Timer(_ => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
dueTime = maxDueTime;
}
/// <inheritdoc />
public void Stop()
_timer = new Timer(_ => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
}
/// <inheritdoc />
public void Stop()
{
DisposeTimer();
}
/// <summary>
/// Disposes the timer.
/// </summary>
private void DisposeTimer()
{
_timer?.Dispose();
_timer = null;
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
DisposeTimer();
if (Triggered is not null)
{
DisposeTimer();
}
/// <summary>
/// Disposes the timer.
/// </summary>
private void DisposeTimer()
{
_timer?.Dispose();
_timer = null;
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
DisposeTimer();
if (Triggered is not null)
{
_lastStartDate = DateTime.UtcNow;
Triggered(this, EventArgs.Empty);
}
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
DisposeTimer();
_disposed = true;
_lastStartDate = DateTime.UtcNow;
Triggered(this, EventArgs.Empty);
}
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
DisposeTimer();
_disposed = true;
}
}

View File

@ -3,52 +3,51 @@ using System.Threading.Tasks;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Triggers
namespace Emby.Server.Implementations.ScheduledTasks.Triggers;
/// <summary>
/// Class StartupTaskTrigger.
/// </summary>
public sealed class StartupTrigger : ITaskTrigger
{
private const int DelayMs = 3000;
/// <summary>
/// Class StartupTaskTrigger.
/// Initializes a new instance of the <see cref="StartupTrigger"/> class.
/// </summary>
public sealed class StartupTrigger : ITaskTrigger
/// <param name="taskOptions">The options of this task.</param>
public StartupTrigger(TaskOptions taskOptions)
{
private const int DelayMs = 3000;
TaskOptions = taskOptions;
}
/// <summary>
/// Initializes a new instance of the <see cref="StartupTrigger"/> class.
/// </summary>
/// <param name="taskOptions">The options of this task.</param>
public StartupTrigger(TaskOptions taskOptions)
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public async void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
if (isApplicationStartup)
{
TaskOptions = taskOptions;
}
await Task.Delay(DelayMs).ConfigureAwait(false);
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public async void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
if (isApplicationStartup)
{
await Task.Delay(DelayMs).ConfigureAwait(false);
OnTriggered();
}
}
/// <inheritdoc />
public void Stop()
{
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
Triggered?.Invoke(this, EventArgs.Empty);
OnTriggered();
}
}
/// <inheritdoc />
public void Stop()
{
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
Triggered?.Invoke(this, EventArgs.Empty);
}
}

View File

@ -3,108 +3,107 @@ using System.Threading;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Triggers
namespace Emby.Server.Implementations.ScheduledTasks.Triggers;
/// <summary>
/// Represents a task trigger that fires on a weekly basis.
/// </summary>
public sealed class WeeklyTrigger : ITaskTrigger, IDisposable
{
private readonly TimeSpan _timeOfDay;
private readonly DayOfWeek _dayOfWeek;
private Timer? _timer;
private bool _disposed;
/// <summary>
/// Represents a task trigger that fires on a weekly basis.
/// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
/// </summary>
public sealed class WeeklyTrigger : ITaskTrigger, IDisposable
/// <param name="timeOfDay">The time of day to trigger the task to run.</param>
/// <param name="dayOfWeek">The day of week.</param>
/// <param name="taskOptions">The options of this task.</param>
public WeeklyTrigger(TimeSpan timeOfDay, DayOfWeek dayOfWeek, TaskOptions taskOptions)
{
private readonly TimeSpan _timeOfDay;
private readonly DayOfWeek _dayOfWeek;
private Timer? _timer;
private bool _disposed;
_timeOfDay = timeOfDay;
_dayOfWeek = dayOfWeek;
TaskOptions = taskOptions;
}
/// <summary>
/// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
/// </summary>
/// <param name="timeofDay">The time of day to trigger the task to run.</param>
/// <param name="dayOfWeek">The day of week.</param>
/// <param name="taskOptions">The options of this task.</param>
public WeeklyTrigger(TimeSpan timeofDay, DayOfWeek dayOfWeek, TaskOptions taskOptions)
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
DisposeTimer();
var triggerDate = GetNextTriggerDateTime();
_timer = new Timer(_ => OnTriggered(), null, triggerDate - DateTime.Now, TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Gets the next trigger date time.
/// </summary>
/// <returns>DateTime.</returns>
private DateTime GetNextTriggerDateTime()
{
var now = DateTime.Now;
// If it's on the same day
if (now.DayOfWeek == _dayOfWeek)
{
_timeOfDay = timeofDay;
_dayOfWeek = dayOfWeek;
TaskOptions = taskOptions;
// It's either later today, or a week from now
return now.TimeOfDay < _timeOfDay ? now.Date.Add(_timeOfDay) : now.Date.AddDays(7).Add(_timeOfDay);
}
/// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
var triggerDate = now.Date;
/// <inheritdoc />
public TaskOptions TaskOptions { get; }
/// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
// Walk the date forward until we get to the trigger day
while (triggerDate.DayOfWeek != _dayOfWeek)
{
DisposeTimer();
var triggerDate = GetNextTriggerDateTime();
_timer = new Timer(_ => OnTriggered(), null, triggerDate - DateTime.Now, TimeSpan.FromMilliseconds(-1));
triggerDate = triggerDate.AddDays(1);
}
/// <summary>
/// Gets the next trigger date time.
/// </summary>
/// <returns>DateTime.</returns>
private DateTime GetNextTriggerDateTime()
// Return the trigger date plus the time offset
return triggerDate.Add(_timeOfDay);
}
/// <inheritdoc />
public void Stop()
{
DisposeTimer();
}
/// <summary>
/// Disposes the timer.
/// </summary>
private void DisposeTimer()
{
_timer?.Dispose();
_timer = null;
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
Triggered?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
var now = DateTime.Now;
// If it's on the same day
if (now.DayOfWeek == _dayOfWeek)
{
// It's either later today, or a week from now
return now.TimeOfDay < _timeOfDay ? now.Date.Add(_timeOfDay) : now.Date.AddDays(7).Add(_timeOfDay);
}
var triggerDate = now.Date;
// Walk the date forward until we get to the trigger day
while (triggerDate.DayOfWeek != _dayOfWeek)
{
triggerDate = triggerDate.AddDays(1);
}
// Return the trigger date plus the time offset
return triggerDate.Add(_timeOfDay);
return;
}
/// <inheritdoc />
public void Stop()
{
DisposeTimer();
}
DisposeTimer();
/// <summary>
/// Disposes the timer.
/// </summary>
private void DisposeTimer()
{
_timer?.Dispose();
_timer = null;
}
/// <summary>
/// Called when [triggered].
/// </summary>
private void OnTriggered()
{
Triggered?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
DisposeTimer();
_disposed = true;
}
_disposed = true;
}
}