mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-24 02:02:29 -04:00
Merge pull request #14028 from Shadowghost/cleanup-tasks
Cleanup Tasks and Validators
This commit is contained in:
commit
b4a58ee13a
@ -5,45 +5,44 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class ArtistsPostScanTask.
|
/// The _library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The _library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
private readonly ILogger<ArtistsValidator> _logger;
|
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="progress">The progress.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
/// <returns>Task.</returns>
|
||||||
public ArtistsPostScanTask(
|
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
ILibraryManager libraryManager,
|
{
|
||||||
ILogger<ArtistsValidator> logger,
|
return new ArtistsValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,102 +10,101 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library.Validators
|
namespace Emby.Server.Implementations.Library.Validators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class ArtistsValidator.
|
||||||
|
/// </summary>
|
||||||
|
public class ArtistsValidator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class ArtistsValidator.
|
/// The library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILogger<ArtistsValidator> _logger;
|
/// <param name="progress">The progress.</param>
|
||||||
private readonly IItemRepository _itemRepo;
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var names = _itemRepo.GetAllArtistNames();
|
||||||
|
|
||||||
/// <summary>
|
var numComplete = 0;
|
||||||
/// Initializes a new instance of the <see cref="ArtistsValidator" /> class.
|
var count = names.Count;
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
foreach (var name in names)
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
|
||||||
public ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> logger, IItemRepository itemRepo)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
try
|
||||||
_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
|
var item = _libraryManager.GetArtist(name);
|
||||||
{
|
|
||||||
var item = _libraryManager.GetArtist(name);
|
|
||||||
|
|
||||||
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error refreshing {ArtistName}", name);
|
_logger.LogError(ex, "Error refreshing {ArtistName}", name);
|
||||||
}
|
|
||||||
|
|
||||||
numComplete++;
|
|
||||||
double percent = numComplete;
|
|
||||||
percent /= count;
|
|
||||||
percent *= 100;
|
|
||||||
|
|
||||||
progress.Report(percent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 },
|
continue;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,149 +9,146 @@ using MediaBrowser.Controller.Collections;
|
|||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class CollectionPostScanTask.
|
/// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
|
||||||
/// </summary>
|
/// </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;
|
_libraryManager = libraryManager;
|
||||||
private readonly ICollectionManager _collectionManager;
|
_collectionManager = collectionManager;
|
||||||
private readonly ILogger<CollectionPostScanTask> _logger;
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="progress">The progress.</param>
|
||||||
/// <param name="collectionManager">The collection manager.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <returns>Task.</returns>
|
||||||
public CollectionPostScanTask(
|
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
ILibraryManager libraryManager,
|
{
|
||||||
ICollectionManager collectionManager,
|
var collectionNameMoviesMap = new Dictionary<string, HashSet<Guid>>();
|
||||||
ILogger<CollectionPostScanTask> logger)
|
|
||||||
|
foreach (var library in _libraryManager.RootFolder.Children)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
if (!_libraryManager.GetLibraryOptions(library).AutomaticallyAddToCollection)
|
||||||
_collectionManager = collectionManager;
|
{
|
||||||
_logger = logger;
|
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>
|
var numComplete = 0;
|
||||||
/// Runs the specified progress.
|
var count = collectionNameMoviesMap.Count;
|
||||||
/// </summary>
|
|
||||||
/// <param name="progress">The progress.</param>
|
if (count == 0)
|
||||||
/// <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)
|
|
||||||
{
|
|
||||||
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);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,45 +5,44 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class GenresPostScanTask.
|
/// The _library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The _library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
private readonly ILogger<GenresValidator> _logger;
|
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="GenresPostScanTask" /> class.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="progress">The progress.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
/// <returns>Task.</returns>
|
||||||
public GenresPostScanTask(
|
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
ILibraryManager libraryManager,
|
{
|
||||||
ILogger<GenresValidator> logger,
|
return new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,97 +8,96 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library.Validators
|
namespace Emby.Server.Implementations.Library.Validators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class GenresValidator.
|
||||||
|
/// </summary>
|
||||||
|
public class GenresValidator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GenresValidator.
|
/// The library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILogger<GenresValidator> _logger;
|
/// <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>
|
var numComplete = 0;
|
||||||
/// Initializes a new instance of the <see cref="GenresValidator"/> class.
|
var count = names.Count;
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
foreach (var name in names)
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
|
||||||
public GenresValidator(ILibraryManager libraryManager, ILogger<GenresValidator> logger, IItemRepository itemRepo)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
try
|
||||||
_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
|
var item = _libraryManager.GetGenre(name);
|
||||||
{
|
|
||||||
var item = _libraryManager.GetGenre(name);
|
|
||||||
|
|
||||||
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error refreshing {GenreName}", name);
|
_logger.LogError(ex, "Error refreshing {GenreName}", name);
|
||||||
}
|
|
||||||
|
|
||||||
numComplete++;
|
|
||||||
double percent = numComplete;
|
|
||||||
percent /= count;
|
|
||||||
percent *= 100;
|
|
||||||
|
|
||||||
progress.Report(percent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
|
numComplete++;
|
||||||
{
|
double percent = numComplete;
|
||||||
IncludeItemTypes = [BaseItemKind.Genre, BaseItemKind.MusicGenre],
|
percent /= count;
|
||||||
IsDeadGenre = true,
|
percent *= 100;
|
||||||
IsLocked = false
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var item in deadEntities)
|
progress.Report(percent);
|
||||||
{
|
|
||||||
_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,45 +5,44 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class MusicGenresPostScanTask.
|
/// The library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
private readonly ILogger<MusicGenresValidator> _logger;
|
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MusicGenresPostScanTask" /> class.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="progress">The progress.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
/// <returns>Task.</returns>
|
||||||
public MusicGenresPostScanTask(
|
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
ILibraryManager libraryManager,
|
{
|
||||||
ILogger<MusicGenresValidator> logger,
|
return new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,77 +5,76 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library.Validators
|
namespace Emby.Server.Implementations.Library.Validators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class MusicGenresValidator.
|
||||||
|
/// </summary>
|
||||||
|
public class MusicGenresValidator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class MusicGenresValidator.
|
/// The library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger.
|
/// Runs the specified progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILogger<MusicGenresValidator> _logger;
|
/// <param name="progress">The progress.</param>
|
||||||
private readonly IItemRepository _itemRepo;
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var names = _itemRepo.GetMusicGenreNames();
|
||||||
|
|
||||||
/// <summary>
|
var numComplete = 0;
|
||||||
/// Initializes a new instance of the <see cref="MusicGenresValidator" /> class.
|
var count = names.Count;
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
foreach (var name in names)
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
|
||||||
public MusicGenresValidator(ILibraryManager libraryManager, ILogger<MusicGenresValidator> logger, IItemRepository itemRepo)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
try
|
||||||
_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
|
var item = _libraryManager.GetMusicGenre(name);
|
||||||
{
|
|
||||||
var item = _libraryManager.GetMusicGenre(name);
|
|
||||||
|
|
||||||
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error refreshing {GenreName}", name);
|
_logger.LogError(ex, "Error refreshing {GenreName}", name);
|
||||||
}
|
|
||||||
|
|
||||||
numComplete++;
|
|
||||||
double percent = numComplete;
|
|
||||||
percent /= count;
|
|
||||||
percent *= 100;
|
|
||||||
|
|
||||||
progress.Report(percent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.Report(100);
|
numComplete++;
|
||||||
|
double percent = numComplete;
|
||||||
|
percent /= count;
|
||||||
|
percent *= 100;
|
||||||
|
|
||||||
|
progress.Report(percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progress.Report(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,119 +9,114 @@ using MediaBrowser.Controller.Providers;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library.Validators
|
namespace Emby.Server.Implementations.Library.Validators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class PeopleValidator.
|
||||||
|
/// </summary>
|
||||||
|
public class PeopleValidator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class PeopleValidator.
|
/// The _library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The _library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_fileSystem = fileSystem;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _logger.
|
/// Validates the people.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILogger _logger;
|
/// <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>
|
var numPeople = people.Count;
|
||||||
/// Initializes a new instance of the <see cref="PeopleValidator" /> class.
|
|
||||||
/// </summary>
|
_logger.LogDebug("Will refresh {Amount} people", numPeople);
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
|
||||||
/// <param name="logger">The logger.</param>
|
foreach (var person in people)
|
||||||
/// <param name="fileSystem">The file system.</param>
|
|
||||||
public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
_logger = logger;
|
|
||||||
_fileSystem = fileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
try
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
var item = _libraryManager.GetPerson(person);
|
||||||
|
if (item is null)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetPerson(person);
|
_logger.LogWarning("Failed to get person: {Name}", person);
|
||||||
if (item is null)
|
continue;
|
||||||
{
|
|
||||||
_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update progress
|
var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
||||||
numComplete++;
|
{
|
||||||
double percent = numComplete;
|
ImageRefreshMode = MetadataRefreshMode.ValidationOnly,
|
||||||
percent /= numPeople;
|
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
|
// Update progress
|
||||||
{
|
numComplete++;
|
||||||
IncludeItemTypes = [BaseItemKind.Person],
|
double percent = numComplete;
|
||||||
IsDeadPerson = true,
|
percent /= numPeople;
|
||||||
IsLocked = false
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var item in deadEntities)
|
progress.Report(100 * percent);
|
||||||
{
|
|
||||||
_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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,46 +5,45 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class MusicGenresPostScanTask.
|
/// The _library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The _library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
|
|
||||||
private readonly ILogger<StudiosValidator> _logger;
|
/// <summary>
|
||||||
private readonly IItemRepository _itemRepo;
|
/// Runs the specified progress.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <param name="progress">The progress.</param>
|
||||||
/// Initializes a new instance of the <see cref="StudiosPostScanTask" /> class.
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// </summary>
|
/// <returns>Task.</returns>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
/// <param name="logger">The logger.</param>
|
{
|
||||||
/// <param name="itemRepo">The item repository.</param>
|
return new StudiosValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,98 +8,97 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library.Validators
|
namespace Emby.Server.Implementations.Library.Validators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class StudiosValidator.
|
||||||
|
/// </summary>
|
||||||
|
public class StudiosValidator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class StudiosValidator.
|
/// The library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The library manager.
|
_logger = logger;
|
||||||
/// </summary>
|
_itemRepo = itemRepo;
|
||||||
private readonly ILibraryManager _libraryManager;
|
}
|
||||||
|
|
||||||
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>
|
var numComplete = 0;
|
||||||
/// The logger.
|
var count = names.Count;
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger<StudiosValidator> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
foreach (var name in names)
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
try
|
||||||
_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
|
var item = _libraryManager.GetStudio(name);
|
||||||
{
|
|
||||||
var item = _libraryManager.GetStudio(name);
|
|
||||||
|
|
||||||
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error refreshing {StudioName}", name);
|
_logger.LogError(ex, "Error refreshing {StudioName}", name);
|
||||||
}
|
|
||||||
|
|
||||||
numComplete++;
|
|
||||||
double percent = numComplete;
|
|
||||||
percent /= count;
|
|
||||||
percent *= 100;
|
|
||||||
|
|
||||||
progress.Report(percent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
|
numComplete++;
|
||||||
{
|
double percent = numComplete;
|
||||||
IncludeItemTypes = new[] { BaseItemKind.Studio },
|
percent /= count;
|
||||||
IsDeadStudio = true,
|
percent *= 100;
|
||||||
IsLocked = false
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var item in deadEntities)
|
progress.Report(percent);
|
||||||
{
|
|
||||||
_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,255 +8,254 @@ using MediaBrowser.Common.Configuration;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.ScheduledTasks
|
namespace Emby.Server.Implementations.ScheduledTasks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class TaskManager.
|
||||||
|
/// </summary>
|
||||||
|
public class TaskManager : ITaskManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class TaskManager.
|
/// The _task queue.
|
||||||
/// </summary>
|
/// </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>
|
_applicationPaths = applicationPaths;
|
||||||
/// The _task queue.
|
_logger = logger;
|
||||||
/// </summary>
|
|
||||||
private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
|
|
||||||
new ConcurrentQueue<Tuple<Type, TaskOptions>>();
|
|
||||||
|
|
||||||
private readonly IApplicationPaths _applicationPaths;
|
ScheduledTasks = [];
|
||||||
private readonly ILogger<TaskManager> _logger;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="TaskManager" /> class.
|
public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
|
||||||
/// </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;
|
|
||||||
|
|
||||||
ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
|
/// <inheritdoc />
|
||||||
}
|
public event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
|
public IReadOnlyList<IScheduledTaskWorker> ScheduledTasks { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
|
public void CancelIfRunningAndQueue<T>(TaskOptions options)
|
||||||
|
where T : IScheduledTask
|
||||||
|
{
|
||||||
|
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
||||||
|
((ScheduledTaskWorker)task).CancelIfRunning();
|
||||||
|
|
||||||
/// <inheritdoc />
|
QueueScheduledTask<T>(options);
|
||||||
public IReadOnlyList<IScheduledTaskWorker> ScheduledTasks { get; private set; }
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void CancelIfRunningAndQueue<T>(TaskOptions options)
|
public void CancelIfRunningAndQueue<T>()
|
||||||
where T : IScheduledTask
|
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));
|
_logger.LogError("Unable to find scheduled task of type {Type} in QueueScheduledTask.", typeof(T).Name);
|
||||||
((ScheduledTaskWorker)task).CancelIfRunning();
|
|
||||||
|
|
||||||
QueueScheduledTask<T>(options);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <inheritdoc />
|
|
||||||
public void CancelIfRunningAndQueue<T>()
|
|
||||||
where T : IScheduledTask
|
|
||||||
{
|
{
|
||||||
CancelIfRunningAndQueue<T>(new TaskOptions());
|
QueueScheduledTask(scheduledTask, options);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void CancelIfRunning<T>()
|
public void QueueScheduledTask<T>()
|
||||||
where T : IScheduledTask
|
where T : IScheduledTask
|
||||||
{
|
{
|
||||||
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
QueueScheduledTask<T>(new TaskOptions());
|
||||||
((ScheduledTaskWorker)task).CancelIfRunning();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void QueueScheduledTask<T>(TaskOptions options)
|
public void QueueIfNotRunning<T>()
|
||||||
where T : IScheduledTask
|
where T : IScheduledTask
|
||||||
{
|
{
|
||||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
|
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
||||||
|
|
||||||
if (scheduledTask is null)
|
if (task.State != TaskState.Running)
|
||||||
{
|
|
||||||
_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
|
|
||||||
{
|
{
|
||||||
QueueScheduledTask<T>(new TaskOptions());
|
QueueScheduledTask<T>(new TaskOptions());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void QueueIfNotRunning<T>()
|
public void Execute<T>()
|
||||||
where T : IScheduledTask
|
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));
|
_logger.LogError("Unable to find scheduled task of type {Type} in Execute.", typeof(T).Name);
|
||||||
|
|
||||||
if (task.State != TaskState.Running)
|
|
||||||
{
|
|
||||||
QueueScheduledTask<T>(new TaskOptions());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <inheritdoc />
|
|
||||||
public void Execute<T>()
|
|
||||||
where T : IScheduledTask
|
|
||||||
{
|
{
|
||||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
|
var type = scheduledTask.ScheduledTask.GetType();
|
||||||
|
|
||||||
if (scheduledTask is null)
|
_logger.LogDebug("Queuing task {Name}", type.Name);
|
||||||
{
|
|
||||||
_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);
|
|
||||||
|
|
||||||
lock (_taskQueue)
|
lock (_taskQueue)
|
||||||
{
|
{
|
||||||
if (task.State == TaskState.Idle)
|
if (scheduledTask.State == TaskState.Idle)
|
||||||
{
|
{
|
||||||
Execute(task, options);
|
Execute(scheduledTask, new TaskOptions());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_taskQueue.Enqueue(new Tuple<Type, TaskOptions>(type, options));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void AddTasks(IEnumerable<IScheduledTask> tasks)
|
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));
|
_logger.LogError("Unable to find scheduled task of type {Type} in QueueScheduledTask.", task.GetType().Name);
|
||||||
|
|
||||||
ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
{
|
||||||
Dispose(true);
|
QueueScheduledTask(scheduledTask, options);
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Queues the scheduled task.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
/// <param name="task">The task.</param>
|
||||||
protected virtual void Dispose(bool dispose)
|
/// <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 />
|
/// <inheritdoc />
|
||||||
public void Cancel(IScheduledTaskWorker task)
|
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 />
|
/// <inheritdoc />
|
||||||
public Task Execute(IScheduledTaskWorker task, TaskOptions options)
|
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>
|
while (_taskQueue.TryDequeue(out var item))
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
var list = new List<Tuple<Type, TaskOptions>>();
|
if (list.All(i => i.Item1 != item.Item1))
|
||||||
|
|
||||||
while (_taskQueue.TryDequeue(out var item))
|
|
||||||
{
|
{
|
||||||
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);
|
Execute(scheduledTask, enqueuedType.Item2);
|
||||||
|
|
||||||
if (scheduledTask.State == TaskState.Idle)
|
|
||||||
{
|
|
||||||
Execute(scheduledTask, enqueuedType.Item2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,14 +156,11 @@ public partial class AudioNormalizationTask : IScheduledTask
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
{
|
{
|
||||||
return
|
yield return new TaskTriggerInfo
|
||||||
[
|
{
|
||||||
new TaskTriggerInfo
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
{
|
IntervalTicks = TimeSpan.FromHours(24).Ticks
|
||||||
Type = TaskTriggerInfoType.IntervalTrigger,
|
};
|
||||||
IntervalTicks = TimeSpan.FromHours(24).Ticks
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<float?> CalculateLUFSAsync(string inputArgs, bool waitForExit, CancellationToken cancellationToken)
|
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;
|
using var reader = process.StandardError;
|
||||||
float? lufs = null;
|
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);
|
Match match = LUFSRegex().Match(line);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
|
@ -17,155 +17,151 @@ using MediaBrowser.Model.IO;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class ChapterImagesTask.
|
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
|
||||||
/// </summary>
|
/// </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;
|
_logger = logger;
|
||||||
private readonly ILibraryManager _libraryManager;
|
_libraryManager = libraryManager;
|
||||||
private readonly IApplicationPaths _appPaths;
|
_appPaths = appPaths;
|
||||||
private readonly IChapterManager _chapterManager;
|
_chapterManager = chapterManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
_fileSystem = fileSystem;
|
||||||
private readonly ILocalizationManager _localization;
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
|
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
|
||||||
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
|
|
||||||
/// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
|
||||||
public ChapterImagesTask(
|
/// <inheritdoc />
|
||||||
ILogger<ChapterImagesTask> logger,
|
public string Key => "RefreshChapterImages";
|
||||||
ILibraryManager libraryManager,
|
|
||||||
IApplicationPaths appPaths,
|
/// <inheritdoc />
|
||||||
IChapterManager chapterManager,
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
IFileSystem fileSystem,
|
{
|
||||||
ILocalizationManager localization)
|
yield return new TaskTriggerInfo
|
||||||
{
|
{
|
||||||
_logger = logger;
|
Type = TaskTriggerInfoType.DailyTrigger,
|
||||||
_libraryManager = libraryManager;
|
TimeOfDayTicks = TimeSpan.FromHours(2).Ticks,
|
||||||
_appPaths = appPaths;
|
MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks
|
||||||
_chapterManager = chapterManager;
|
};
|
||||||
_fileSystem = fileSystem;
|
}
|
||||||
_localization = localization;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
|
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
/// <inheritdoc />
|
var videos = _libraryManager.GetItemList(new InternalItemsQuery
|
||||||
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => "RefreshChapterImages";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
|
||||||
{
|
{
|
||||||
return
|
MediaTypes = [MediaType.Video],
|
||||||
[
|
IsFolder = false,
|
||||||
new TaskTriggerInfo
|
Recursive = true,
|
||||||
{
|
DtoOptions = new DtoOptions(false)
|
||||||
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],
|
EnableImages = false
|
||||||
IsFolder = false,
|
},
|
||||||
Recursive = true,
|
SourceTypes = [SourceType.Library],
|
||||||
DtoOptions = new DtoOptions(false)
|
IsVirtualItem = false
|
||||||
{
|
})
|
||||||
EnableImages = false
|
.OfType<Video>()
|
||||||
},
|
.ToList();
|
||||||
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)
|
||||||
previouslyFailedImages = (await File.ReadAllTextAsync(failHistoryPath, cancellationToken).ConfigureAwait(false))
|
.ToList();
|
||||||
.Split('|', StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
catch (IOException)
|
|
||||||
{
|
|
||||||
previouslyFailedImages = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
catch (IOException)
|
||||||
{
|
{
|
||||||
previouslyFailedImages = [];
|
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);
|
if (!success)
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var chapters = _chapterManager.GetChapters(video.Id);
|
previouslyFailedImages.Add(key);
|
||||||
|
|
||||||
var success = await _chapterManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false);
|
var parentPath = Path.GetDirectoryName(failHistoryPath);
|
||||||
|
if (parentPath is not null)
|
||||||
if (!success)
|
|
||||||
{
|
{
|
||||||
previouslyFailedImages.Add(key);
|
Directory.CreateDirectory(parentPath);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
numComplete++;
|
string text = string.Join('|', previouslyFailedImages);
|
||||||
double percent = numComplete;
|
await File.WriteAllTextAsync(failHistoryPath, text, cancellationToken).ConfigureAwait(false);
|
||||||
percent /= videos.Count;
|
}
|
||||||
|
|
||||||
progress.Report(100 * percent);
|
numComplete++;
|
||||||
}
|
double percent = numComplete;
|
||||||
catch (ObjectDisposedException ex)
|
percent /= videos.Count;
|
||||||
{
|
|
||||||
// TODO Investigate and properly fix.
|
progress.Report(100 * percent);
|
||||||
_logger.LogError(ex, "Object Disposed");
|
}
|
||||||
break;
|
catch (ObjectDisposedException ex)
|
||||||
}
|
{
|
||||||
|
// TODO Investigate and properly fix.
|
||||||
|
_logger.LogError(ex, "Object Disposed");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,71 +7,70 @@ using MediaBrowser.Model.Activity;
|
|||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Tasks;
|
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>
|
/// <summary>
|
||||||
/// Deletes old activity log entries.
|
/// Initializes a new instance of the <see cref="CleanActivityLogTask"/> class.
|
||||||
/// </summary>
|
/// </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;
|
_localization = localization;
|
||||||
private readonly IActivityManager _activityManager;
|
_activityManager = activityManager;
|
||||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="CleanActivityLogTask"/> class.
|
public string Name => _localization.GetLocalizedString("TaskCleanActivityLog");
|
||||||
/// </summary>
|
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param>
|
public string Key => "CleanActivityLog";
|
||||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
|
||||||
public CleanActivityLogTask(
|
/// <inheritdoc />
|
||||||
ILocalizationManager localization,
|
public string Description => _localization.GetLocalizedString("TaskCleanActivityLogDescription");
|
||||||
IActivityManager activityManager,
|
|
||||||
IServerConfigurationManager serverConfigurationManager)
|
/// <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;
|
throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
|
||||||
_activityManager = activityManager;
|
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
|
||||||
public string Name => _localization.GetLocalizedString("TaskCleanActivityLog");
|
return _activityManager.CleanAsync(startDate);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => "CleanActivityLog";
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
|
{
|
||||||
/// <inheritdoc />
|
return [];
|
||||||
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 [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
|
|||||||
private readonly IPlaylistManager _playlistManager;
|
private readonly IPlaylistManager _playlistManager;
|
||||||
private readonly ILogger<CleanupCollectionAndPlaylistPathsTask> _logger;
|
private readonly ILogger<CleanupCollectionAndPlaylistPathsTask> _logger;
|
||||||
private readonly IProviderManager _providerManager;
|
private readonly IProviderManager _providerManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="CleanupCollectionAndPlaylistPathsTask"/> class.
|
/// 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="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param>
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger"/> 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="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
|
||||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
|
||||||
public CleanupCollectionAndPlaylistPathsTask(
|
public CleanupCollectionAndPlaylistPathsTask(
|
||||||
ILocalizationManager localization,
|
ILocalizationManager localization,
|
||||||
ICollectionManager collectionManager,
|
ICollectionManager collectionManager,
|
||||||
IPlaylistManager playlistManager,
|
IPlaylistManager playlistManager,
|
||||||
ILogger<CleanupCollectionAndPlaylistPathsTask> logger,
|
ILogger<CleanupCollectionAndPlaylistPathsTask> logger,
|
||||||
IProviderManager providerManager,
|
IProviderManager providerManager)
|
||||||
IFileSystem fileSystem)
|
|
||||||
{
|
{
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_collectionManager = collectionManager;
|
_collectionManager = collectionManager;
|
||||||
_playlistManager = playlistManager;
|
_playlistManager = playlistManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_providerManager = providerManager;
|
_providerManager = providerManager;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -135,6 +131,9 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
{
|
{
|
||||||
return [new TaskTriggerInfo() { Type = TaskTriggerInfoType.StartupTrigger }];
|
yield return new TaskTriggerInfo
|
||||||
|
{
|
||||||
|
Type = TaskTriggerInfoType.StartupTrigger,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,134 +11,133 @@ using MediaBrowser.Model.IO;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Deletes old cache files.
|
/// Gets or sets the application paths.
|
||||||
/// </summary>
|
/// </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>
|
_applicationPaths = appPaths;
|
||||||
/// Gets or sets the application paths.
|
_logger = logger;
|
||||||
/// </summary>
|
_fileSystem = fileSystem;
|
||||||
/// <value>The application paths.</value>
|
_localization = localization;
|
||||||
private readonly IApplicationPaths _applicationPaths;
|
}
|
||||||
private readonly ILogger<DeleteCacheFileTask> _logger;
|
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private readonly ILocalizationManager _localization;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
|
public string Name => _localization.GetLocalizedString("TaskCleanCache");
|
||||||
/// </summary>
|
|
||||||
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription");
|
||||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
public DeleteCacheFileTask(
|
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
|
||||||
IApplicationPaths appPaths,
|
|
||||||
ILogger<DeleteCacheFileTask> logger,
|
/// <inheritdoc />
|
||||||
IFileSystem fileSystem,
|
public string Key => "DeleteCacheFiles";
|
||||||
ILocalizationManager localization)
|
|
||||||
|
/// <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;
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
_logger = logger;
|
IntervalTicks = TimeSpan.FromHours(24).Ticks
|
||||||
_fileSystem = fileSystem;
|
};
|
||||||
_localization = localization;
|
}
|
||||||
|
|
||||||
|
/// <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 />
|
progress.Report(90);
|
||||||
public string Name => _localization.GetLocalizedString("TaskCleanCache");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
minDateModified = DateTime.UtcNow.AddDays(-1);
|
||||||
public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
try
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
return
|
DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken);
|
||||||
[
|
}
|
||||||
// Every so often
|
catch (DirectoryNotFoundException)
|
||||||
new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks }
|
{
|
||||||
];
|
// No biggie here. Nothing to delete
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
return Task.CompletedTask;
|
||||||
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
}
|
||||||
|
|
||||||
|
/// <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
|
progress.Report(100 * percent);
|
||||||
{
|
|
||||||
DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException)
|
|
||||||
{
|
|
||||||
// No biggie here. Nothing to delete
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(90);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
minDateModified = DateTime.UtcNow.AddDays(-1);
|
FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
|
||||||
|
|
||||||
try
|
index++;
|
||||||
{
|
|
||||||
DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException)
|
|
||||||
{
|
|
||||||
// No biggie here. Nothing to delete
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
|
||||||
/// 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;
|
progress.Report(100);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,93 +9,93 @@ using MediaBrowser.Model.Globalization;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Tasks;
|
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>
|
/// <summary>
|
||||||
/// Deletes old log files.
|
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
|
||||||
/// </summary>
|
/// </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;
|
_configurationManager = configurationManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
_fileSystem = fileSystem;
|
||||||
private readonly ILocalizationManager _localization;
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
|
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
|
||||||
/// </summary>
|
|
||||||
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
public string Description => string.Format(
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
CultureInfo.InvariantCulture,
|
||||||
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
|
_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;
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
_fileSystem = fileSystem;
|
IntervalTicks = TimeSpan.FromHours(24).Ticks
|
||||||
_localization = localization;
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 />
|
progress.Report(100);
|
||||||
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
return Task.CompletedTask;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,118 +10,115 @@ using MediaBrowser.Model.IO;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Deletes all transcoding temp files.
|
/// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class.
|
||||||
/// </summary>
|
/// </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;
|
_logger = logger;
|
||||||
private readonly IConfigurationManager _configurationManager;
|
_fileSystem = fileSystem;
|
||||||
private readonly IFileSystem _fileSystem;
|
_configurationManager = configurationManager;
|
||||||
private readonly ILocalizationManager _localization;
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class.
|
public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger{DeleteTranscodeFileTask}"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
|
||||||
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
|
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
public DeleteTranscodeFileTask(
|
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
|
||||||
ILogger<DeleteTranscodeFileTask> logger,
|
|
||||||
IFileSystem fileSystem,
|
/// <inheritdoc />
|
||||||
IConfigurationManager configurationManager,
|
public string Key => "DeleteTranscodeFiles";
|
||||||
ILocalizationManager localization)
|
|
||||||
|
/// <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;
|
Type = TaskTriggerInfoType.StartupTrigger
|
||||||
_fileSystem = fileSystem;
|
};
|
||||||
_configurationManager = configurationManager;
|
|
||||||
_localization = localization;
|
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 />
|
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
|
||||||
public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
progress.Report(100);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,11 @@ public class MediaSegmentExtractionTask : IScheduledTask
|
|||||||
|
|
||||||
var query = new InternalItemsQuery
|
var query = new InternalItemsQuery
|
||||||
{
|
{
|
||||||
MediaTypes = new[] { MediaType.Video, MediaType.Audio },
|
MediaTypes = [MediaType.Video, MediaType.Audio],
|
||||||
IsVirtualItem = false,
|
IsVirtualItem = false,
|
||||||
IncludeItemTypes = _itemTypes,
|
IncludeItemTypes = _itemTypes,
|
||||||
DtoOptions = new DtoOptions(true),
|
DtoOptions = new DtoOptions(true),
|
||||||
SourceTypes = new[] { SourceType.Library },
|
SourceTypes = [SourceType.Library],
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
Limit = pagesize
|
Limit = pagesize
|
||||||
};
|
};
|
||||||
|
@ -5,84 +5,78 @@ using System.Threading.Tasks;
|
|||||||
using Jellyfin.Database.Implementations;
|
using Jellyfin.Database.Implementations;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Optimizes Jellyfin's database by issuing a VACUUM command.
|
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
|
||||||
/// </summary>
|
/// </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;
|
_logger = logger;
|
||||||
private readonly ILocalizationManager _localization;
|
_localization = localization;
|
||||||
private readonly IDbContextFactory<JellyfinDbContext> _provider;
|
_jellyfinDatabaseProvider = jellyfinDatabaseProvider;
|
||||||
private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
|
public string Name => _localization.GetLocalizedString("TaskOptimizeDatabase");
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
public string Description => _localization.GetLocalizedString("TaskOptimizeDatabaseDescription");
|
||||||
/// <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>
|
/// <inheritdoc />
|
||||||
public OptimizeDatabaseTask(
|
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
|
||||||
ILogger<OptimizeDatabaseTask> logger,
|
|
||||||
ILocalizationManager localization,
|
/// <inheritdoc />
|
||||||
IDbContextFactory<JellyfinDbContext> provider,
|
public string Key => "OptimizeDatabaseTask";
|
||||||
IJellyfinDatabaseProvider jellyfinDatabaseProvider)
|
|
||||||
|
/// <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;
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
_localization = localization;
|
IntervalTicks = TimeSpan.FromHours(24).Ticks
|
||||||
_provider = provider;
|
};
|
||||||
_jellyfinDatabaseProvider = jellyfinDatabaseProvider;
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
/// <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()
|
|
||||||
{
|
{
|
||||||
return
|
_logger.LogError(e, "Error while optimizing jellyfin.db");
|
||||||
[
|
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,68 +6,64 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Tasks;
|
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>
|
/// <summary>
|
||||||
/// Class PeopleValidationTask.
|
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
|
||||||
/// </summary>
|
/// </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;
|
_libraryManager = libraryManager;
|
||||||
private readonly ILocalizationManager _localization;
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
|
public string Name => _localization.GetLocalizedString("TaskRefreshPeople");
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription");
|
||||||
public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization)
|
|
||||||
|
/// <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;
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
_localization = localization;
|
IntervalTicks = TimeSpan.FromDays(7).Ticks
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => _localization.GetLocalizedString("TaskRefreshPeople");
|
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
/// <inheritdoc />
|
return _libraryManager.ValidatePeopleAsync(progress, cancellationToken);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,111 +10,115 @@ using MediaBrowser.Model.Globalization;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Plugin Update Task.
|
/// Initializes a new instance of the <see cref="PluginUpdateTask" /> class.
|
||||||
/// </summary>
|
/// </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;
|
/// <inheritdoc />
|
||||||
private readonly ILocalizationManager _localization;
|
public string Name => _localization.GetLocalizedString("TaskUpdatePlugins");
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="PluginUpdateTask" /> class.
|
public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription");
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
|
public string Category => _localization.GetLocalizedString("TasksApplicationCategory");
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
|
||||||
public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization)
|
/// <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;
|
Type = TaskTriggerInfoType.StartupTrigger
|
||||||
_installationManager = installationManager;
|
};
|
||||||
_localization = localization;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
yield return new TaskTriggerInfo
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
// At startup
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.StartupTrigger };
|
IntervalTicks = TimeSpan.FromHours(24).Ticks
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Every so often
|
/// <inheritdoc />
|
||||||
yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks };
|
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
}
|
{
|
||||||
|
progress.Report(0);
|
||||||
|
|
||||||
/// <inheritdoc />
|
var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken);
|
||||||
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken 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);
|
try
|
||||||
var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList();
|
|
||||||
|
|
||||||
progress.Report(10);
|
|
||||||
|
|
||||||
var numComplete = 0;
|
|
||||||
|
|
||||||
foreach (var package in packagesToInstall)
|
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
try
|
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);
|
throw;
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,60 +7,59 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Tasks;
|
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>
|
/// <summary>
|
||||||
/// Class RefreshMediaLibraryTask.
|
/// The _library manager.
|
||||||
/// </summary>
|
/// </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>
|
_libraryManager = libraryManager;
|
||||||
/// The _library manager.
|
_localization = localization;
|
||||||
/// </summary>
|
}
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
private readonly ILocalizationManager _localization;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
|
public string Name => _localization.GetLocalizedString("TaskRefreshLibrary");
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription");
|
||||||
public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization)
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => "RefreshLibrary";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
|
{
|
||||||
|
yield return new TaskTriggerInfo
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||||
_localization = localization;
|
IntervalTicks = TimeSpan.FromHours(12).Ticks
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => _localization.GetLocalizedString("TaskRefreshLibrary");
|
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
/// <inheritdoc />
|
progress.Report(0);
|
||||||
public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,85 +3,84 @@ using System.Threading;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Represents a task trigger that fires everyday.
|
/// Initializes a new instance of the <see cref="DailyTrigger"/> class.
|
||||||
/// </summary>
|
/// </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;
|
_timeOfDay = timeOfDay;
|
||||||
private Timer? _timer;
|
TaskOptions = taskOptions;
|
||||||
private bool _disposed = false;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="DailyTrigger"/> class.
|
public event EventHandler<EventArgs>? Triggered;
|
||||||
/// </summary>
|
|
||||||
/// <param name="timeofDay">The time of day to trigger the task to run.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="taskOptions">The options of this task.</param>
|
public TaskOptions TaskOptions { get; }
|
||||||
public DailyTrigger(TimeSpan timeofDay, TaskOptions taskOptions)
|
|
||||||
|
/// <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;
|
return;
|
||||||
TaskOptions = taskOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
DisposeTimer();
|
||||||
public event EventHandler<EventArgs>? Triggered;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
_disposed = true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,104 +4,103 @@ using System.Threading;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Represents a task trigger that runs repeatedly on an interval.
|
/// Initializes a new instance of the <see cref="IntervalTrigger"/> class.
|
||||||
/// </summary>
|
/// </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;
|
_interval = interval;
|
||||||
private DateTime _lastStartDate;
|
TaskOptions = taskOptions;
|
||||||
private Timer? _timer;
|
}
|
||||||
private bool _disposed = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="IntervalTrigger"/> class.
|
public event EventHandler<EventArgs>? Triggered;
|
||||||
/// </summary>
|
|
||||||
/// <param name="interval">The interval.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="taskOptions">The options of this task.</param>
|
public TaskOptions TaskOptions { get; }
|
||||||
public IntervalTrigger(TimeSpan interval, TaskOptions taskOptions)
|
|
||||||
|
/// <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;
|
// Task has never been completed before
|
||||||
TaskOptions = taskOptions;
|
triggerDate = now.AddHours(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
triggerDate = new[] { lastResult.EndTimeUtc, _lastStartDate, now.AddMinutes(1) }.Max().Add(_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
var dueTime = triggerDate - now;
|
||||||
public event EventHandler<EventArgs>? Triggered;
|
var maxDueTime = TimeSpan.FromDays(7);
|
||||||
|
|
||||||
/// <inheritdoc />
|
if (dueTime > maxDueTime)
|
||||||
public TaskOptions TaskOptions { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
|
||||||
{
|
{
|
||||||
DisposeTimer();
|
dueTime = maxDueTime;
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
_timer = new Timer(_ => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
|
||||||
public void Stop()
|
}
|
||||||
|
|
||||||
|
/// <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();
|
_lastStartDate = DateTime.UtcNow;
|
||||||
}
|
Triggered(this, EventArgs.Empty);
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeTimer();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,52 +3,51 @@ using System.Threading.Tasks;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Class StartupTaskTrigger.
|
/// Initializes a new instance of the <see cref="StartupTrigger"/> class.
|
||||||
/// </summary>
|
/// </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>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="StartupTrigger"/> class.
|
public event EventHandler<EventArgs>? Triggered;
|
||||||
/// </summary>
|
|
||||||
/// <param name="taskOptions">The options of this task.</param>
|
/// <inheritdoc />
|
||||||
public StartupTrigger(TaskOptions taskOptions)
|
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 />
|
OnTriggered();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when [triggered].
|
||||||
|
/// </summary>
|
||||||
|
private void OnTriggered()
|
||||||
|
{
|
||||||
|
Triggered?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,108 +3,107 @@ using System.Threading;
|
|||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Represents a task trigger that fires on a weekly basis.
|
/// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
|
||||||
/// </summary>
|
/// </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;
|
_timeOfDay = timeOfDay;
|
||||||
private readonly DayOfWeek _dayOfWeek;
|
_dayOfWeek = dayOfWeek;
|
||||||
private Timer? _timer;
|
TaskOptions = taskOptions;
|
||||||
private bool _disposed;
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
|
public event EventHandler<EventArgs>? Triggered;
|
||||||
/// </summary>
|
|
||||||
/// <param name="timeofDay">The time of day to trigger the task to run.</param>
|
/// <inheritdoc />
|
||||||
/// <param name="dayOfWeek">The day of week.</param>
|
public TaskOptions TaskOptions { get; }
|
||||||
/// <param name="taskOptions">The options of this task.</param>
|
|
||||||
public WeeklyTrigger(TimeSpan timeofDay, DayOfWeek dayOfWeek, TaskOptions taskOptions)
|
/// <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;
|
// It's either later today, or a week from now
|
||||||
_dayOfWeek = dayOfWeek;
|
return now.TimeOfDay < _timeOfDay ? now.Date.Add(_timeOfDay) : now.Date.AddDays(7).Add(_timeOfDay);
|
||||||
TaskOptions = taskOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
var triggerDate = now.Date;
|
||||||
public event EventHandler<EventArgs>? Triggered;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
// Walk the date forward until we get to the trigger day
|
||||||
public TaskOptions TaskOptions { get; }
|
while (triggerDate.DayOfWeek != _dayOfWeek)
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
|
||||||
{
|
{
|
||||||
DisposeTimer();
|
triggerDate = triggerDate.AddDays(1);
|
||||||
|
|
||||||
var triggerDate = GetNextTriggerDateTime();
|
|
||||||
|
|
||||||
_timer = new Timer(_ => OnTriggered(), null, triggerDate - DateTime.Now, TimeSpan.FromMilliseconds(-1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Return the trigger date plus the time offset
|
||||||
/// Gets the next trigger date time.
|
return triggerDate.Add(_timeOfDay);
|
||||||
/// </summary>
|
}
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
private DateTime GetNextTriggerDateTime()
|
/// <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;
|
return;
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
DisposeTimer();
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
DisposeTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
_disposed = true;
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user