mirror of
				https://github.com/Kareadita/Kavita.git
				synced 2025-11-04 03:27:05 -05:00 
			
		
		
		
	* Started designing the backend localization service * Worked in Transloco for initial PoC * Worked in Transloco for initial PoC * Translated the login screen * translated dashboard screen * Started work on the backend * Fixed a logic bug * translated edit-user screen * Hooked up the backend for having a locale property. * Hooked up the ability to view the available locales and switch to them. * Made the localization service languages be derived from what's in langs/ directory. * Fixed up localization switching * Switched when we check for a license on UI bootstrap * Tweaked some code * Fixed the bug where dashboard wasn't loading and made it so language switching is working. * Fixed a bug on dashboard with languagePath * Converted user-scrobble-history.component.html * Converted spoiler.component.html * Converted review-series-modal.component.html * Converted review-card-modal.component.html * Updated the readme * Translated using Weblate (English) Currently translated at 100.0% (54 of 54 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/en/ * Converted review-card.component.html * Deleted dead component * Converted want-to-read.component.html * Added translation using Weblate (Korean) * Translated using Weblate (Spanish) Currently translated at 40.7% (22 of 54 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/es/ * Translated using Weblate (Korean) Currently translated at 62.9% (34 of 54 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/ko/ * Converted user-preferences.component.html * Translated using Weblate (Korean) Currently translated at 92.5% (50 of 54 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/ko/ * Converted user-holds.component.html * Converted theme-manager.component.html * Converted restriction-selector.component.html * Converted manage-devices.component.html * Converted edit-device.component.html * Converted change-password.component.html * Converted change-email.component.html * Converted change-age-restriction.component.html * Converted api-key.component.html * Converted anilist-key.component.html * Converted typeahead.component.html * Converted user-stats-info-cards.component.html * Converted user-stats.component.html * Converted top-readers.component.html * Converted some pipes and ensure translation is loaded before the app. * Finished all but one pipe for localization * Converted directory-picker.component.html * Converted library-access-modal.component.html * Converted a few components * Converted a few components * Converted a few components * Converted a few components * Converted a few components * Merged weblate in * ... -> … update * Updated the readme * Updateded all fonts to be woff2 * Cleaned up some strings to increase re-use * Removed an old flow (that doesn't exist in backend any longer) from when we introduced emails on Kavita. * Converted Series detail * Lots more converted * Lots more converted & hooked up the ability to flatten during prod build the language files. * Lots more converted * Lots more converted & fixed a bunch of broken pipes due to inject() * Lots more converted * Lots more converted * Lots more converted & fixed some bad keys * Lots more converted * Fixed some bugs with admin dasbhoard nested tabs not rendering on first load due to not using onpush change detection * Fixed up some localization errors and fixed forgot password error when the user doesn't have change password permission * Fixed a stupid build issue again * Started adding errors for interceptor and backend. * Finished off manga-reader * More translations * Few fixes * Fixed a bug where character tag badges weren't showing the name on chapter info * All components are translated * All toasts are translated * All confirm/alerts are translated * Trying something new for the backend * Migrated the localization strings for the backend into a new file. * Updated the localization service to be able to do backend localization with fallback to english. * Cleaned up some external reviews code to reduce looping * Localized AccountController.cs * 60% done with controllers * All controllers are done * All KavitaExceptions are covered * Some shakeout fixes * Prep for initial merge * Everything is done except options and basic shakeout proves response times are good. Unit tests are broken. * Fixed up the unit tests * All unit tests are now working * Removed some quantifier * I'm not sure I can support localization for some Volume/Chapter/Book strings within the codebase. --------- Co-authored-by: Robbie Davis <robbie@therobbiedavis.com> Co-authored-by: majora2007 <kavitareader@gmail.com> Co-authored-by: expertjun <jtrobin@naver.com> Co-authored-by: ThePromidius <thepromidiusyt@gmail.com>
		
			
				
	
	
		
			395 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			395 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Threading.Tasks;
 | 
						|
using API.Constants;
 | 
						|
using API.Data;
 | 
						|
using API.DTOs.Uploads;
 | 
						|
using API.Extensions;
 | 
						|
using API.Services;
 | 
						|
using API.SignalR;
 | 
						|
using Flurl.Http;
 | 
						|
using Microsoft.AspNetCore.Authorization;
 | 
						|
using Microsoft.AspNetCore.Mvc;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
 | 
						|
namespace API.Controllers;
 | 
						|
 | 
						|
/// <summary>
 | 
						|
///
 | 
						|
/// </summary>
 | 
						|
public class UploadController : BaseApiController
 | 
						|
{
 | 
						|
    private readonly IUnitOfWork _unitOfWork;
 | 
						|
    private readonly IImageService _imageService;
 | 
						|
    private readonly ILogger<UploadController> _logger;
 | 
						|
    private readonly ITaskScheduler _taskScheduler;
 | 
						|
    private readonly IDirectoryService _directoryService;
 | 
						|
    private readonly IEventHub _eventHub;
 | 
						|
    private readonly IReadingListService _readingListService;
 | 
						|
    private readonly ILocalizationService _localizationService;
 | 
						|
 | 
						|
    /// <inheritdoc />
 | 
						|
    public UploadController(IUnitOfWork unitOfWork, IImageService imageService, ILogger<UploadController> logger,
 | 
						|
        ITaskScheduler taskScheduler, IDirectoryService directoryService, IEventHub eventHub, IReadingListService readingListService,
 | 
						|
        ILocalizationService localizationService)
 | 
						|
    {
 | 
						|
        _unitOfWork = unitOfWork;
 | 
						|
        _imageService = imageService;
 | 
						|
        _logger = logger;
 | 
						|
        _taskScheduler = taskScheduler;
 | 
						|
        _directoryService = directoryService;
 | 
						|
        _eventHub = eventHub;
 | 
						|
        _readingListService = readingListService;
 | 
						|
        _localizationService = localizationService;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// This stores a file (image) in temp directory for use in a cover image replacement flow.
 | 
						|
    /// This is automatically cleaned up.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="dto">Escaped url to download from</param>
 | 
						|
    /// <returns>filename</returns>
 | 
						|
    [Authorize(Policy = "RequireAdminRole")]
 | 
						|
    [HttpPost("upload-by-url")]
 | 
						|
    public async Task<ActionResult<string>> GetImageFromFile(UploadUrlDto dto)
 | 
						|
    {
 | 
						|
        var dateString = $"{DateTime.UtcNow.ToShortDateString()}_{DateTime.UtcNow.ToLongTimeString()}".Replace('/', '_').Replace(':', '_');
 | 
						|
        var format = _directoryService.FileSystem.Path.GetExtension(dto.Url.Split('?')[0]).Replace(".", string.Empty);
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var path = await dto.Url
 | 
						|
                .DownloadFileAsync(_directoryService.TempDirectory, $"coverupload_{dateString}.{format}");
 | 
						|
 | 
						|
            if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path))
 | 
						|
                return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-not-valid"));
 | 
						|
 | 
						|
            if (!await _imageService.IsImage(path)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-not-valid"));
 | 
						|
 | 
						|
            return $"coverupload_{dateString}.{format}";
 | 
						|
        }
 | 
						|
        catch (FlurlHttpException ex)
 | 
						|
        {
 | 
						|
            // Unauthorized
 | 
						|
            if (ex.StatusCode == 401)
 | 
						|
                return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-not-valid"));
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-not-valid"));
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Replaces series cover image and locks it with a base64 encoded image
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="uploadFileDto"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [Authorize(Policy = "RequireAdminRole")]
 | 
						|
    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]
 | 
						|
    [HttpPost("series")]
 | 
						|
    public async Task<ActionResult> UploadSeriesCoverImageFromUrl(UploadFileDto uploadFileDto)
 | 
						|
    {
 | 
						|
        // Check if Url is non empty, request the image and place in temp, then ask image service to handle it.
 | 
						|
        // See if we can do this all in memory without touching underlying system
 | 
						|
        if (string.IsNullOrEmpty(uploadFileDto.Url))
 | 
						|
        {
 | 
						|
            return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-required"));
 | 
						|
        }
 | 
						|
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(uploadFileDto.Id);
 | 
						|
            if (series == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "series-doesnt-exist"));
 | 
						|
            var filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetSeriesFormat(uploadFileDto.Id)}");
 | 
						|
 | 
						|
            if (!string.IsNullOrEmpty(filePath))
 | 
						|
            {
 | 
						|
                series.CoverImage = filePath;
 | 
						|
                series.CoverImageLocked = true;
 | 
						|
                _unitOfWork.SeriesRepository.Update(series);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(series.Id, MessageFactoryEntityTypes.Series), false);
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                return Ok();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        catch (Exception e)
 | 
						|
        {
 | 
						|
            _logger.LogError(e, "There was an issue uploading cover image for Series {Id}", uploadFileDto.Id);
 | 
						|
            await _unitOfWork.RollbackAsync();
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-cover-series-save"));
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Replaces collection tag cover image and locks it with a base64 encoded image
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="uploadFileDto"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [Authorize(Policy = "RequireAdminRole")]
 | 
						|
    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]
 | 
						|
    [HttpPost("collection")]
 | 
						|
    public async Task<ActionResult> UploadCollectionCoverImageFromUrl(UploadFileDto uploadFileDto)
 | 
						|
    {
 | 
						|
        // Check if Url is non empty, request the image and place in temp, then ask image service to handle it.
 | 
						|
        // See if we can do this all in memory without touching underlying system
 | 
						|
        if (string.IsNullOrEmpty(uploadFileDto.Url))
 | 
						|
        {
 | 
						|
            return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-required"));
 | 
						|
        }
 | 
						|
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(uploadFileDto.Id);
 | 
						|
            if (tag == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "collection-doesnt-exist"));
 | 
						|
            var filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetCollectionTagFormat(uploadFileDto.Id)}");
 | 
						|
 | 
						|
            if (!string.IsNullOrEmpty(filePath))
 | 
						|
            {
 | 
						|
                tag.CoverImage = filePath;
 | 
						|
                tag.CoverImageLocked = true;
 | 
						|
                _unitOfWork.CollectionTagRepository.Update(tag);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(tag.Id, MessageFactoryEntityTypes.CollectionTag), false);
 | 
						|
                return Ok();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        catch (Exception e)
 | 
						|
        {
 | 
						|
            _logger.LogError(e, "There was an issue uploading cover image for Collection Tag {Id}", uploadFileDto.Id);
 | 
						|
            await _unitOfWork.RollbackAsync();
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-cover-collection-save"));
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Replaces reading list cover image and locks it with a base64 encoded image
 | 
						|
    /// </summary>
 | 
						|
    /// <remarks>This is the only API that can be called by non-admins, but the authenticated user must have a readinglist permission</remarks>
 | 
						|
    /// <param name="uploadFileDto"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]
 | 
						|
    [HttpPost("reading-list")]
 | 
						|
    public async Task<ActionResult> UploadReadingListCoverImageFromUrl(UploadFileDto uploadFileDto)
 | 
						|
    {
 | 
						|
        // Check if Url is non empty, request the image and place in temp, then ask image service to handle it.
 | 
						|
        // See if we can do this all in memory without touching underlying system
 | 
						|
        if (string.IsNullOrEmpty(uploadFileDto.Url))
 | 
						|
        {
 | 
						|
            return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-required"));
 | 
						|
        }
 | 
						|
 | 
						|
        if (_readingListService.UserHasReadingListAccess(uploadFileDto.Id, User.GetUsername()) == null)
 | 
						|
            return Unauthorized(await _localizationService.Translate(User.GetUserId(), "access-denied"));
 | 
						|
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var readingList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(uploadFileDto.Id);
 | 
						|
            if (readingList == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "reading-list-doesnt-exist"));
 | 
						|
            var filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetReadingListFormat(uploadFileDto.Id)}");
 | 
						|
 | 
						|
            if (!string.IsNullOrEmpty(filePath))
 | 
						|
            {
 | 
						|
                readingList.CoverImage = filePath;
 | 
						|
                readingList.CoverImageLocked = true;
 | 
						|
                _unitOfWork.ReadingListRepository.Update(readingList);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(readingList.Id, MessageFactoryEntityTypes.ReadingList), false);
 | 
						|
                return Ok();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        catch (Exception e)
 | 
						|
        {
 | 
						|
            _logger.LogError(e, "There was an issue uploading cover image for Reading List {Id}", uploadFileDto.Id);
 | 
						|
            await _unitOfWork.RollbackAsync();
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-cover-reading-list-save"));
 | 
						|
    }
 | 
						|
 | 
						|
    private async Task<string> CreateThumbnail(UploadFileDto uploadFileDto, string filename, int thumbnailSize = 0)
 | 
						|
    {
 | 
						|
        var encodeFormat = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EncodeMediaAs;
 | 
						|
        if (thumbnailSize > 0)
 | 
						|
        {
 | 
						|
            return _imageService.CreateThumbnailFromBase64(uploadFileDto.Url,
 | 
						|
                filename, encodeFormat, thumbnailSize);
 | 
						|
        }
 | 
						|
 | 
						|
        return _imageService.CreateThumbnailFromBase64(uploadFileDto.Url,
 | 
						|
            filename, encodeFormat);
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Replaces chapter cover image and locks it with a base64 encoded image. This will update the parent volume's cover image.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="uploadFileDto"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [Authorize(Policy = "RequireAdminRole")]
 | 
						|
    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]
 | 
						|
    [HttpPost("chapter")]
 | 
						|
    public async Task<ActionResult> UploadChapterCoverImageFromUrl(UploadFileDto uploadFileDto)
 | 
						|
    {
 | 
						|
        // Check if Url is non empty, request the image and place in temp, then ask image service to handle it.
 | 
						|
        // See if we can do this all in memory without touching underlying system
 | 
						|
        if (string.IsNullOrEmpty(uploadFileDto.Url))
 | 
						|
        {
 | 
						|
            return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-required"));
 | 
						|
        }
 | 
						|
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(uploadFileDto.Id);
 | 
						|
            if (chapter == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "chapter-doesnt-exist"));
 | 
						|
            var filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetChapterFormat(uploadFileDto.Id, chapter.VolumeId)}");
 | 
						|
 | 
						|
            if (!string.IsNullOrEmpty(filePath))
 | 
						|
            {
 | 
						|
                chapter.CoverImage = filePath;
 | 
						|
                chapter.CoverImageLocked = true;
 | 
						|
                _unitOfWork.ChapterRepository.Update(chapter);
 | 
						|
                var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(chapter.VolumeId);
 | 
						|
                if (volume != null)
 | 
						|
                {
 | 
						|
                    volume.CoverImage = chapter.CoverImage;
 | 
						|
                    _unitOfWork.VolumeRepository.Update(volume);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(chapter.VolumeId, MessageFactoryEntityTypes.Volume), false);
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(chapter.Id, MessageFactoryEntityTypes.Chapter), false);
 | 
						|
                return Ok();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        catch (Exception e)
 | 
						|
        {
 | 
						|
            _logger.LogError(e, "There was an issue uploading cover image for Chapter {Id}", uploadFileDto.Id);
 | 
						|
            await _unitOfWork.RollbackAsync();
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-cover-chapter-save"));
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Replaces library cover image with a base64 encoded image. If empty string passed, will reset to null.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="uploadFileDto"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [Authorize(Policy = "RequireAdminRole")]
 | 
						|
    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]
 | 
						|
    [HttpPost("library")]
 | 
						|
    public async Task<ActionResult> UploadLibraryCoverImageFromUrl(UploadFileDto uploadFileDto)
 | 
						|
    {
 | 
						|
        var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(uploadFileDto.Id);
 | 
						|
        if (library == null) return BadRequest("This library does not exist");
 | 
						|
 | 
						|
        // Check if Url is non empty, request the image and place in temp, then ask image service to handle it.
 | 
						|
        // See if we can do this all in memory without touching underlying system
 | 
						|
        if (string.IsNullOrEmpty(uploadFileDto.Url))
 | 
						|
        {
 | 
						|
            library.CoverImage = null;
 | 
						|
            _unitOfWork.LibraryRepository.Update(library);
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(library.Id, MessageFactoryEntityTypes.Library), false);
 | 
						|
            }
 | 
						|
 | 
						|
            return Ok();
 | 
						|
        }
 | 
						|
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var filePath = await CreateThumbnail(uploadFileDto,
 | 
						|
                $"{ImageService.GetLibraryFormat(uploadFileDto.Id)}",
 | 
						|
                ImageService.LibraryThumbnailWidth);
 | 
						|
 | 
						|
            if (!string.IsNullOrEmpty(filePath))
 | 
						|
            {
 | 
						|
                library.CoverImage = filePath;
 | 
						|
                _unitOfWork.LibraryRepository.Update(library);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
 | 
						|
                    MessageFactory.CoverUpdateEvent(library.Id, MessageFactoryEntityTypes.Library), false);
 | 
						|
                return Ok();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        catch (Exception e)
 | 
						|
        {
 | 
						|
            _logger.LogError(e, "There was an issue uploading cover image for Library {Id}", uploadFileDto.Id);
 | 
						|
            await _unitOfWork.RollbackAsync();
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-cover-library-save"));
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Replaces chapter cover image and locks it with a base64 encoded image. This will update the parent volume's cover image.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="uploadFileDto">Does not use Url property</param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [Authorize(Policy = "RequireAdminRole")]
 | 
						|
    [HttpPost("reset-chapter-lock")]
 | 
						|
    public async Task<ActionResult> ResetChapterLock(UploadFileDto uploadFileDto)
 | 
						|
    {
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(uploadFileDto.Id);
 | 
						|
            if (chapter == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "chapter-doesnt-exist"));
 | 
						|
            var originalFile = chapter.CoverImage;
 | 
						|
            chapter.CoverImage = string.Empty;
 | 
						|
            chapter.CoverImageLocked = false;
 | 
						|
            _unitOfWork.ChapterRepository.Update(chapter);
 | 
						|
            var volume = (await _unitOfWork.VolumeRepository.GetVolumeAsync(chapter.VolumeId))!;
 | 
						|
            volume.CoverImage = chapter.CoverImage;
 | 
						|
            _unitOfWork.VolumeRepository.Update(volume);
 | 
						|
            var series = (await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId))!;
 | 
						|
 | 
						|
            if (_unitOfWork.HasChanges())
 | 
						|
            {
 | 
						|
                await _unitOfWork.CommitAsync();
 | 
						|
                if (originalFile != null) System.IO.File.Delete(originalFile);
 | 
						|
                _taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id, true);
 | 
						|
                return Ok();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        catch (Exception e)
 | 
						|
        {
 | 
						|
            _logger.LogError(e, "There was an issue resetting cover lock for Chapter {Id}", uploadFileDto.Id);
 | 
						|
            await _unitOfWork.RollbackAsync();
 | 
						|
        }
 | 
						|
 | 
						|
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "reset-chapter-lock"));
 | 
						|
    }
 | 
						|
 | 
						|
}
 |