Feature/progress widget (#760)

* Implemented a new widget to show when operations are occuring in the backend (tasks + progress events). Fixed an oversight on progress reporting where I sent 100F instead of 1F.

* Hooked in more progress events for tasks on the backend. Cleaned up code and integrated some RBS into it. CSS needed.

* Show a colored icon when events are active

* Added some styling to the progress widget
This commit is contained in:
Joseph Milazzo 2021-11-15 16:50:14 -06:00 committed by GitHub
parent a94fdbc9cb
commit 281352001d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 255 additions and 16 deletions

View file

@ -280,13 +280,13 @@ namespace API.Services
"[MetadataService] Processed {SeriesStart} - {SeriesEnd} out of {TotalSeries} series in {ElapsedScanTime} milliseconds for {LibraryName}",
chunk * chunkInfo.ChunkSize, (chunk * chunkInfo.ChunkSize) + nonLibrarySeries.Count, chunkInfo.TotalSize, stopwatch.ElapsedMilliseconds, library.Name);
}
var progress = Math.Max(0F, Math.Min(100F, i * 1F / chunkInfo.TotalChunks));
var progress = Math.Max(0F, Math.Min(1F, i * 1F / chunkInfo.TotalChunks));
await _messageHub.Clients.All.SendAsync(SignalREvents.RefreshMetadataProgress,
MessageFactory.RefreshMetadataProgressEvent(library.Id, progress));
}
await _messageHub.Clients.All.SendAsync(SignalREvents.RefreshMetadataProgress,
MessageFactory.RefreshMetadataProgressEvent(library.Id, 100F));
MessageFactory.RefreshMetadataProgressEvent(library.Id, 1F));
_logger.LogInformation("[MetadataService] Updated metadata for {SeriesNumber} series in library {LibraryName} in {ElapsedMilliseconds} milliseconds total", chunkInfo.TotalSize, library.Name, totalTime);
}

View file

@ -8,7 +8,9 @@ using API.Entities.Enums;
using API.Extensions;
using API.Interfaces;
using API.Interfaces.Services;
using API.SignalR;
using Hangfire;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
@ -19,14 +21,17 @@ namespace API.Services.Tasks
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<BackupService> _logger;
private readonly IDirectoryService _directoryService;
private readonly IHubContext<MessageHub> _messageHub;
private readonly IList<string> _backupFiles;
public BackupService(IUnitOfWork unitOfWork, ILogger<BackupService> logger, IDirectoryService directoryService, IConfiguration config)
public BackupService(IUnitOfWork unitOfWork, ILogger<BackupService> logger,
IDirectoryService directoryService, IConfiguration config, IHubContext<MessageHub> messageHub)
{
_unitOfWork = unitOfWork;
_logger = logger;
_directoryService = directoryService;
_messageHub = messageHub;
var maxRollingFiles = config.GetMaxRollingFiles();
var loggingSection = config.GetLoggingFileName();
@ -76,6 +81,8 @@ namespace API.Services.Tasks
return;
}
await SendProgress(0F);
var dateString = $"{DateTime.Now.ToShortDateString()}_{DateTime.Now.ToLongTimeString()}".Replace("/", "_").Replace(":", "_");
var zipPath = Path.Join(backupDirectory, $"kavita_backup_{dateString}.zip");
@ -92,8 +99,12 @@ namespace API.Services.Tasks
_directoryService.CopyFilesToDirectory(
_backupFiles.Select(file => Path.Join(DirectoryService.ConfigDirectory, file)).ToList(), tempDirectory);
await SendProgress(0.25F);
await CopyCoverImagesToBackupDirectory(tempDirectory);
await SendProgress(0.75F);
try
{
ZipFile.CreateFromDirectory(tempDirectory, zipPath);
@ -105,6 +116,7 @@ namespace API.Services.Tasks
DirectoryService.ClearAndDeleteDirectory(tempDirectory);
_logger.LogInformation("Database backup completed");
await SendProgress(1F);
}
private async Task CopyCoverImagesToBackupDirectory(string tempDirectory)
@ -137,6 +149,12 @@ namespace API.Services.Tasks
}
}
private async Task SendProgress(float progress)
{
await _messageHub.Clients.All.SendAsync(SignalREvents.BackupDatabaseProgress,
MessageFactory.BackupDatabaseProgressEvent(progress));
}
/// <summary>
/// Removes Database backups older than 30 days. If all backups are older than 30 days, the latest is kept.
/// </summary>

View file

@ -2,7 +2,9 @@
using System.Threading.Tasks;
using API.Interfaces;
using API.Interfaces.Services;
using API.SignalR;
using Hangfire;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
namespace API.Services.Tasks
@ -16,14 +18,16 @@ namespace API.Services.Tasks
private readonly ILogger<CleanupService> _logger;
private readonly IBackupService _backupService;
private readonly IUnitOfWork _unitOfWork;
private readonly IHubContext<MessageHub> _messageHub;
public CleanupService(ICacheService cacheService, ILogger<CleanupService> logger,
IBackupService backupService, IUnitOfWork unitOfWork)
IBackupService backupService, IUnitOfWork unitOfWork, IHubContext<MessageHub> messageHub)
{
_cacheService = cacheService;
_logger = logger;
_backupService = backupService;
_unitOfWork = unitOfWork;
_messageHub = messageHub;
}
public void CleanupCacheDirectory()
@ -39,19 +43,31 @@ namespace API.Services.Tasks
public async Task Cleanup()
{
_logger.LogInformation("Starting Cleanup");
await SendProgress(0F);
_logger.LogInformation("Cleaning temp directory");
var tempDirectory = DirectoryService.TempDirectory;
DirectoryService.ClearDirectory(tempDirectory);
DirectoryService.ClearDirectory(DirectoryService.TempDirectory);
await SendProgress(0.1F);
CleanupCacheDirectory();
await SendProgress(0.25F);
_logger.LogInformation("Cleaning old database backups");
_backupService.CleanupBackups();
await SendProgress(0.50F);
_logger.LogInformation("Cleaning deleted cover images");
await DeleteSeriesCoverImages();
await SendProgress(0.6F);
await DeleteChapterCoverImages();
await SendProgress(0.7F);
await DeleteTagCoverImages();
await SendProgress(1F);
_logger.LogInformation("Cleanup finished");
}
private async Task SendProgress(float progress)
{
await _messageHub.Clients.All.SendAsync(SignalREvents.CleanupProgress,
MessageFactory.CleanupProgressEvent(progress));
}
private async Task DeleteSeriesCoverImages()
{
var images = await _unitOfWork.SeriesRepository.GetAllCoverImagesAsync();

View file

@ -244,7 +244,7 @@ namespace API.Services.Tasks
BackgroundJob.Enqueue(() => _metadataService.RefreshMetadata(libraryId, false));
await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress,
MessageFactory.ScanLibraryProgressEvent(libraryId, 100));
MessageFactory.ScanLibraryProgressEvent(libraryId, 1F));
}
/// <summary>
@ -342,7 +342,7 @@ namespace API.Services.Tasks
await _messageHub.Clients.All.SendAsync(SignalREvents.SeriesRemoved, MessageFactory.SeriesRemovedEvent(missing.Id, missing.Name, library.Id));
}
var progress = Math.Max(0, Math.Min(100, ((chunk + 1F) * chunkInfo.ChunkSize) / chunkInfo.TotalSize));
var progress = Math.Max(0, Math.Min(1, ((chunk + 1F) * chunkInfo.ChunkSize) / chunkInfo.TotalSize));
await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress,
MessageFactory.ScanLibraryProgressEvent(library.Id, progress));
}
@ -405,7 +405,7 @@ namespace API.Services.Tasks
series.Name, $"{series.Name}_{series.NormalizedName}_{series.LocalizedName}_{series.LibraryId}_{series.Format}");
}
var progress = Math.Max(0F, Math.Min(100F, i * 1F / newSeries.Count));
var progress = Math.Max(0F, Math.Min(1F, i * 1F / newSeries.Count));
await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress,
MessageFactory.ScanLibraryProgressEvent(library.Id, progress));
i++;

View file

@ -89,6 +89,31 @@ namespace API.SignalR
};
}
public static SignalRMessage BackupDatabaseProgressEvent(float progress)
{
return new SignalRMessage()
{
Name = SignalREvents.BackupDatabaseProgress,
Body = new
{
Progress = progress
}
};
}
public static SignalRMessage CleanupProgressEvent(float progress)
{
return new SignalRMessage()
{
Name = SignalREvents.CleanupProgress,
Body = new
{
Progress = progress
}
};
}
public static SignalRMessage UpdateVersionEvent(UpdateNotificationDto update)
{
return new SignalRMessage

View file

@ -19,5 +19,13 @@
public const string OnlineUsers = "OnlineUsers";
public const string SeriesAddedToCollection = "SeriesAddedToCollection";
public const string ScanLibraryError = "ScanLibraryError";
/// <summary>
/// Event sent out during backing up the database
/// </summary>
public const string BackupDatabaseProgress = "BackupDatabaseProgress";
/// <summary>
/// Event sent out during cleaning up temp and cache folders
/// </summary>
public const string CleanupProgress = "CleanupProgress";
}
}