Use publicationCache to store and update Manga
This commit is contained in:
parent
e360037fda
commit
8c66bbc89f
@ -1,8 +1,11 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Tranga.LibraryConnectors;
|
||||
using Tranga.MangaConnectors;
|
||||
using Tranga.NotificationConnectors;
|
||||
|
||||
namespace Tranga;
|
||||
@ -14,7 +17,9 @@ public abstract class GlobalBase
|
||||
protected TrangaSettings settings { get; init; }
|
||||
protected HashSet<NotificationConnector> notificationConnectors { get; init; }
|
||||
protected HashSet<LibraryConnector> libraryConnectors { get; init; }
|
||||
protected List<Manga> cachedPublications { get; init; }
|
||||
private Dictionary<string, Manga> cachedPublications { get; init; }
|
||||
|
||||
protected HashSet<MangaConnector> _connectors;
|
||||
public static readonly NumberFormatInfo numberFormatDecimalPoint = new (){ NumberDecimalSeparator = "." };
|
||||
protected static readonly Regex baseUrlRex = new(@"https?:\/\/[0-9A-z\.-]+(:[0-9]+)?");
|
||||
|
||||
@ -25,6 +30,7 @@ public abstract class GlobalBase
|
||||
this.notificationConnectors = clone.notificationConnectors;
|
||||
this.libraryConnectors = clone.libraryConnectors;
|
||||
this.cachedPublications = clone.cachedPublications;
|
||||
this._connectors = clone._connectors;
|
||||
}
|
||||
|
||||
protected GlobalBase(Logger? logger, TrangaSettings settings)
|
||||
@ -34,6 +40,85 @@ public abstract class GlobalBase
|
||||
this.notificationConnectors = settings.LoadNotificationConnectors(this);
|
||||
this.libraryConnectors = settings.LoadLibraryConnectors(this);
|
||||
this.cachedPublications = new();
|
||||
this._connectors = new();
|
||||
}
|
||||
|
||||
protected Manga? GetCachedManga(string internalId)
|
||||
{
|
||||
return cachedPublications.TryGetValue(internalId, out Manga manga) switch
|
||||
{
|
||||
true => manga,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
protected IEnumerable<Manga> GetAllCachedManga() => cachedPublications.Values;
|
||||
|
||||
protected void AddMangaToCache(Manga manga)
|
||||
{
|
||||
if (!cachedPublications.TryAdd(manga.internalId, manga))
|
||||
{
|
||||
Log($"Overwriting Manga {manga.internalId}");
|
||||
cachedPublications[manga.internalId] = manga;
|
||||
}
|
||||
ExportManga();
|
||||
}
|
||||
|
||||
protected void RemoveMangaFromCache(Manga manga) => RemoveMangaFromCache(manga.internalId);
|
||||
|
||||
protected void RemoveMangaFromCache(string internalId)
|
||||
{
|
||||
cachedPublications.Remove(internalId);
|
||||
ExportManga();
|
||||
}
|
||||
|
||||
internal void ImportManga()
|
||||
{
|
||||
string folder = settings.mangaCacheFolderPath;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
Directory.CreateDirectory(folder,
|
||||
UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.OtherRead | UnixFileMode.OtherWrite |
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite);
|
||||
else
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
foreach (FileInfo fileInfo in new DirectoryInfo(folder).GetFiles())
|
||||
{
|
||||
string content = File.ReadAllText(fileInfo.FullName);
|
||||
try
|
||||
{
|
||||
Manga m = JsonConvert.DeserializeObject<Manga>(content, new MangaConnectorJsonConverter(this, _connectors));
|
||||
this.cachedPublications.TryAdd(m.internalId, m);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Log($"Error parsing Manga {fileInfo.Name}:\n{e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ExportManga()
|
||||
{
|
||||
string folder = settings.mangaCacheFolderPath;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
Directory.CreateDirectory(folder,
|
||||
UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.OtherRead | UnixFileMode.OtherWrite |
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite);
|
||||
else
|
||||
Directory.CreateDirectory(folder);
|
||||
foreach (Manga manga in cachedPublications.Values)
|
||||
{
|
||||
string content = JsonConvert.SerializeObject(manga, Formatting.Indented);
|
||||
string filePath = Path.Combine(folder, $"{manga.internalId}.json");
|
||||
File.WriteAllText(filePath, content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
foreach (FileInfo fileInfo in new DirectoryInfo(folder).GetFiles())
|
||||
{
|
||||
if(!cachedPublications.Keys.Any(key => fileInfo.Name.Substring(0, fileInfo.Name.LastIndexOf('.')).Equals(key)))
|
||||
fileInfo.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void Log(string message)
|
||||
|
@ -7,12 +7,12 @@ public class DownloadChapter : Job
|
||||
{
|
||||
public Chapter chapter { get; init; }
|
||||
|
||||
public DownloadChapter(GlobalBase clone, MangaConnector connector, Chapter chapter, DateTime lastExecution, string? parentJobId = null) : base(clone, JobType.DownloadChapterJob, connector, lastExecution, parentJobId: parentJobId)
|
||||
public DownloadChapter(GlobalBase clone, Chapter chapter, DateTime lastExecution, string? parentJobId = null) : base(clone, JobType.DownloadChapterJob, lastExecution, parentJobId: parentJobId)
|
||||
{
|
||||
this.chapter = chapter;
|
||||
}
|
||||
|
||||
public DownloadChapter(GlobalBase clone, MangaConnector connector, Chapter chapter, string? parentJobId = null) : base(clone, JobType.DownloadChapterJob, connector, parentJobId: parentJobId)
|
||||
public DownloadChapter(GlobalBase clone, Chapter chapter, string? parentJobId = null) : base(clone, JobType.DownloadChapterJob, parentJobId: parentJobId)
|
||||
{
|
||||
this.chapter = chapter;
|
||||
}
|
||||
@ -44,11 +44,15 @@ public class DownloadChapter : Job
|
||||
return Array.Empty<Job>();
|
||||
}
|
||||
|
||||
protected override MangaConnector GetMangaConnector()
|
||||
{
|
||||
return chapter.parentManga.mangaConnector;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not DownloadChapter otherJob)
|
||||
return false;
|
||||
return otherJob.mangaConnector == this.mangaConnector &&
|
||||
otherJob.chapter.Equals(this.chapter);
|
||||
return otherJob.chapter.Equals(this.chapter);
|
||||
}
|
||||
}
|
@ -1,29 +1,29 @@
|
||||
using Tranga.MangaConnectors;
|
||||
using Newtonsoft.Json;
|
||||
using Tranga.MangaConnectors;
|
||||
|
||||
namespace Tranga.Jobs;
|
||||
|
||||
public class DownloadNewChapters : Job
|
||||
{
|
||||
public Manga manga { get; set; }
|
||||
public string mangaInternalId { get; set; }
|
||||
[JsonIgnore] private Manga? manga => GetCachedManga(mangaInternalId);
|
||||
public string translatedLanguage { get; init; }
|
||||
|
||||
public DownloadNewChapters(GlobalBase clone, Manga manga, DateTime lastExecution,
|
||||
bool recurring = false, TimeSpan? recurrence = null, string? parentJobId = null, string translatedLanguage = "en") : base(clone, JobType.DownloadNewChaptersJob, manga.mangaConnector, lastExecution, recurring,
|
||||
recurrence, parentJobId)
|
||||
public DownloadNewChapters(GlobalBase clone, string mangaInternalId, DateTime lastExecution, bool recurring = false, TimeSpan? recurrence = null, string? parentJobId = null, string translatedLanguage = "en") : base(clone, JobType.DownloadNewChaptersJob, lastExecution, recurring, recurrence, parentJobId)
|
||||
{
|
||||
this.manga = manga;
|
||||
this.mangaInternalId = mangaInternalId;
|
||||
this.translatedLanguage = translatedLanguage;
|
||||
}
|
||||
|
||||
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, bool recurring = false, TimeSpan? recurrence = null, string? parentJobId = null, string translatedLanguage = "en") : base (clone, JobType.DownloadNewChaptersJob, connector, recurring, recurrence, parentJobId)
|
||||
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, string mangaInternalId, bool recurring = false, TimeSpan? recurrence = null, string? parentJobId = null, string translatedLanguage = "en") : base (clone, JobType.DownloadNewChaptersJob, recurring, recurrence, parentJobId)
|
||||
{
|
||||
this.manga = manga;
|
||||
this.mangaInternalId = mangaInternalId;
|
||||
this.translatedLanguage = translatedLanguage;
|
||||
}
|
||||
|
||||
protected override string GetId()
|
||||
{
|
||||
return $"{GetType()}-{manga.internalId}";
|
||||
return $"{GetType()}-{mangaInternalId}";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@ -33,27 +33,38 @@ public class DownloadNewChapters : Job
|
||||
|
||||
protected override IEnumerable<Job> ExecuteReturnSubTasksInternal(JobBoss jobBoss)
|
||||
{
|
||||
manga.SaveSeriesInfoJson(settings.downloadLocation);
|
||||
Chapter[] chapters = mangaConnector.GetNewChapters(manga, this.translatedLanguage);
|
||||
if (manga is null)
|
||||
{
|
||||
Log($"Manga {mangaInternalId} is missing! Can not execute job.");
|
||||
return Array.Empty<Job>();
|
||||
}
|
||||
manga.Value.SaveSeriesInfoJson(settings.downloadLocation);
|
||||
Chapter[] chapters = manga.Value.mangaConnector.GetNewChapters(manga.Value, this.translatedLanguage);
|
||||
this.progressToken.increments = chapters.Length;
|
||||
List<Job> jobs = new();
|
||||
mangaConnector.CopyCoverFromCacheToDownloadLocation(manga);
|
||||
manga.Value.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga.Value);
|
||||
foreach (Chapter chapter in chapters)
|
||||
{
|
||||
DownloadChapter downloadChapterJob = new(this, this.mangaConnector, chapter, parentJobId: this.id);
|
||||
DownloadChapter downloadChapterJob = new(this, chapter, parentJobId: this.id);
|
||||
jobs.Add(downloadChapterJob);
|
||||
}
|
||||
UpdateMetadata updateMetadataJob = new(this, this.manga, parentJobId: this.id);
|
||||
UpdateMetadata updateMetadataJob = new(this, mangaInternalId, parentJobId: this.id);
|
||||
jobs.Add(updateMetadataJob);
|
||||
progressToken.Complete();
|
||||
return jobs;
|
||||
}
|
||||
|
||||
protected override MangaConnector GetMangaConnector()
|
||||
{
|
||||
if (manga is null)
|
||||
throw new Exception($"Missing Manga {mangaInternalId}");
|
||||
return manga.Value.mangaConnector;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not DownloadNewChapters otherJob)
|
||||
return false;
|
||||
return otherJob.mangaConnector == this.mangaConnector &&
|
||||
otherJob.manga.Equals(this.manga);
|
||||
return otherJob.manga.Equals(this.manga);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ namespace Tranga.Jobs;
|
||||
|
||||
public abstract class Job : GlobalBase
|
||||
{
|
||||
public MangaConnector mangaConnector { get; init; }
|
||||
public ProgressToken progressToken { get; private set; }
|
||||
public bool recurring { get; init; }
|
||||
public TimeSpan? recurrenceTime { get; set; }
|
||||
@ -15,12 +14,13 @@ public abstract class Job : GlobalBase
|
||||
public string? parentJobId { get; init; }
|
||||
public enum JobType : byte { DownloadChapterJob, DownloadNewChaptersJob, UpdateMetaDataJob, MonitorManga }
|
||||
|
||||
public MangaConnector mangaConnector => GetMangaConnector();
|
||||
|
||||
public JobType jobType;
|
||||
|
||||
internal Job(GlobalBase clone, JobType jobType, MangaConnector connector, bool recurring = false, TimeSpan? recurrenceTime = null, string? parentJobId = null) : base(clone)
|
||||
internal Job(GlobalBase clone, JobType jobType, bool recurring = false, TimeSpan? recurrenceTime = null, string? parentJobId = null) : base(clone)
|
||||
{
|
||||
this.jobType = jobType;
|
||||
this.mangaConnector = connector;
|
||||
this.progressToken = new ProgressToken(0);
|
||||
this.recurring = recurring;
|
||||
if (recurring && recurrenceTime is null)
|
||||
@ -31,11 +31,10 @@ public abstract class Job : GlobalBase
|
||||
this.parentJobId = parentJobId;
|
||||
}
|
||||
|
||||
internal Job(GlobalBase clone, JobType jobType, MangaConnector connector, DateTime lastExecution, bool recurring = false,
|
||||
internal Job(GlobalBase clone, JobType jobType, DateTime lastExecution, bool recurring = false,
|
||||
TimeSpan? recurrenceTime = null, string? parentJobId = null) : base(clone)
|
||||
{
|
||||
this.jobType = jobType;
|
||||
this.mangaConnector = connector;
|
||||
this.progressToken = new ProgressToken(0);
|
||||
this.recurring = recurring;
|
||||
if (recurring && recurrenceTime is null)
|
||||
@ -95,4 +94,6 @@ public abstract class Job : GlobalBase
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<Job> ExecuteReturnSubTasksInternal(JobBoss jobBoss);
|
||||
|
||||
protected abstract MangaConnector GetMangaConnector();
|
||||
}
|
@ -65,11 +65,9 @@ public class JobBoss : GlobalBase
|
||||
RemoveJob(job);
|
||||
}
|
||||
|
||||
public IEnumerable<Job> GetJobsLike(string? connectorName = null, string? internalId = null, string? chapterNumber = null)
|
||||
public IEnumerable<Job> GetJobsLike(string? internalId = null, string? chapterNumber = null)
|
||||
{
|
||||
IEnumerable<Job> ret = this.jobs;
|
||||
if (connectorName is not null)
|
||||
ret = ret.Where(job => job.mangaConnector.name == connectorName);
|
||||
|
||||
if (internalId is not null && chapterNumber is not null)
|
||||
ret = ret.Where(jjob =>
|
||||
@ -84,18 +82,18 @@ public class JobBoss : GlobalBase
|
||||
{
|
||||
if (jjob is not DownloadNewChapters job)
|
||||
return false;
|
||||
return job.manga.internalId == internalId;
|
||||
return job.mangaInternalId == internalId;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Manga? publication = null,
|
||||
public IEnumerable<Job> GetJobsLike(Manga? publication = null,
|
||||
Chapter? chapter = null)
|
||||
{
|
||||
if (chapter is not null)
|
||||
return GetJobsLike(mangaConnector?.name, chapter.Value.parentManga.internalId, chapter.Value.chapterNumber);
|
||||
return GetJobsLike(chapter.Value.parentManga.internalId, chapter.Value.chapterNumber);
|
||||
else
|
||||
return GetJobsLike(mangaConnector?.name, publication?.internalId);
|
||||
return GetJobsLike(publication?.internalId);
|
||||
}
|
||||
|
||||
public Job? GetJobById(string jobId)
|
||||
@ -155,15 +153,27 @@ public class JobBoss : GlobalBase
|
||||
this.jobs.Add(job);
|
||||
}
|
||||
|
||||
//Connect jobs to parent-jobs and add Publications to cache
|
||||
//Load Manga-Files
|
||||
ImportManga();
|
||||
|
||||
//Connect jobs to parent-jobs
|
||||
foreach (Job job in this.jobs)
|
||||
{
|
||||
this.jobs.FirstOrDefault(jjob => jjob.id == job.parentJobId)?.AddSubJob(job);
|
||||
if (job is DownloadNewChapters dncJob)
|
||||
cachedPublications.Add(dncJob.manga);
|
||||
}
|
||||
|
||||
HashSet<string> coverFileNames = cachedPublications.Select(manga => manga.coverFileNameInCache!).ToHashSet();
|
||||
string[] jobMangaInternalIds = this.jobs.Where(job => job is DownloadNewChapters)
|
||||
.Select(dnc => ((DownloadNewChapters)dnc).mangaInternalId).ToArray();
|
||||
jobMangaInternalIds = jobMangaInternalIds.Concat(
|
||||
this.jobs.Where(job => job is UpdateMetadata)
|
||||
.Select(dnc => ((UpdateMetadata)dnc).mangaInternalId)).ToArray();
|
||||
string[] internalIds = GetAllCachedManga().Select(m => m.internalId).ToArray();
|
||||
|
||||
string[] extraneousIds = internalIds.Except(jobMangaInternalIds).ToArray();
|
||||
foreach (string internalId in extraneousIds)
|
||||
RemoveMangaFromCache(internalId);
|
||||
|
||||
HashSet<string> coverFileNames = GetAllCachedManga().Select(manga => manga.coverFileNameInCache!).ToHashSet();
|
||||
foreach (string fileName in Directory.GetFiles(settings.coverImageCache)) //Cleanup Unused Covers
|
||||
{
|
||||
if(!coverFileNames.Any(existingManga => fileName.Contains(existingManga)))
|
||||
@ -171,7 +181,7 @@ public class JobBoss : GlobalBase
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateJobFile(Job job)
|
||||
internal void UpdateJobFile(Job job)
|
||||
{
|
||||
string jobFilePath = Path.Join(settings.jobsFolderPath, $"{job.id}.json");
|
||||
|
||||
@ -245,7 +255,9 @@ public class JobBoss : GlobalBase
|
||||
Log($"Next job in {jobs.MinBy(job => job.nextExecution)?.nextExecution.Subtract(DateTime.Now)} {jobs.MinBy(job => job.nextExecution)?.id}");
|
||||
}else if (queueHead.progressToken.state is ProgressToken.State.Standby)
|
||||
{
|
||||
Job[] subJobs = jobQueue.Peek().ExecuteReturnSubTasks(this).ToArray();
|
||||
Job eJob = jobQueue.Peek();
|
||||
Job[] subJobs = eJob.ExecuteReturnSubTasks(this).ToArray();
|
||||
UpdateJobFile(eJob);
|
||||
AddJobs(subJobs);
|
||||
AddJobsToQueue(subJobs);
|
||||
}else if (queueHead.progressToken.state is ProgressToken.State.Running && DateTime.Now.Subtract(queueHead.progressToken.lastUpdate) > TimeSpan.FromMinutes(5))
|
||||
|
@ -30,16 +30,9 @@ public class JobJsonConverter : JsonConverter
|
||||
return Enum.Parse<Job.JobType>(jo["jobType"]!.Value<byte>().ToString()) switch
|
||||
{
|
||||
Job.JobType.UpdateMetaDataJob => new UpdateMetadata(_clone,
|
||||
jo.GetValue("manga")!.ToObject<Manga>(JsonSerializer.Create(new JsonSerializerSettings()
|
||||
{
|
||||
Converters = { this._mangaConnectorJsonConverter }
|
||||
})),
|
||||
jo.GetValue("mangaInternalId")!.Value<string>()!,
|
||||
jo.GetValue("parentJobId")!.Value<string?>()),
|
||||
Job.JobType.DownloadChapterJob => new DownloadChapter(this._clone,
|
||||
jo.GetValue("mangaConnector")!.ToObject<MangaConnector>(JsonSerializer.Create(new JsonSerializerSettings()
|
||||
{
|
||||
Converters = { this._mangaConnectorJsonConverter }
|
||||
}))!,
|
||||
jo.GetValue("chapter")!.ToObject<Chapter>(JsonSerializer.Create(new JsonSerializerSettings()
|
||||
{
|
||||
Converters = { this._mangaConnectorJsonConverter }
|
||||
@ -47,10 +40,7 @@ public class JobJsonConverter : JsonConverter
|
||||
DateTime.UnixEpoch,
|
||||
jo.GetValue("parentJobId")!.Value<string?>()),
|
||||
Job.JobType.DownloadNewChaptersJob => new DownloadNewChapters(this._clone,
|
||||
jo.GetValue("manga")!.ToObject<Manga>(JsonSerializer.Create(new JsonSerializerSettings()
|
||||
{
|
||||
Converters = { this._mangaConnectorJsonConverter }
|
||||
})),
|
||||
jo.GetValue("mangaInternalId")!.Value<string>()!,
|
||||
jo.GetValue("lastExecution") is {} le
|
||||
? le.ToObject<DateTime>()
|
||||
: DateTime.UnixEpoch,
|
||||
|
@ -1,19 +1,21 @@
|
||||
using Tranga.MangaConnectors;
|
||||
using System.Text.Json.Serialization;
|
||||
using Tranga.MangaConnectors;
|
||||
|
||||
namespace Tranga.Jobs;
|
||||
|
||||
public class UpdateMetadata : Job
|
||||
{
|
||||
public Manga manga { get; set; }
|
||||
public string mangaInternalId { get; set; }
|
||||
[JsonIgnore] private Manga? manga => GetCachedManga(mangaInternalId);
|
||||
|
||||
public UpdateMetadata(GlobalBase clone, Manga manga, string? parentJobId = null) : base(clone, JobType.UpdateMetaDataJob, manga.mangaConnector, parentJobId: parentJobId)
|
||||
public UpdateMetadata(GlobalBase clone, string mangaInternalId, string? parentJobId = null) : base(clone, JobType.UpdateMetaDataJob, parentJobId: parentJobId)
|
||||
{
|
||||
this.manga = manga;
|
||||
this.mangaInternalId = mangaInternalId;
|
||||
}
|
||||
|
||||
protected override string GetId()
|
||||
{
|
||||
return $"{GetType()}-{manga.internalId}";
|
||||
return $"{GetType()}-{mangaInternalId}";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@ -23,8 +25,14 @@ public class UpdateMetadata : Job
|
||||
|
||||
protected override IEnumerable<Job> ExecuteReturnSubTasksInternal(JobBoss jobBoss)
|
||||
{
|
||||
if (manga is null)
|
||||
{
|
||||
Log($"Manga {mangaInternalId} is missing! Can not execute job.");
|
||||
return Array.Empty<Job>();
|
||||
}
|
||||
|
||||
//Retrieve new Metadata
|
||||
Manga? possibleUpdatedManga = mangaConnector.GetMangaFromId(manga.publicationId);
|
||||
Manga? possibleUpdatedManga = mangaConnector.GetMangaFromId(manga.Value.publicationId);
|
||||
if (possibleUpdatedManga is { } updatedManga)
|
||||
{
|
||||
if (updatedManga.Equals(this.manga)) //Check if anything changed
|
||||
@ -33,8 +41,8 @@ public class UpdateMetadata : Job
|
||||
return Array.Empty<Job>();
|
||||
}
|
||||
|
||||
this.manga.UpdateMetadata(updatedManga);
|
||||
this.manga.SaveSeriesInfoJson(settings.downloadLocation, true);
|
||||
AddMangaToCache(manga.Value.WithMetadata(updatedManga));
|
||||
this.manga.Value.SaveSeriesInfoJson(settings.downloadLocation, true);
|
||||
this.progressToken.Complete();
|
||||
}
|
||||
else
|
||||
@ -47,12 +55,18 @@ public class UpdateMetadata : Job
|
||||
return Array.Empty<Job>();
|
||||
}
|
||||
|
||||
protected override MangaConnector GetMangaConnector()
|
||||
{
|
||||
if (manga is null)
|
||||
throw new Exception($"Missing Manga {mangaInternalId}");
|
||||
return manga.Value.mangaConnector;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
|
||||
if (obj is not UpdateMetadata otherJob)
|
||||
return false;
|
||||
return otherJob.mangaConnector == this.mangaConnector &&
|
||||
otherJob.manga.Equals(this.manga);
|
||||
return otherJob.manga.Equals(this.manga);
|
||||
}
|
||||
}
|
@ -74,16 +74,20 @@ public struct Manga
|
||||
this.websiteUrl = websiteUrl;
|
||||
}
|
||||
|
||||
public void UpdateMetadata(Manga newManga)
|
||||
public Manga WithMetadata(Manga newManga)
|
||||
{
|
||||
this.sortName = newManga.sortName;
|
||||
this.description = newManga.description;
|
||||
this.coverUrl = newManga.coverUrl;
|
||||
this.authors = authors.Union(newManga.authors).ToList();
|
||||
this.altTitles = altTitles.UnionBy(newManga.altTitles, kv => kv.Key).ToDictionary(x => x.Key, x => x.Value);
|
||||
this.tags = tags.Union(newManga.tags).ToArray();
|
||||
this.releaseStatus = newManga.releaseStatus;
|
||||
this.year = newManga.year;
|
||||
return this with
|
||||
{
|
||||
sortName = newManga.sortName,
|
||||
description = newManga.description,
|
||||
coverUrl = newManga.coverUrl,
|
||||
authors = authors.Union(newManga.authors).ToList(),
|
||||
altTitles = altTitles.UnionBy(newManga.altTitles, kv => kv.Key).ToDictionary(x => x.Key, x => x.Value),
|
||||
tags = tags.Union(newManga.tags).ToArray(),
|
||||
releaseStatus = newManga.releaseStatus,
|
||||
year = newManga.year,
|
||||
websiteUrl = newManga.websiteUrl
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@ -96,6 +100,7 @@ public struct Manga
|
||||
this.sortName == compareManga.sortName &&
|
||||
this.latestChapterAvailable.Equals(compareManga.latestChapterAvailable) &&
|
||||
this.authors.All(a => compareManga.authors.Contains(a)) &&
|
||||
this.websiteUrl.Equals(compareManga.websiteUrl) &&
|
||||
this.tags.All(t => compareManga.tags.Contains(t));
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ public class Bato : MangaConnector
|
||||
|
||||
Manga manga = new (this, sortName, authors, description, altTitles, tags, posterUrl, coverFileNameInCache, new Dictionary<string, string>(),
|
||||
year, originalLanguage, publicationId, releaseStatus, websiteUrl);
|
||||
cachedPublications.Add(manga);
|
||||
AddMangaToCache(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ public class MangaDex : MangaConnector
|
||||
releaseStatus,
|
||||
$"https://mangadex.org/title/{publicationId}"
|
||||
);
|
||||
cachedPublications.Add(pub);
|
||||
AddMangaToCache(pub);
|
||||
return pub;
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ public class MangaKatana : MangaConnector
|
||||
|
||||
Manga manga = new (this, sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
|
||||
year, originalLanguage, publicationId, releaseStatus, websiteUrl);
|
||||
cachedPublications.Add(manga);
|
||||
AddMangaToCache(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ public class MangaLife : MangaConnector
|
||||
|
||||
Manga manga = new(this, sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl,
|
||||
coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl);
|
||||
cachedPublications.Add(manga);
|
||||
AddMangaToCache(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class Manganato : MangaConnector
|
||||
|
||||
Manga manga = new (this, sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
|
||||
year, originalLanguage, publicationId, releaseStatus, websiteUrl);
|
||||
cachedPublications.Add(manga);
|
||||
AddMangaToCache(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ public class Mangasee : MangaConnector
|
||||
|
||||
Manga manga = new(this, sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl,
|
||||
coverFileNameInCache, links, year, originalLanguage, publicationId, releaseStatus, websiteUrl);
|
||||
cachedPublications.Add(manga);
|
||||
AddMangaToCache(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ public class Mangaworld: MangaConnector
|
||||
|
||||
Manga manga = new (this, sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
|
||||
year, originalLanguage, publicationId, releaseStatus, websiteUrl);
|
||||
cachedPublications.Add(manga);
|
||||
AddMangaToCache(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ public partial class Server : GlobalBase, IDisposable
|
||||
new ("GET", @"/v2/Jobs/Waiting", GetV2JobsWaiting),
|
||||
new ("GET", @"/v2/Jobs/Monitoring", GetV2JobsMonitoring),
|
||||
new ("Get", @"/v2/Job/Types", GetV2JobTypes),
|
||||
new ("POST", @"/v2/Job/Create/([a-zA-Z]+)", PostV2JobsCreateType),
|
||||
new ("POST", @"/v2/Job/Create/([a-zA-Z]+)", PostV2JobCreateType),
|
||||
new ("GET", @"/v2/Job/([a-zA-Z\.]+-[-A-Za-z0-9+/]*={0,3}(?:-[0-9]+)?)", GetV2JobJobId),
|
||||
new ("DELETE", @"/v2/Job/([a-zA-Z\.]+-[-A-Za-z0-9+/]*={0,3}(?:-[0-9]+)?)", DeleteV2JobJobId),
|
||||
new ("GET", @"/v2/Job/([a-zA-Z\.]+-[-A-Za-z0-9+/]*={0,3}(?:-[0-9]+)?)/Progress", GetV2JobJobIdProgress),
|
||||
|
@ -38,7 +38,7 @@ public partial class Server
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.OK, Enum.GetNames(typeof(Job.JobType)));
|
||||
}
|
||||
|
||||
private ValueTuple<HttpStatusCode, object?> PostV2JobsCreateType(GroupCollection groups, Dictionary<string, string> requestParameters)
|
||||
private ValueTuple<HttpStatusCode, object?> PostV2JobCreateType(GroupCollection groups, Dictionary<string, string> requestParameters)
|
||||
{
|
||||
if (groups.Count < 1 ||
|
||||
!Enum.TryParse(groups[1].Value, true, out Job.JobType jobType))
|
||||
@ -59,14 +59,14 @@ public partial class Server
|
||||
!TimeSpan.TryParse(intervalStr, out TimeSpan interval))
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.InternalServerError, "'interval' Parameter missing, or is not in correct format.");
|
||||
requestParameters.TryGetValue("language", out string? language);
|
||||
_parent.jobBoss.AddJob(new DownloadNewChapters(this, ((Manga)manga).mangaConnector, (Manga)manga, true, interval, language));
|
||||
_parent.jobBoss.AddJob(new DownloadNewChapters(this, ((Manga)manga).mangaConnector, ((Manga)manga).internalId, true, interval, language));
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.OK, null);
|
||||
case Job.JobType.UpdateMetaDataJob:
|
||||
if(!requestParameters.TryGetValue("internalId", out mangaId) ||
|
||||
!_parent.TryGetPublicationById(mangaId, out manga) ||
|
||||
manga is null)
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.NotFound, "InternalId Parameter missing, or is not a valid ID.");
|
||||
_parent.jobBoss.AddJob(new UpdateMetadata(this, (Manga)manga));
|
||||
_parent.jobBoss.AddJob(new UpdateMetadata(this, ((Manga)manga).internalId));
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.OK, null);
|
||||
case Job.JobType.DownloadNewChaptersJob: //TODO
|
||||
case Job.JobType.DownloadChapterJob: //TODO
|
||||
|
@ -8,7 +8,7 @@ public partial class Server
|
||||
{
|
||||
private ValueTuple<HttpStatusCode, object?> GetV2Manga(GroupCollection groups, Dictionary<string, string> requestParameters)
|
||||
{
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.OK, cachedPublications.Select(m => m.internalId));
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.OK, GetAllCachedManga().Select(m => m.internalId));
|
||||
}
|
||||
|
||||
private ValueTuple<HttpStatusCode, object?> GetV2MangaInternalId(GroupCollection groups, Dictionary<string, string> requestParameters)
|
||||
@ -28,6 +28,7 @@ public partial class Server
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.NotFound, $"Manga with ID '{groups[1].Value} could not be found.'");
|
||||
Job[] jobs = _parent.jobBoss.GetJobsLike(publication: manga).ToArray();
|
||||
_parent.jobBoss.RemoveJobs(jobs);
|
||||
RemoveMangaFromCache(groups[1].Value);
|
||||
return new ValueTuple<HttpStatusCode, object?>(HttpStatusCode.OK, null);
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ public partial class Tranga : GlobalBase
|
||||
public bool keepRunning;
|
||||
public JobBoss jobBoss;
|
||||
private Server.Server _server;
|
||||
private HashSet<MangaConnector> _connectors;
|
||||
|
||||
public Tranga(Logger? logger, TrangaSettings settings) : base(logger, settings)
|
||||
{
|
||||
@ -54,12 +53,7 @@ public partial class Tranga : GlobalBase
|
||||
return _connectors.Select(c => c.name);
|
||||
}
|
||||
|
||||
public Manga? GetPublicationById(string internalId)
|
||||
{
|
||||
if (cachedPublications.Exists(publication => publication.internalId == internalId))
|
||||
return cachedPublications.First(publication => publication.internalId == internalId);
|
||||
return null;
|
||||
}
|
||||
public Manga? GetPublicationById(string internalId) => GetCachedManga(internalId);
|
||||
|
||||
public bool TryGetPublicationById(string internalId, out Manga? manga)
|
||||
{
|
||||
|
@ -19,6 +19,7 @@ public class TrangaSettings
|
||||
[JsonIgnore] public string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json");
|
||||
[JsonIgnore] public string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json");
|
||||
[JsonIgnore] public string jobsFolderPath => Path.Join(workingDirectory, "jobs");
|
||||
[JsonIgnore] public string mangaCacheFolderPath => Path.Join(workingDirectory, "manga");
|
||||
[JsonIgnore] public string coverImageCache => Path.Join(workingDirectory, "imageCache");
|
||||
[JsonIgnore] internal static readonly string DefaultUserAgent = $"Tranga ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")}) / 1.0";
|
||||
public ushort? version { get; } = 2;
|
||||
|
Loading…
Reference in New Issue
Block a user