mirror of
https://github.com/C9Glax/tranga.git
synced 2025-04-17 13:53:18 +02:00
Fix DownloadMangaCover, Implement Manga/Cover endpoint
This commit is contained in:
parent
4075adfe6b
commit
c679d7c677
API
@ -106,7 +106,7 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
Image image = Image.Load(m.CoverFileNameInCache);
|
Image image = Image.Load(m.CoverFileNameInCache);
|
||||||
using MemoryStream ms = new();
|
using MemoryStream ms = new();
|
||||||
image.Save(ms, new JpegEncoder(){Quality = 100});
|
image.Save(ms, new JpegEncoder(){Quality = 100});
|
||||||
return File(ms, "image/jpeg");
|
return File(ms.GetBuffer(), "image/jpeg");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -11,125 +11,21 @@ using static System.IO.UnixFileMode;
|
|||||||
|
|
||||||
namespace API.Schema.Jobs;
|
namespace API.Schema.Jobs;
|
||||||
|
|
||||||
public class DownloadMangaCoverJob(string chapterId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
public class DownloadMangaCoverJob(string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||||
: Job(TokenGen.CreateToken(typeof(DownloadMangaCoverJob)), JobType.DownloadMangaCoverJob, 0, parentJobId, dependsOnJobsIds)
|
: Job(TokenGen.CreateToken(typeof(DownloadMangaCoverJob)), JobType.DownloadMangaCoverJob, 0, parentJobId, dependsOnJobsIds)
|
||||||
{
|
{
|
||||||
[MaxLength(64)]
|
[MaxLength(64)]
|
||||||
public string ChapterId { get; init; } = chapterId;
|
public string MangaId { get; init; } = mangaId;
|
||||||
public Chapter? Chapter { get; init; }
|
public Manga? Manga { get; init; }
|
||||||
|
|
||||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||||
{
|
{
|
||||||
MangaConnector connector = Chapter.ParentManga?.MangaConnector ?? context.MangaConnectors.Find(context.Manga.Find(Chapter.ParentMangaId)?.MangaId)!;
|
Manga? manga = Manga ?? context.Manga.Find(this.MangaId);
|
||||||
DownloadChapterImages(Chapter, connector);
|
if (manga is null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
manga.CoverFileNameInCache = manga.SaveCoverImageToCache();
|
||||||
|
context.SaveChanges();
|
||||||
return [];
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -58,7 +58,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyCoverFromCacheToDownloadLocation(context, manga);
|
CopyCoverFromCacheToDownloadLocation(manga);
|
||||||
|
|
||||||
File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString());
|
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
|
//Check if Publication already has a Folder and cover
|
||||||
string publicationFolder = manga.CreatePublicationFolder();
|
string publicationFolder = manga.CreatePublicationFolder();
|
||||||
@ -98,18 +98,10 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string? fileInCache = manga.CoverFileNameInCache;
|
string? fileInCache = manga.CoverFileNameInCache ?? manga.SaveCoverImageToCache();
|
||||||
if (fileInCache is null || !File.Exists(fileInCache))
|
if (fileInCache is null)
|
||||||
{
|
|
||||||
if (retries > 0)
|
|
||||||
{
|
|
||||||
manga.CoverFileNameInCache = manga.SaveCoverImageToCache();
|
|
||||||
context.SaveChanges();
|
|
||||||
CopyCoverFromCacheToDownloadLocation(context, manga, --retries);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" );
|
string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" );
|
||||||
File.Copy(fileInCache, newFilePath, true);
|
File.Copy(fileInCache, newFilePath, true);
|
||||||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.Net;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using API.MangaDownloadClients;
|
using API.MangaDownloadClients;
|
||||||
@ -99,8 +99,11 @@ public class Manga
|
|||||||
return mangaName;
|
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]+))");
|
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
|
//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);
|
Match match = urlRex.Match(CoverUrl);
|
||||||
@ -111,10 +114,14 @@ public class Manga
|
|||||||
return saveImagePath;
|
return saveImagePath;
|
||||||
|
|
||||||
RequestResult coverResult = new HttpDownloadClient().MakeRequest(CoverUrl, RequestType.MangaCover);
|
RequestResult coverResult = new HttpDownloadClient().MakeRequest(CoverUrl, RequestType.MangaCover);
|
||||||
|
if (coverResult.statusCode is < HttpStatusCode.Accepted or >= HttpStatusCode.Ambiguous)
|
||||||
|
return SaveCoverImageToCache(--retries);
|
||||||
|
|
||||||
using MemoryStream ms = new();
|
using MemoryStream ms = new();
|
||||||
coverResult.result.CopyTo(ms);
|
coverResult.result.CopyTo(ms);
|
||||||
Directory.CreateDirectory(TrangaSettings.coverImageCache);
|
Directory.CreateDirectory(TrangaSettings.coverImageCache);
|
||||||
File.WriteAllBytes(saveImagePath, ms.ToArray());
|
File.WriteAllBytes(saveImagePath, ms.ToArray());
|
||||||
|
|
||||||
return saveImagePath;
|
return saveImagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user