Use publicationCache to store and update Manga

This commit is contained in:
Glax 2024-04-22 23:45:51 +02:00
parent e360037fda
commit 8c66bbc89f
20 changed files with 207 additions and 89 deletions

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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)
@ -154,16 +152,28 @@ public class JobBoss : GlobalBase
new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors)))!;
this.jobs.Add(job);
}
//Load Manga-Files
ImportManga();
//Connect jobs to parent-jobs and add Publications to cache
//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))

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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));
}

View File

@ -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;
}

View File

@ -188,7 +188,7 @@ public class MangaDex : MangaConnector
releaseStatus,
$"https://mangadex.org/title/{publicationId}"
);
cachedPublications.Add(pub);
AddMangaToCache(pub);
return pub;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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),

View File

@ -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

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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;