mirror of
https://github.com/C9Glax/tranga.git
synced 2025-06-12 06:27:54 +02:00
Fix some Jobs
This commit is contained in:
136
API/Schema/Jobs/DownloadMangaCoverJob.cs
Normal file
136
API/Schema/Jobs/DownloadMangaCoverJob.cs
Normal file
@ -0,0 +1,136 @@
|
||||
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;
|
||||
|
||||
public class DownloadMangaCoverJob(string chapterId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadMangaCoverJob), 64), JobType.DownloadMangaCoverJob, 0, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string ChapterId { get; init; } = chapterId;
|
||||
public Chapter? Chapter { get; init; }
|
||||
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
MangaConnector connector = Chapter.ParentManga?.MangaConnector ?? context.MangaConnectors.Find(context.Manga.Find(Chapter.ParentMangaId)?.MangaId)!;
|
||||
DownloadChapterImages(Chapter, connector);
|
||||
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;
|
||||
DateTime start = DateTime.Now;
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,22 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using API.Schema.MangaConnectors;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class DownloadNewChaptersJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, string[]? dependsOnJobIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadNewChaptersJob), 64), JobType.DownloadNewChaptersJob, recurrenceMs, parentJobId, dependsOnJobIds)
|
||||
public class DownloadNewChaptersJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadNewChaptersJob), 64), JobType.DownloadNewChaptersJob, recurrenceMs, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string MangaId { get; init; } = mangaId;
|
||||
public virtual Manga Manga { get; init; }
|
||||
public Manga? Manga { get; init; }
|
||||
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
MangaConnector connector = Manga.MangaConnector;
|
||||
Chapter[] newChapters = connector.GetNewChapters(Manga);
|
||||
Manga m = Manga ?? context.Manga.Find(MangaId)!;
|
||||
MangaConnector connector = m.MangaConnector ?? context.MangaConnectors.Find(m.MangaConnectorId)!;
|
||||
Chapter[] newChapters = connector.GetNewChapters(m);
|
||||
context.Chapters.AddRangeAsync(newChapters).Wait();
|
||||
context.SaveChangesAsync().Wait();
|
||||
return newChapters.Select(chapter => new DownloadSingleChapterJob(chapter.ChapterId, this.JobId));
|
||||
}
|
||||
}
|
@ -11,24 +11,25 @@ using static System.IO.UnixFileMode;
|
||||
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class DownloadSingleChapterJob(string chapterId, string? parentJobId = null, string[]? dependsOnJobIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadSingleChapterJob), 64), JobType.DownloadSingleChapterJob, 0, parentJobId, dependsOnJobIds)
|
||||
public class DownloadSingleChapterJob(string chapterId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadSingleChapterJob), 64), JobType.DownloadSingleChapterJob, 0, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string ChapterId { get; init; } = chapterId;
|
||||
public virtual Chapter Chapter { get; init; }
|
||||
public Chapter? Chapter { get; init; }
|
||||
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
MangaConnector connector = Chapter.ParentManga.MangaConnector;
|
||||
DownloadChapterImages(Chapter);
|
||||
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);
|
||||
return [];
|
||||
}
|
||||
|
||||
private bool DownloadChapterImages(Chapter chapter)
|
||||
private bool DownloadChapterImages(Chapter chapter, MangaConnector connector, Manga manga)
|
||||
{
|
||||
MangaConnector connector = Chapter.ParentManga.MangaConnector;
|
||||
string[] imageUrls = connector.GetChapterImageUrls(Chapter);
|
||||
string[] imageUrls = connector.GetChapterImageUrls(chapter);
|
||||
string saveArchiveFilePath = chapter.GetArchiveFilePath();
|
||||
|
||||
//Check if Publication Directory already exists
|
||||
@ -63,7 +64,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
return false;
|
||||
}
|
||||
|
||||
CopyCoverFromCacheToDownloadLocation();
|
||||
CopyCoverFromCacheToDownloadLocation(manga);
|
||||
|
||||
File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString());
|
||||
|
||||
@ -91,23 +92,23 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
});
|
||||
}
|
||||
|
||||
private void CopyCoverFromCacheToDownloadLocation(int? retries = 1)
|
||||
private void CopyCoverFromCacheToDownloadLocation(Manga manga, int? retries = 1)
|
||||
{
|
||||
//Check if Publication already has a Folder and cover
|
||||
string publicationFolder = Chapter.ParentManga.CreatePublicationFolder();
|
||||
string publicationFolder = manga.CreatePublicationFolder();
|
||||
DirectoryInfo dirInfo = new (publicationFolder);
|
||||
if (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover", StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? fileInCache = Chapter.ParentManga.CoverFileNameInCache;
|
||||
string? fileInCache = manga.CoverFileNameInCache;
|
||||
if (fileInCache is null || !File.Exists(fileInCache))
|
||||
{
|
||||
if (retries > 0 && Chapter.ParentManga.CoverUrl is not null)
|
||||
if (retries > 0)
|
||||
{
|
||||
Chapter.ParentManga.SaveCoverImageToCache();
|
||||
CopyCoverFromCacheToDownloadLocation(--retries);
|
||||
manga.SaveCoverImageToCache();
|
||||
CopyCoverFromCacheToDownloadLocation(manga, --retries);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -12,28 +12,35 @@ public abstract class Job
|
||||
public string JobId { get; init; }
|
||||
|
||||
[MaxLength(64)]
|
||||
public string? ParentJobId { get; internal set; }
|
||||
internal virtual Job ParentJob { get; }
|
||||
public string? ParentJobId { get; init; }
|
||||
public Job? ParentJob { get; init; }
|
||||
|
||||
[MaxLength(64)]
|
||||
public string[]? DependsOnJobIds { get; init; }
|
||||
public virtual Job[] DependsOnJobs { get; init; }
|
||||
public ICollection<string>? DependsOnJobsIds { get; init; }
|
||||
public ICollection<Job>? DependsOnJobs { get; init; }
|
||||
|
||||
public JobType JobType { get; init; }
|
||||
public ulong RecurrenceMs { get; set; }
|
||||
public DateTime LastExecution { get; internal set; } = DateTime.UnixEpoch;
|
||||
public DateTime NextExecution { get; internal set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateTime NextExecution => LastExecution.AddMilliseconds(RecurrenceMs);
|
||||
public JobState state { get; internal set; } = JobState.Waiting;
|
||||
|
||||
public Job(string jobId, JobType jobType, ulong recurrenceMs, string? parentJobId = null,
|
||||
string[]? dependsOnJobIds = null)
|
||||
public Job(string jobId, JobType jobType, ulong recurrenceMs, Job? parentJob = null, ICollection<Job>? dependsOnJobs = null)
|
||||
: this(jobId, jobType, recurrenceMs, parentJob?.JobId, dependsOnJobs?.Select(j => j.JobId).ToList())
|
||||
{
|
||||
this.ParentJob = parentJob;
|
||||
this.DependsOnJobs = dependsOnJobs;
|
||||
}
|
||||
|
||||
public Job(string jobId, JobType jobType, ulong recurrenceMs, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
{
|
||||
JobId = jobId;
|
||||
ParentJobId = parentJobId;
|
||||
DependsOnJobIds = dependsOnJobIds;
|
||||
DependsOnJobsIds = dependsOnJobsIds;
|
||||
JobType = jobType;
|
||||
RecurrenceMs = recurrenceMs;
|
||||
NextExecution = LastExecution.AddMilliseconds(RecurrenceMs);
|
||||
}
|
||||
|
||||
public IEnumerable<Job> Run(PgsqlContext context)
|
||||
|
@ -6,5 +6,6 @@ public enum JobType : byte
|
||||
DownloadSingleChapterJob = 0,
|
||||
DownloadNewChaptersJob = 1,
|
||||
UpdateMetaDataJob = 2,
|
||||
MoveFileOrFolderJob = 3
|
||||
MoveFileOrFolderJob = 3,
|
||||
DownloadMangaCoverJob = 4
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class MoveFileOrFolderJob(string fromLocation, string toLocation, string? parentJobId = null, string[]? dependsOnJobIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(MoveFileOrFolderJob), 64), JobType.MoveFileOrFolderJob, 0, parentJobId, dependsOnJobIds)
|
||||
public class MoveFileOrFolderJob(string fromLocation, string toLocation, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(MoveFileOrFolderJob), 64), JobType.MoveFileOrFolderJob, 0, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
public string FromLocation { get; init; } = fromLocation;
|
||||
public string ToLocation { get; init; } = toLocation;
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class UpdateMetadataJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, string[]? dependsOnJobIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(UpdateMetadataJob), 64), JobType.UpdateMetaDataJob, recurrenceMs, parentJobId, dependsOnJobIds)
|
||||
public class UpdateMetadataJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(UpdateMetadataJob), 64), JobType.UpdateMetaDataJob, recurrenceMs, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string MangaId { get; init; } = mangaId;
|
||||
|
Reference in New Issue
Block a user