From c679d7c677d6ac130a0e7471ed676441f755c3d7 Mon Sep 17 00:00:00 2001 From: Glax Date: Fri, 7 Mar 2025 14:07:59 +0100 Subject: [PATCH] Fix DownloadMangaCover, Implement Manga/Cover endpoint --- API/Controllers/MangaController.cs | 2 +- API/Schema/Jobs/DownloadMangaCoverJob.cs | 122 ++------------------ API/Schema/Jobs/DownloadSingleChapterJob.cs | 18 +-- API/Schema/Manga.cs | 11 +- 4 files changed, 24 insertions(+), 129 deletions(-) diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs index ec99162..e05d155 100644 --- a/API/Controllers/MangaController.cs +++ b/API/Controllers/MangaController.cs @@ -106,7 +106,7 @@ public class MangaController(PgsqlContext context) : Controller Image image = Image.Load(m.CoverFileNameInCache); using MemoryStream ms = new(); image.Save(ms, new JpegEncoder(){Quality = 100}); - return File(ms, "image/jpeg"); + return File(ms.GetBuffer(), "image/jpeg"); } /// diff --git a/API/Schema/Jobs/DownloadMangaCoverJob.cs b/API/Schema/Jobs/DownloadMangaCoverJob.cs index 4c6403a..1d75886 100644 --- a/API/Schema/Jobs/DownloadMangaCoverJob.cs +++ b/API/Schema/Jobs/DownloadMangaCoverJob.cs @@ -11,125 +11,21 @@ using static System.IO.UnixFileMode; namespace API.Schema.Jobs; -public class DownloadMangaCoverJob(string chapterId, string? parentJobId = null, ICollection? dependsOnJobsIds = null) +public class DownloadMangaCoverJob(string mangaId, string? parentJobId = null, ICollection? dependsOnJobsIds = null) : Job(TokenGen.CreateToken(typeof(DownloadMangaCoverJob)), JobType.DownloadMangaCoverJob, 0, parentJobId, dependsOnJobsIds) { [MaxLength(64)] - public string ChapterId { get; init; } = chapterId; - public Chapter? Chapter { get; init; } + public string MangaId { get; init; } = mangaId; + public Manga? Manga { get; init; } protected override IEnumerable RunInternal(PgsqlContext context) { - MangaConnector connector = Chapter.ParentManga?.MangaConnector ?? context.MangaConnectors.Find(context.Manga.Find(Chapter.ParentMangaId)?.MangaId)!; - DownloadChapterImages(Chapter, connector); + Manga? manga = Manga ?? context.Manga.Find(this.MangaId); + if (manga is null) + return []; + + manga.CoverFileNameInCache = manga.SaveCoverImageToCache(); + context.SaveChanges(); return []; } - - private bool DownloadChapterImages(Chapter chapter, MangaConnector connector) - { - string[] imageUrls = connector.GetChapterImageUrls(Chapter); - string saveArchiveFilePath = chapter.GetArchiveFilePath(); - - //Check if Publication Directory already exists - string directoryPath = Path.GetDirectoryName(saveArchiveFilePath)!; - if (!Directory.Exists(directoryPath)) - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - Directory.CreateDirectory(directoryPath, - UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute ); - else - Directory.CreateDirectory(directoryPath); - - if (File.Exists(saveArchiveFilePath)) //Don't download twice. Redownload - File.Delete(saveArchiveFilePath); - - //Create a temporary folder to store images - string tempFolder = Directory.CreateTempSubdirectory("trangatemp").FullName; - - int chapterNum = 0; - //Download all Images to temporary Folder - if (imageUrls.Length == 0) - { - Directory.Delete(tempFolder, true); - return false; - } - - foreach (string imageUrl in imageUrls) - { - string extension = imageUrl.Split('.')[^1].Split('?')[0]; - string imagePath = Path.Join(tempFolder, $"{chapterNum++}.{extension}"); - bool status = DownloadImage(imageUrl, imagePath); - if (status is false) - return false; - } - - CopyCoverFromCacheToDownloadLocation(); - - File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString()); - - //ZIP-it and ship-it - ZipFile.CreateFromDirectory(tempFolder, saveArchiveFilePath); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - File.SetUnixFileMode(saveArchiveFilePath, UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute | OtherRead | OtherExecute); - Directory.Delete(tempFolder, true); //Cleanup - - return true; - } - - private void ProcessImage(string imagePath) - { - if (!TrangaSettings.bwImages && TrangaSettings.compression == 100) - return; - using Image image = Image.Load(imagePath); - File.Delete(imagePath); - if(TrangaSettings.bwImages) - image.Mutate(i => i.ApplyProcessor(new AdaptiveThresholdProcessor())); - image.SaveAsJpeg(imagePath, new JpegEncoder() - { - Quality = TrangaSettings.compression - }); - } - - private void CopyCoverFromCacheToDownloadLocation(int? retries = 1) - { - //Check if Publication already has a Folder and cover - string publicationFolder = Chapter.ParentManga.CreatePublicationFolder(); - DirectoryInfo dirInfo = new (publicationFolder); - if (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover", StringComparison.InvariantCultureIgnoreCase))) - { - return; - } - - string? fileInCache = Chapter.ParentManga.CoverFileNameInCache; - if (fileInCache is null || !File.Exists(fileInCache)) - { - if (retries > 0 && Chapter.ParentManga.CoverUrl is not null) - { - Chapter.ParentManga.SaveCoverImageToCache(); - CopyCoverFromCacheToDownloadLocation(--retries); - } - - return; - } - string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" ); - File.Copy(fileInCache, newFilePath, true); - if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - File.SetUnixFileMode(newFilePath, GroupRead | GroupWrite | UserRead | UserWrite); - } - - private bool DownloadImage(string imageUrl, string savePath) - { - HttpDownloadClient downloadClient = new(); - RequestResult requestResult = downloadClient.MakeRequest(imageUrl, RequestType.MangaImage); - - if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) - return false; - if (requestResult.result == Stream.Null) - return false; - - FileStream fs = new (savePath, FileMode.Create); - requestResult.result.CopyTo(fs); - fs.Close(); - ProcessImage(savePath); - return true; - } } \ No newline at end of file diff --git a/API/Schema/Jobs/DownloadSingleChapterJob.cs b/API/Schema/Jobs/DownloadSingleChapterJob.cs index ed4d21b..0b79889 100644 --- a/API/Schema/Jobs/DownloadSingleChapterJob.cs +++ b/API/Schema/Jobs/DownloadSingleChapterJob.cs @@ -58,7 +58,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu return []; } - CopyCoverFromCacheToDownloadLocation(context, manga); + CopyCoverFromCacheToDownloadLocation(manga); File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString()); @@ -88,7 +88,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu }); } - private void CopyCoverFromCacheToDownloadLocation(PgsqlContext context, Manga manga, int? retries = 1) + private void CopyCoverFromCacheToDownloadLocation(Manga manga) { //Check if Publication already has a Folder and cover string publicationFolder = manga.CreatePublicationFolder(); @@ -98,18 +98,10 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu return; } - string? fileInCache = manga.CoverFileNameInCache; - if (fileInCache is null || !File.Exists(fileInCache)) - { - if (retries > 0) - { - manga.CoverFileNameInCache = manga.SaveCoverImageToCache(); - context.SaveChanges(); - CopyCoverFromCacheToDownloadLocation(context, manga, --retries); - } - + string? fileInCache = manga.CoverFileNameInCache ?? manga.SaveCoverImageToCache(); + if (fileInCache is null) return; - } + string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" ); File.Copy(fileInCache, newFilePath, true); if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) diff --git a/API/Schema/Manga.cs b/API/Schema/Manga.cs index 0296bbd..f87ec30 100644 --- a/API/Schema/Manga.cs +++ b/API/Schema/Manga.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; +using System.Net; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using API.MangaDownloadClients; @@ -99,8 +99,11 @@ public class Manga return mangaName; } - internal string SaveCoverImageToCache() + internal string? SaveCoverImageToCache(int retries = 3) { + if(retries < 0) + return null; + Regex urlRex = new (@"https?:\/\/((?:[a-zA-Z0-9-]+\.)+[a-zA-Z0-9]+)\/(?:.+\/)*(.+\.([a-zA-Z]+))"); //https?:\/\/[a-zA-Z0-9-]+\.([a-zA-Z0-9-]+\.[a-zA-Z0-9]+)\/(?:.+\/)*(.+\.([a-zA-Z]+)) for only second level domains Match match = urlRex.Match(CoverUrl); @@ -111,10 +114,14 @@ public class Manga return saveImagePath; RequestResult coverResult = new HttpDownloadClient().MakeRequest(CoverUrl, RequestType.MangaCover); + if (coverResult.statusCode is < HttpStatusCode.Accepted or >= HttpStatusCode.Ambiguous) + return SaveCoverImageToCache(--retries); + using MemoryStream ms = new(); coverResult.result.CopyTo(ms); Directory.CreateDirectory(TrangaSettings.coverImageCache); File.WriteAllBytes(saveImagePath, ms.ToArray()); + return saveImagePath; }