2024-12-14 21:53:29 +01:00
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
|
using System.IO.Compression;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using API.MangaDownloadClients;
|
|
|
|
|
using API.Schema.MangaConnectors;
|
|
|
|
|
using SixLabors.ImageSharp;
|
|
|
|
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
|
|
|
|
using SixLabors.ImageSharp.Processing;
|
|
|
|
|
using SixLabors.ImageSharp.Processing.Processors.Binarization;
|
|
|
|
|
using static System.IO.UnixFileMode;
|
|
|
|
|
|
|
|
|
|
namespace API.Schema.Jobs;
|
|
|
|
|
|
2025-01-09 01:34:03 +01:00
|
|
|
|
public class DownloadSingleChapterJob(string chapterId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
2025-01-31 21:24:35 +01:00
|
|
|
|
: Job(TokenGen.CreateToken(typeof(DownloadSingleChapterJob)), JobType.DownloadSingleChapterJob, 0, parentJobId, dependsOnJobsIds)
|
2024-12-14 21:53:29 +01:00
|
|
|
|
{
|
|
|
|
|
[MaxLength(64)]
|
|
|
|
|
public string ChapterId { get; init; } = chapterId;
|
2025-01-09 01:34:03 +01:00
|
|
|
|
public Chapter? Chapter { get; init; }
|
2024-12-14 21:53:29 +01:00
|
|
|
|
|
2024-12-16 23:29:57 +01:00
|
|
|
|
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
2024-12-14 21:53:29 +01:00
|
|
|
|
{
|
2025-01-09 01:34:03 +01:00
|
|
|
|
Chapter c = Chapter ?? context.Chapters.Find(ChapterId)!;
|
|
|
|
|
Manga m = c.ParentManga ?? context.Manga.Find(c.ParentMangaId)!;
|
|
|
|
|
MangaConnector connector = m.MangaConnector ?? context.MangaConnectors.Find(m.MangaConnectorId)!;
|
|
|
|
|
DownloadChapterImages(c, connector, m);
|
2024-12-14 21:53:29 +01:00
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 01:34:03 +01:00
|
|
|
|
private bool DownloadChapterImages(Chapter chapter, MangaConnector connector, Manga manga)
|
2024-12-14 21:53:29 +01:00
|
|
|
|
{
|
2025-01-09 01:34:03 +01:00
|
|
|
|
string[] imageUrls = connector.GetChapterImageUrls(chapter);
|
2024-12-14 21:53:29 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 01:34:03 +01:00
|
|
|
|
CopyCoverFromCacheToDownloadLocation(manga);
|
2024-12-14 21:53:29 +01:00
|
|
|
|
|
|
|
|
|
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;
|
2025-02-01 21:58:50 +01:00
|
|
|
|
DateTime start = DateTime.UtcNow;
|
2024-12-14 21:53:29 +01:00
|
|
|
|
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
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 01:34:03 +01:00
|
|
|
|
private void CopyCoverFromCacheToDownloadLocation(Manga manga, int? retries = 1)
|
2024-12-14 21:53:29 +01:00
|
|
|
|
{
|
|
|
|
|
//Check if Publication already has a Folder and cover
|
2025-01-09 01:34:03 +01:00
|
|
|
|
string publicationFolder = manga.CreatePublicationFolder();
|
2024-12-14 21:53:29 +01:00
|
|
|
|
DirectoryInfo dirInfo = new (publicationFolder);
|
|
|
|
|
if (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover", StringComparison.InvariantCultureIgnoreCase)))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 01:34:03 +01:00
|
|
|
|
string? fileInCache = manga.CoverFileNameInCache;
|
2024-12-14 21:53:29 +01:00
|
|
|
|
if (fileInCache is null || !File.Exists(fileInCache))
|
|
|
|
|
{
|
2025-01-09 01:34:03 +01:00
|
|
|
|
if (retries > 0)
|
2024-12-14 21:53:29 +01:00
|
|
|
|
{
|
2025-01-09 01:34:03 +01:00
|
|
|
|
manga.SaveCoverImageToCache();
|
|
|
|
|
CopyCoverFromCacheToDownloadLocation(manga, --retries);
|
2024-12-14 21:53:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|