UX Changes, Tasks, WebP, and More! (#1280)
* When account updates occur for a user, send an event to them to tell them to refresh their account information (if they are on the site at the time). This way if we revoke permissions, the site will reactively adapt. * Some cleanup on the user preferences to remove some calls we don't need anymore. * Removed old bulk cleanup bookmark code as it's no longer needed. * Tweaked the messaging for stat collection to reflect what we collect now versus when this was initially implemented. * Implemented the ability for users to configure their servers to save bookmarks as webP. Reorganized the tabs for Admin dashboard to account for upcoming features. * Implemented the ability to bulk convert bookmarks (as many times as the user wants). Added a display of Reoccurring Jobs to the Tasks admin tab. Currently it's just placeholder, but will be enhanced further later in the release. * Tweaked the wording around the convert switch. * Moved System actions to the task tab * Added a controller just for Tachiyomi so we can have dedicated APIs for that client. Deprecated an existing API on the Reader route. * Fixed the unit tests
This commit is contained in:
parent
dd83b6a9a1
commit
e0a2fc615f
51 changed files with 971 additions and 271 deletions
|
@ -8,6 +8,7 @@ using API.DTOs.Reader;
|
|||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.SignalR;
|
||||
using Hangfire;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services;
|
||||
|
@ -18,6 +19,7 @@ public interface IBookmarkService
|
|||
Task<bool> BookmarkPage(AppUser userWithBookmarks, BookmarkDto bookmarkDto, string imageToBookmark);
|
||||
Task<bool> RemoveBookmarkPage(AppUser userWithBookmarks, BookmarkDto bookmarkDto);
|
||||
Task<IEnumerable<string>> GetBookmarkFilesById(IEnumerable<int> bookmarkIds);
|
||||
Task ConvertAllBookmarkToWebP();
|
||||
|
||||
}
|
||||
|
||||
|
@ -26,12 +28,17 @@ public class BookmarkService : IBookmarkService
|
|||
private readonly ILogger<BookmarkService> _logger;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IDirectoryService _directoryService;
|
||||
private readonly IImageService _imageService;
|
||||
private readonly IEventHub _eventHub;
|
||||
|
||||
public BookmarkService(ILogger<BookmarkService> logger, IUnitOfWork unitOfWork, IDirectoryService directoryService)
|
||||
public BookmarkService(ILogger<BookmarkService> logger, IUnitOfWork unitOfWork,
|
||||
IDirectoryService directoryService, IImageService imageService, IEventHub eventHub)
|
||||
{
|
||||
_logger = logger;
|
||||
_unitOfWork = unitOfWork;
|
||||
_directoryService = directoryService;
|
||||
_imageService = imageService;
|
||||
_eventHub = eventHub;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -87,18 +94,28 @@ public class BookmarkService : IBookmarkService
|
|||
var targetFolderStem = BookmarkStem(userWithBookmarks.Id, bookmarkDto.SeriesId, bookmarkDto.ChapterId);
|
||||
var targetFilepath = Path.Join(bookmarkDirectory, targetFolderStem);
|
||||
|
||||
userWithBookmarks.Bookmarks ??= new List<AppUserBookmark>();
|
||||
userWithBookmarks.Bookmarks.Add(new AppUserBookmark()
|
||||
var bookmark = new AppUserBookmark()
|
||||
{
|
||||
Page = bookmarkDto.Page,
|
||||
VolumeId = bookmarkDto.VolumeId,
|
||||
SeriesId = bookmarkDto.SeriesId,
|
||||
ChapterId = bookmarkDto.ChapterId,
|
||||
FileName = Path.Join(targetFolderStem, fileInfo.Name)
|
||||
});
|
||||
};
|
||||
|
||||
_directoryService.CopyFileToDirectory(imageToBookmark, targetFilepath);
|
||||
userWithBookmarks.Bookmarks ??= new List<AppUserBookmark>();
|
||||
userWithBookmarks.Bookmarks.Add(bookmark);
|
||||
|
||||
_unitOfWork.UserRepository.Update(userWithBookmarks);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
var convertToWebP = bool.Parse((await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.ConvertBookmarkToWebP)).Value);
|
||||
if (convertToWebP)
|
||||
{
|
||||
// Enqueue a task to convert the bookmark to webP
|
||||
BackgroundJob.Enqueue(() => ConvertBookmarkToWebP(bookmark.Id));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -153,6 +170,94 @@ public class BookmarkService : IBookmarkService
|
|||
b.FileName)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a long-running job that will convert all bookmarks into WebP. Do not invoke anyway except via Hangfire.
|
||||
/// </summary>
|
||||
[DisableConcurrentExecution(timeoutInSeconds: 2 * 60 * 60), AutomaticRetry(Attempts = 0)]
|
||||
public async Task ConvertAllBookmarkToWebP()
|
||||
{
|
||||
var bookmarkDirectory =
|
||||
(await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.BookmarkDirectory)).Value;
|
||||
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.ConvertBookmarksProgressEvent(0F, ProgressEventType.Started));
|
||||
var bookmarks = (await _unitOfWork.UserRepository.GetAllBookmarksAsync())
|
||||
.Where(b => !b.FileName.EndsWith(".webp")).ToList();
|
||||
|
||||
var count = 1F;
|
||||
foreach (var bookmark in bookmarks)
|
||||
{
|
||||
await SaveBookmarkAsWebP(bookmarkDirectory, bookmark);
|
||||
await _unitOfWork.CommitAsync();
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.ConvertBookmarksProgressEvent(count / bookmarks.Count, ProgressEventType.Started));
|
||||
count++;
|
||||
}
|
||||
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.ConvertBookmarksProgressEvent(1F, ProgressEventType.Ended));
|
||||
|
||||
_logger.LogInformation("[BookmarkService] Converted bookmarks to WebP");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a job that runs after a bookmark is saved
|
||||
/// </summary>
|
||||
public async Task ConvertBookmarkToWebP(int bookmarkId)
|
||||
{
|
||||
var bookmarkDirectory =
|
||||
(await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.BookmarkDirectory)).Value;
|
||||
var convertBookmarkToWebP =
|
||||
(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).ConvertBookmarkToWebP;
|
||||
|
||||
if (!convertBookmarkToWebP) return;
|
||||
|
||||
// Validate the bookmark still exists
|
||||
var bookmark = await _unitOfWork.UserRepository.GetBookmarkAsync(bookmarkId);
|
||||
if (bookmark == null) return;
|
||||
|
||||
await SaveBookmarkAsWebP(bookmarkDirectory, bookmark);
|
||||
await _unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts bookmark file, deletes original, marks bookmark as dirty. Does not commit.
|
||||
/// </summary>
|
||||
/// <param name="bookmarkDirectory"></param>
|
||||
/// <param name="bookmark"></param>
|
||||
private async Task SaveBookmarkAsWebP(string bookmarkDirectory, AppUserBookmark bookmark)
|
||||
{
|
||||
var fullSourcePath = _directoryService.FileSystem.Path.Join(bookmarkDirectory, bookmark.FileName);
|
||||
var fullTargetDirectory = fullSourcePath.Replace(new FileInfo(bookmark.FileName).Name, string.Empty);
|
||||
var targetFolderStem = BookmarkStem(bookmark.AppUserId, bookmark.SeriesId, bookmark.ChapterId);
|
||||
|
||||
_logger.LogDebug("Converting {Source} bookmark into WebP at {Target}", fullSourcePath, fullTargetDirectory);
|
||||
|
||||
try
|
||||
{
|
||||
// Convert target file to webp then delete original target file and update bookmark
|
||||
|
||||
var originalFile = bookmark.FileName;
|
||||
try
|
||||
{
|
||||
var targetFile = await _imageService.ConvertToWebP(fullSourcePath, fullTargetDirectory);
|
||||
var targetName = new FileInfo(targetFile).Name;
|
||||
bookmark.FileName = Path.Join(targetFolderStem, targetName);
|
||||
_directoryService.DeleteFiles(new[] {fullSourcePath});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not convert file {FilePath}", bookmark.FileName);
|
||||
bookmark.FileName = originalFile;
|
||||
}
|
||||
_unitOfWork.UserRepository.Update(bookmark);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not convert bookmark to WebP");
|
||||
}
|
||||
}
|
||||
|
||||
private static string BookmarkStem(int userId, int seriesId, int chapterId)
|
||||
{
|
||||
return Path.Join($"{userId}", $"{seriesId}", $"{chapterId}");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue