Compare commits
7 Commits
2a389f1ede
...
ed79ee5d0f
Author | SHA1 | Date | |
---|---|---|---|
ed79ee5d0f | |||
28e05e549d | |||
eaab7c5235 | |||
0552b3db82 | |||
c813e1854d | |||
32036df057 | |||
394829ee36 |
@ -45,14 +45,22 @@ public class MemoryLogger : LoggerBase
|
|||||||
public string[] GetNewLines()
|
public string[] GetNewLines()
|
||||||
{
|
{
|
||||||
int logMessageCount = _logMessages.Count;
|
int logMessageCount = _logMessages.Count;
|
||||||
string[] ret = new string[logMessageCount - _lastLogMessageIndex];
|
List<string> ret = new();
|
||||||
|
|
||||||
for (int retIndex = 0; retIndex < ret.Length; retIndex++)
|
int retIndex = 0;
|
||||||
|
for (; retIndex < logMessageCount - _lastLogMessageIndex; retIndex++)
|
||||||
{
|
{
|
||||||
ret[retIndex] = _logMessages.GetValueAtIndex(_lastLogMessageIndex + retIndex).ToString();
|
try
|
||||||
|
{
|
||||||
|
ret.Add(_logMessages.GetValueAtIndex(_lastLogMessageIndex + retIndex).ToString());
|
||||||
|
}
|
||||||
|
catch (NullReferenceException e)//Called when LogMessage has not finished writing
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastLogMessageIndex = logMessageCount;
|
_lastLogMessageIndex = _lastLogMessageIndex + retIndex;
|
||||||
return ret;
|
return ret.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,6 +19,8 @@ public abstract class Job : GlobalBase
|
|||||||
this.recurring = recurring;
|
this.recurring = recurring;
|
||||||
if (recurring && recurrenceTime is null)
|
if (recurring && recurrenceTime is null)
|
||||||
throw new ArgumentException("If recurrence is set to true, a recurrence time has to be provided.");
|
throw new ArgumentException("If recurrence is set to true, a recurrence time has to be provided.");
|
||||||
|
else if(recurring && recurrenceTime is not null)
|
||||||
|
this.lastExecution = DateTime.Now.Subtract((TimeSpan)recurrenceTime);
|
||||||
this.recurrenceTime = recurrenceTime;
|
this.recurrenceTime = recurrenceTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Tranga.MangaConnectors;
|
using Newtonsoft.Json;
|
||||||
|
using Tranga.MangaConnectors;
|
||||||
|
|
||||||
namespace Tranga.Jobs;
|
namespace Tranga.Jobs;
|
||||||
|
|
||||||
@ -7,16 +8,42 @@ public class JobBoss : GlobalBase
|
|||||||
public HashSet<Job> jobs { get; init; }
|
public HashSet<Job> jobs { get; init; }
|
||||||
private Dictionary<MangaConnector, Queue<Job>> mangaConnectorJobQueue { get; init; }
|
private Dictionary<MangaConnector, Queue<Job>> mangaConnectorJobQueue { get; init; }
|
||||||
|
|
||||||
public JobBoss(GlobalBase clone) : base(clone)
|
public JobBoss(GlobalBase clone, HashSet<MangaConnector> connectors) : base(clone)
|
||||||
{
|
{
|
||||||
this.jobs = new();
|
if (File.Exists(settings.jobsFilePath))
|
||||||
|
this.jobs = JsonConvert.DeserializeObject<HashSet<Job>>(File.ReadAllText(settings.jobsFilePath), new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors)))!;
|
||||||
|
else
|
||||||
|
this.jobs = new();
|
||||||
|
foreach (DownloadNewChapters ncJob in this.jobs.Where(job => job is DownloadNewChapters))
|
||||||
|
cachedPublications.Add(ncJob.manga);
|
||||||
this.mangaConnectorJobQueue = new();
|
this.mangaConnectorJobQueue = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddJob(Job job)
|
public void AddJob(Job job)
|
||||||
{
|
{
|
||||||
Log($"Added {job}");
|
if (ContainsJobLike(job))
|
||||||
this.jobs.Add(job);
|
{
|
||||||
|
Log($"Already Contains Job {job}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"Added {job}");
|
||||||
|
this.jobs.Add(job);
|
||||||
|
File.WriteAllText(settings.jobsFilePath, JsonConvert.SerializeObject(this.jobs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsJobLike(Job job)
|
||||||
|
{
|
||||||
|
if (job is DownloadChapter dcJob)
|
||||||
|
{
|
||||||
|
return this.GetJobsLike(dcJob.mangaConnector, chapter: dcJob.chapter).Any();
|
||||||
|
}else if (job is DownloadNewChapters ncJob)
|
||||||
|
{
|
||||||
|
return this.GetJobsLike(ncJob.mangaConnector, ncJob.manga).Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveJob(Job job)
|
public void RemoveJob(Job job)
|
||||||
@ -110,13 +137,13 @@ public class JobBoss : GlobalBase
|
|||||||
foreach (Queue<Job> jobQueue in mangaConnectorJobQueue.Values)
|
foreach (Queue<Job> jobQueue in mangaConnectorJobQueue.Values)
|
||||||
{
|
{
|
||||||
Job queueHead = jobQueue.Peek();
|
Job queueHead = jobQueue.Peek();
|
||||||
if (queueHead.progressToken.state == ProgressToken.State.Complete)
|
if (queueHead.progressToken.state is ProgressToken.State.Complete)
|
||||||
{
|
{
|
||||||
if(queueHead.recurring)
|
if(queueHead.recurring)
|
||||||
queueHead.Reset();
|
queueHead.Reset();
|
||||||
jobQueue.Dequeue();
|
jobQueue.Dequeue();
|
||||||
|
}else if(queueHead.progressToken.state is ProgressToken.State.Standby)
|
||||||
AddJobsToQueue(jobQueue.Peek().ExecuteReturnSubTasks());
|
AddJobsToQueue(jobQueue.Peek().ExecuteReturnSubTasks());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
66
Tranga/Jobs/JobJsonConverter.cs
Normal file
66
Tranga/Jobs/JobJsonConverter.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Tranga.MangaConnectors;
|
||||||
|
|
||||||
|
namespace Tranga.Jobs;
|
||||||
|
|
||||||
|
public class JobJsonConverter : JsonConverter
|
||||||
|
{
|
||||||
|
private GlobalBase _clone;
|
||||||
|
private MangaConnectorJsonConverter _mangaConnectorJsonConverter;
|
||||||
|
|
||||||
|
internal JobJsonConverter(GlobalBase clone, MangaConnectorJsonConverter mangaConnectorJsonConverter)
|
||||||
|
{
|
||||||
|
this._clone = clone;
|
||||||
|
this._mangaConnectorJsonConverter = mangaConnectorJsonConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return (objectType == typeof(Job));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
if (jo.ContainsKey("manga"))//DownloadNewChapters
|
||||||
|
{
|
||||||
|
return new DownloadNewChapters(this._clone,
|
||||||
|
jo.GetValue("mangaConnector")!.ToObject<MangaConnector>(JsonSerializer.Create(new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
Converters =
|
||||||
|
{
|
||||||
|
this._mangaConnectorJsonConverter
|
||||||
|
}
|
||||||
|
}))!,
|
||||||
|
jo.GetValue("manga")!.ToObject<Manga>(),
|
||||||
|
jo.GetValue("recurring")!.Value<bool>(),
|
||||||
|
jo.GetValue("recurrenceTime")!.ToObject<TimeSpan?>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jo.ContainsKey("chapter"))//DownloadChapter
|
||||||
|
{
|
||||||
|
return new DownloadChapter(this._clone,
|
||||||
|
jo.GetValue("mangaConnector")!.ToObject<MangaConnector>(JsonSerializer.Create(new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
Converters =
|
||||||
|
{
|
||||||
|
this._mangaConnectorJsonConverter
|
||||||
|
}
|
||||||
|
}))!,
|
||||||
|
jo.GetValue("chapter")!.ToObject<Chapter>());
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Don't call this
|
||||||
|
/// </summary>
|
||||||
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new Exception("Dont call this");
|
||||||
|
}
|
||||||
|
}
|
@ -36,14 +36,14 @@ public struct Manga
|
|||||||
private static readonly Regex LegalCharacters = new (@"[A-Z]*[a-z]*[0-9]* *\.*-*,*'*\'*\)*\(*~*!*");
|
private static readonly Regex LegalCharacters = new (@"[A-Z]*[a-z]*[0-9]* *\.*-*,*'*\'*\)*\(*~*!*");
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public Manga(string sortName, List<string> authors, string? description, Dictionary<string,string> altTitles, string[] tags, string? coverUrl, Dictionary<string,string>? links, int? year, string? originalLanguage, string status, string publicationId, string? folderName = null, float? ignoreChaptersBelow = 0)
|
public Manga(string sortName, List<string> authors, string? description, Dictionary<string,string> altTitles, string[] tags, string? coverUrl, string? coverFileNameInCache, Dictionary<string,string>? links, int? year, string? originalLanguage, string status, string publicationId, string? folderName = null, float? ignoreChaptersBelow = 0)
|
||||||
{
|
{
|
||||||
this.sortName = sortName;
|
this.sortName = sortName;
|
||||||
this.authors = authors;
|
this.authors = authors;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.altTitles = altTitles;
|
this.altTitles = altTitles;
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
this.coverFileNameInCache = null;
|
this.coverFileNameInCache = coverFileNameInCache;
|
||||||
this.coverUrl = coverUrl;
|
this.coverUrl = coverUrl;
|
||||||
this.links = links ?? new Dictionary<string, string>();
|
this.links = links ?? new Dictionary<string, string>();
|
||||||
this.year = year;
|
this.year = year;
|
||||||
|
49
Tranga/MangaConnectors/MangaConnectorJsonConverter.cs
Normal file
49
Tranga/MangaConnectors/MangaConnectorJsonConverter.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Tranga.MangaConnectors;
|
||||||
|
|
||||||
|
public class MangaConnectorJsonConverter : JsonConverter
|
||||||
|
{
|
||||||
|
private GlobalBase _clone;
|
||||||
|
private HashSet<MangaConnector> connectors;
|
||||||
|
|
||||||
|
internal MangaConnectorJsonConverter(GlobalBase clone, HashSet<MangaConnector> connectors)
|
||||||
|
{
|
||||||
|
this._clone = clone;
|
||||||
|
this.connectors = connectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return (objectType == typeof(MangaConnector));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
switch (jo.GetValue("name")!.Value<string>()!)
|
||||||
|
{
|
||||||
|
case "MangaDex":
|
||||||
|
return this.connectors.First(c => c is MangaDex);
|
||||||
|
case "Manganato":
|
||||||
|
return this.connectors.First(c => c is Manganato);
|
||||||
|
case "MangaKatana":
|
||||||
|
return this.connectors.First(c => c is MangaKatana);
|
||||||
|
case "Mangasee":
|
||||||
|
return this.connectors.First(c => c is Mangasee);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Don't call this
|
||||||
|
/// </summary>
|
||||||
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new Exception("Dont call this");
|
||||||
|
}
|
||||||
|
}
|
@ -102,6 +102,9 @@ public class MangaDex : MangaConnector
|
|||||||
authorIds.Add(node!["id"]!.GetValue<string>());
|
authorIds.Add(node!["id"]!.GetValue<string>());
|
||||||
}
|
}
|
||||||
string? coverUrl = GetCoverUrl(publicationId, posterId);
|
string? coverUrl = GetCoverUrl(publicationId, posterId);
|
||||||
|
string? coverCacheName = null;
|
||||||
|
if (coverUrl is not null)
|
||||||
|
coverCacheName = SaveCoverImageToCache(coverUrl, (byte)RequestType.AtHomeServer);
|
||||||
|
|
||||||
List<string> authors = GetAuthors(authorIds);
|
List<string> authors = GetAuthors(authorIds);
|
||||||
|
|
||||||
@ -132,6 +135,7 @@ public class MangaDex : MangaConnector
|
|||||||
altTitlesDict,
|
altTitlesDict,
|
||||||
tags.ToArray(),
|
tags.ToArray(),
|
||||||
coverUrl,
|
coverUrl,
|
||||||
|
coverCacheName,
|
||||||
linksDict,
|
linksDict,
|
||||||
year,
|
year,
|
||||||
originalLanguage,
|
originalLanguage,
|
||||||
@ -227,8 +231,6 @@ public class MangaDex : MangaConnector
|
|||||||
string comicInfoPath = Path.GetTempFileName();
|
string comicInfoPath = Path.GetTempFileName();
|
||||||
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
||||||
|
|
||||||
if (chapterParentManga.coverUrl is not null)
|
|
||||||
chapterParentManga.coverFileNameInCache = SaveCoverImageToCache(chapterParentManga.coverUrl, (byte)RequestType.AtHomeServer);
|
|
||||||
//Download Chapter-Images
|
//Download Chapter-Images
|
||||||
return DownloadChapterImages(imageUrls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), (byte)RequestType.AtHomeServer, comicInfoPath, progressToken:progressToken);
|
return DownloadChapterImages(imageUrls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), (byte)RequestType.AtHomeServer, comicInfoPath, progressToken:progressToken);
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,8 @@ public class MangaKatana : MangaConnector
|
|||||||
string posterUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First()
|
string posterUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First()
|
||||||
.GetAttributes().First(a => a.Name == "src").Value;
|
.GetAttributes().First(a => a.Name == "src").Value;
|
||||||
|
|
||||||
|
string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1);
|
||||||
|
|
||||||
string description = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[3]/p").InnerText;
|
string description = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[3]/p").InnerText;
|
||||||
while (description.StartsWith('\n'))
|
while (description.StartsWith('\n'))
|
||||||
description = description.Substring(1);
|
description = description.Substring(1);
|
||||||
@ -132,7 +134,7 @@ public class MangaKatana : MangaConnector
|
|||||||
year = Convert.ToInt32(yearString);
|
year = Convert.ToInt32(yearString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Manga(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, links,
|
return new Manga(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
|
||||||
year, originalLanguage, status, publicationId);
|
year, originalLanguage, status, publicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,9 +201,6 @@ public class MangaKatana : MangaConnector
|
|||||||
string comicInfoPath = Path.GetTempFileName();
|
string comicInfoPath = Path.GetTempFileName();
|
||||||
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
||||||
|
|
||||||
if (chapterParentManga.coverUrl is not null)
|
|
||||||
chapterParentManga.coverFileNameInCache = SaveCoverImageToCache(chapterParentManga.coverUrl, 1);
|
|
||||||
|
|
||||||
return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken);
|
return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +111,8 @@ public class Manganato : MangaConnector
|
|||||||
string posterUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First()
|
string posterUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First()
|
||||||
.GetAttributes().First(a => a.Name == "src").Value;
|
.GetAttributes().First(a => a.Name == "src").Value;
|
||||||
|
|
||||||
|
string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1);
|
||||||
|
|
||||||
string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description"))
|
string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description"))
|
||||||
.InnerText.Replace("Description :", "");
|
.InnerText.Replace("Description :", "");
|
||||||
while (description.StartsWith('\n'))
|
while (description.StartsWith('\n'))
|
||||||
@ -120,7 +122,7 @@ public class Manganato : MangaConnector
|
|||||||
.First(s => s.HasClass("chapter-time")).InnerText;
|
.First(s => s.HasClass("chapter-time")).InnerText;
|
||||||
int year = Convert.ToInt32(yearString.Split(',')[^1]) + 2000;
|
int year = Convert.ToInt32(yearString.Split(',')[^1]) + 2000;
|
||||||
|
|
||||||
return new Manga(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, links,
|
return new Manga(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
|
||||||
year, originalLanguage, status, publicationId);
|
year, originalLanguage, status, publicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +187,6 @@ public class Manganato : MangaConnector
|
|||||||
string comicInfoPath = Path.GetTempFileName();
|
string comicInfoPath = Path.GetTempFileName();
|
||||||
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
||||||
|
|
||||||
if (chapterParentManga.coverUrl is not null)
|
|
||||||
chapterParentManga.coverFileNameInCache = SaveCoverImageToCache(chapterParentManga.coverUrl, 1);
|
|
||||||
|
|
||||||
return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://chapmanganato.com/", progressToken:progressToken);
|
return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://chapmanganato.com/", progressToken:progressToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ public class Mangasee : MangaConnector
|
|||||||
HtmlNode posterNode =
|
HtmlNode posterNode =
|
||||||
document.DocumentNode.Descendants("img").First(img => img.HasClass("img-fluid") && img.HasClass("bottom-5"));
|
document.DocumentNode.Descendants("img").First(img => img.HasClass("img-fluid") && img.HasClass("bottom-5"));
|
||||||
string posterUrl = posterNode.GetAttributeValue("src", "");
|
string posterUrl = posterNode.GetAttributeValue("src", "");
|
||||||
|
string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1);
|
||||||
|
|
||||||
HtmlNode attributes = document.DocumentNode.Descendants("div")
|
HtmlNode attributes = document.DocumentNode.Descendants("div")
|
||||||
.First(div => div.HasClass("col-md-9") && div.HasClass("col-sm-8") && div.HasClass("top-5"))
|
.First(div => div.HasClass("col-md-9") && div.HasClass("col-sm-8") && div.HasClass("top-5"))
|
||||||
@ -170,7 +171,7 @@ public class Mangasee : MangaConnector
|
|||||||
foreach(string at in a)
|
foreach(string at in a)
|
||||||
altTitles.Add((i++).ToString(), at);
|
altTitles.Add((i++).ToString(), at);
|
||||||
|
|
||||||
return new Manga(sortName, authors, description, altTitles, tags.ToArray(), posterUrl, links,
|
return new Manga(sortName, authors, description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
|
||||||
year, originalLanguage, status, publicationId);
|
year, originalLanguage, status, publicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,9 +271,6 @@ public class Mangasee : MangaConnector
|
|||||||
string comicInfoPath = Path.GetTempFileName();
|
string comicInfoPath = Path.GetTempFileName();
|
||||||
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
|
||||||
|
|
||||||
if (chapterParentManga.coverUrl is not null)
|
|
||||||
chapterParentManga.coverFileNameInCache = SaveCoverImageToCache(chapterParentManga.coverUrl, 1);
|
|
||||||
|
|
||||||
return DownloadChapterImages(urls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, progressToken:progressToken);
|
return DownloadChapterImages(urls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, progressToken:progressToken);
|
||||||
}
|
}
|
||||||
return response.Status;
|
return response.Status;
|
||||||
|
101
Tranga/Server.cs
101
Tranga/Server.cs
@ -105,68 +105,85 @@ public class Server : GlobalBase
|
|||||||
private void HandleGet(HttpListenerRequest request, HttpListenerResponse response)
|
private void HandleGet(HttpListenerRequest request, HttpListenerResponse response)
|
||||||
{
|
{
|
||||||
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
|
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
|
||||||
string? connectorName, jobId;
|
string? connectorName, jobId, internalId;
|
||||||
MangaConnector connector;
|
MangaConnector? connector;
|
||||||
|
Manga? manga;
|
||||||
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
|
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
|
||||||
switch (path)
|
switch (path)
|
||||||
{
|
{
|
||||||
case "Connectors":
|
case "Connectors":
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent.GetConnectors().Select(con => con.name).ToArray());
|
SendResponse(HttpStatusCode.OK, response, _parent.GetConnectors().Select(con => con.name).ToArray());
|
||||||
break;
|
break;
|
||||||
|
case "Manga/Cover":
|
||||||
|
if (!requestVariables.TryGetValue("internalId", out internalId) ||
|
||||||
|
!_parent.TryGetPublicationById(internalId, out manga))
|
||||||
|
{
|
||||||
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string filePath = settings.GetFullCoverPath((Manga)manga!);
|
||||||
|
if (File.Exists(filePath))
|
||||||
|
{
|
||||||
|
FileStream coverStream = new(filePath, FileMode.Open);
|
||||||
|
SendResponse(HttpStatusCode.OK, response, coverStream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendResponse(HttpStatusCode.NotFound, response);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "Manga/FromConnector":
|
case "Manga/FromConnector":
|
||||||
if (!requestVariables.TryGetValue("connector", out connectorName) ||
|
if (!requestVariables.TryGetValue("connector", out connectorName) ||
|
||||||
!requestVariables.TryGetValue("title", out string? title) ||
|
!requestVariables.TryGetValue("title", out string? title) ||
|
||||||
_parent.GetConnector(connectorName) is null)
|
!_parent.TryGetConnector(connectorName, out connector))
|
||||||
{
|
{
|
||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
connector = _parent.GetConnector(connectorName)!;
|
SendResponse(HttpStatusCode.OK, response, connector!.GetPublications(title));
|
||||||
SendResponse(HttpStatusCode.OK, response, connector.GetPublications(title));
|
|
||||||
break;
|
break;
|
||||||
case "Manga/Chapters":
|
case "Manga/Chapters":
|
||||||
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
||||||
!requestVariables.TryGetValue("internalId", out string? internalId) ||
|
!requestVariables.TryGetValue("internalId", out internalId) ||
|
||||||
_parent.GetConnector(connectorName) is null ||
|
!_parent.TryGetConnector(connectorName, out connector) ||
|
||||||
_parent.GetPublicationById(internalId) is null)
|
!_parent.TryGetPublicationById(internalId, out manga))
|
||||||
{
|
{
|
||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
connector = _parent.GetConnector(connectorName)!;
|
SendResponse(HttpStatusCode.OK, response, connector!.GetChapters((Manga)manga!));
|
||||||
Manga manga = (Manga)_parent.GetPublicationById(internalId)!;
|
|
||||||
SendResponse(HttpStatusCode.OK, response, connector.GetChapters(manga));
|
|
||||||
break;
|
break;
|
||||||
case "Jobs":
|
case "Jobs":
|
||||||
if (!requestVariables.TryGetValue("jobId", out jobId))
|
if (!requestVariables.TryGetValue("jobId", out jobId))
|
||||||
{
|
{
|
||||||
if(!_parent._jobBoss.jobs.Any(jjob => jjob.id == jobId))
|
if(!_parent.jobBoss.jobs.Any(jjob => jjob.id == jobId))
|
||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
else
|
else
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs.First(jjob => jjob.id == jobId));
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.First(jjob => jjob.id == jobId));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs);
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs);
|
||||||
break;
|
break;
|
||||||
case "Jobs/Progress":
|
case "Jobs/Progress":
|
||||||
if (!requestVariables.TryGetValue("jobId", out jobId))
|
if (!requestVariables.TryGetValue("jobId", out jobId))
|
||||||
{
|
{
|
||||||
if(!_parent._jobBoss.jobs.Any(jjob => jjob.id == jobId))
|
if(!_parent.jobBoss.jobs.Any(jjob => jjob.id == jobId))
|
||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
else
|
else
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs.First(jjob => jjob.id == jobId).progressToken);
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.First(jjob => jjob.id == jobId).progressToken);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs.Select(jjob => jjob.progressToken));
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Select(jjob => jjob.progressToken));
|
||||||
break;
|
break;
|
||||||
case "Jobs/Running":
|
case "Jobs/Running":
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs.Where(jjob => jjob.progressToken.state is ProgressToken.State.Running));
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob.progressToken.state is ProgressToken.State.Running));
|
||||||
break;
|
break;
|
||||||
case "Jobs/Waiting":
|
case "Jobs/Waiting":
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs.Where(jjob => jjob.progressToken.state is ProgressToken.State.Standby));
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob.progressToken.state is ProgressToken.State.Standby));
|
||||||
break;
|
break;
|
||||||
case "Jobs/MonitorJobs":
|
case "Jobs/MonitorJobs":
|
||||||
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs.Where(jjob => jjob is DownloadNewChapters));
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob is DownloadNewChapters));
|
||||||
break;
|
break;
|
||||||
case "Settings":
|
case "Settings":
|
||||||
SendResponse(HttpStatusCode.OK, response, settings);
|
SendResponse(HttpStatusCode.OK, response, settings);
|
||||||
@ -213,7 +230,7 @@ public class Server : GlobalBase
|
|||||||
}
|
}
|
||||||
connector = _parent.GetConnector(connectorName)!;
|
connector = _parent.GetConnector(connectorName)!;
|
||||||
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
||||||
_parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, true, interval));
|
_parent.jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, true, interval));
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "Jobs/DownloadNewChapters":
|
case "Jobs/DownloadNewChapters":
|
||||||
@ -227,17 +244,17 @@ public class Server : GlobalBase
|
|||||||
}
|
}
|
||||||
connector = _parent.GetConnector(connectorName)!;
|
connector = _parent.GetConnector(connectorName)!;
|
||||||
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
||||||
_parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, false));
|
_parent.jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, false));
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "Jobs/StartNow":
|
case "Jobs/StartNow":
|
||||||
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
|
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
|
||||||
!_parent._jobBoss.TryGetJobById(jobId, out Job? job))
|
!_parent.jobBoss.TryGetJobById(jobId, out Job? job))
|
||||||
{
|
{
|
||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_parent._jobBoss.AddJobToQueue(job!);
|
_parent.jobBoss.AddJobToQueue(job!);
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "Settings/UpdateDownloadLocation":
|
case "Settings/UpdateDownloadLocation":
|
||||||
@ -346,12 +363,12 @@ public class Server : GlobalBase
|
|||||||
{
|
{
|
||||||
case "Jobs":
|
case "Jobs":
|
||||||
if (!requestVariables.TryGetValue("jobID", out string? jobId) ||
|
if (!requestVariables.TryGetValue("jobID", out string? jobId) ||
|
||||||
!_parent._jobBoss.TryGetJobById(jobId, out Job? job))
|
!_parent.jobBoss.TryGetJobById(jobId, out Job? job))
|
||||||
{
|
{
|
||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_parent._jobBoss.RemoveJob(job!);
|
_parent.jobBoss.RemoveJob(job!);
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "Jobs/DownloadChapter":
|
case "Jobs/DownloadChapter":
|
||||||
@ -364,7 +381,7 @@ public class Server : GlobalBase
|
|||||||
SendResponse(HttpStatusCode.BadRequest, response);
|
SendResponse(HttpStatusCode.BadRequest, response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connectorName, internalId, chapterNumber));
|
_parent.jobBoss.RemoveJobs(_parent.jobBoss.GetJobsLike(connectorName, internalId, chapterNumber));
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "Jobs/MonitorManga":
|
case "Jobs/MonitorManga":
|
||||||
@ -378,7 +395,7 @@ public class Server : GlobalBase
|
|||||||
}
|
}
|
||||||
connector = _parent.GetConnector(connectorName)!;
|
connector = _parent.GetConnector(connectorName)!;
|
||||||
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
||||||
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, manga));
|
_parent.jobBoss.RemoveJobs(_parent.jobBoss.GetJobsLike(connector, manga));
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "Jobs/DownloadNewChapters":
|
case "Jobs/DownloadNewChapters":
|
||||||
@ -392,7 +409,7 @@ public class Server : GlobalBase
|
|||||||
}
|
}
|
||||||
connector = _parent.GetConnector(connectorName)!;
|
connector = _parent.GetConnector(connectorName)!;
|
||||||
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
||||||
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, manga));
|
_parent.jobBoss.RemoveJobs(_parent.jobBoss.GetJobsLike(connector, manga));
|
||||||
SendResponse(HttpStatusCode.Accepted, response);
|
SendResponse(HttpStatusCode.Accepted, response);
|
||||||
break;
|
break;
|
||||||
case "NotificationConnectors":
|
case "NotificationConnectors":
|
||||||
@ -430,17 +447,27 @@ public class Server : GlobalBase
|
|||||||
response.AddHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
response.AddHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
||||||
response.AddHeader("Access-Control-Max-Age", "1728000");
|
response.AddHeader("Access-Control-Max-Age", "1728000");
|
||||||
response.AppendHeader("Access-Control-Allow-Origin", "*");
|
response.AppendHeader("Access-Control-Allow-Origin", "*");
|
||||||
response.ContentType = "application/json";
|
|
||||||
try
|
if (content is not FileStream stream)
|
||||||
{
|
{
|
||||||
response.OutputStream.Write(content is not null
|
response.ContentType = "application/json";
|
||||||
? Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(content))
|
try
|
||||||
: Array.Empty<byte>());
|
{
|
||||||
response.OutputStream.Close();
|
response.OutputStream.Write(content is not null
|
||||||
|
? Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(content))
|
||||||
|
: Array.Empty<byte>());
|
||||||
|
response.OutputStream.Close();
|
||||||
|
}
|
||||||
|
catch (HttpListenerException e)
|
||||||
|
{
|
||||||
|
Log(e.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (HttpListenerException e)
|
else
|
||||||
{
|
{
|
||||||
Log(e.ToString());
|
stream.CopyTo(response.OutputStream);
|
||||||
|
response.OutputStream.Close();
|
||||||
|
stream.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,36 +7,42 @@ namespace Tranga;
|
|||||||
public partial class Tranga : GlobalBase
|
public partial class Tranga : GlobalBase
|
||||||
{
|
{
|
||||||
public bool keepRunning;
|
public bool keepRunning;
|
||||||
public JobBoss _jobBoss;
|
public JobBoss jobBoss;
|
||||||
private Server server;
|
private Server _server;
|
||||||
private HashSet<MangaConnector> connectors;
|
private HashSet<MangaConnector> _connectors;
|
||||||
|
|
||||||
public Tranga(Logger? logger, TrangaSettings settings) : base(logger, settings)
|
public Tranga(Logger? logger, TrangaSettings settings) : base(logger, settings)
|
||||||
{
|
{
|
||||||
keepRunning = true;
|
keepRunning = true;
|
||||||
_jobBoss = new(this);
|
_connectors = new HashSet<MangaConnector>()
|
||||||
connectors = new HashSet<MangaConnector>()
|
|
||||||
{
|
{
|
||||||
new Manganato(this),
|
new Manganato(this),
|
||||||
new Mangasee(this),
|
new Mangasee(this),
|
||||||
new MangaDex(this),
|
new MangaDex(this),
|
||||||
new MangaKatana(this)
|
new MangaKatana(this)
|
||||||
};
|
};
|
||||||
|
jobBoss = new(this, this._connectors);
|
||||||
StartJobBoss();
|
StartJobBoss();
|
||||||
this.server = new Server(this);
|
this._server = new Server(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MangaConnector? GetConnector(string name)
|
public MangaConnector? GetConnector(string name)
|
||||||
{
|
{
|
||||||
foreach(MangaConnector mc in connectors)
|
foreach(MangaConnector mc in _connectors)
|
||||||
if (mc.name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
if (mc.name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
return mc;
|
return mc;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetConnector(string name, out MangaConnector? connector)
|
||||||
|
{
|
||||||
|
connector = GetConnector(name);
|
||||||
|
return connector is not null;
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<MangaConnector> GetConnectors()
|
public IEnumerable<MangaConnector> GetConnectors()
|
||||||
{
|
{
|
||||||
return connectors;
|
return _connectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Manga? GetPublicationById(string internalId)
|
public Manga? GetPublicationById(string internalId)
|
||||||
@ -46,13 +52,19 @@ public partial class Tranga : GlobalBase
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetPublicationById(string internalId, out Manga? manga)
|
||||||
|
{
|
||||||
|
manga = GetPublicationById(internalId);
|
||||||
|
return manga is not null;
|
||||||
|
}
|
||||||
|
|
||||||
private void StartJobBoss()
|
private void StartJobBoss()
|
||||||
{
|
{
|
||||||
Thread t = new (() =>
|
Thread t = new (() =>
|
||||||
{
|
{
|
||||||
while (keepRunning)
|
while (keepRunning)
|
||||||
{
|
{
|
||||||
_jobBoss.CheckJobs();
|
jobBoss.CheckJobs();
|
||||||
Thread.Sleep(1000);
|
Thread.Sleep(1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ public class TrangaSettings
|
|||||||
[JsonIgnore] public string settingsFilePath => Path.Join(workingDirectory, "settings.json");
|
[JsonIgnore] public string settingsFilePath => Path.Join(workingDirectory, "settings.json");
|
||||||
[JsonIgnore] public string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json");
|
[JsonIgnore] public string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json");
|
||||||
[JsonIgnore] public string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json");
|
[JsonIgnore] public string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json");
|
||||||
[JsonIgnore] public string tasksFilePath => Path.Join(workingDirectory, "tasks.json");
|
[JsonIgnore] public string jobsFilePath => Path.Join(workingDirectory, "jobs.json");
|
||||||
[JsonIgnore] public string coverImageCache => Path.Join(workingDirectory, "imageCache");
|
[JsonIgnore] public string coverImageCache => Path.Join(workingDirectory, "imageCache");
|
||||||
public ushort? version { get; set; }
|
public ushort? version { get; set; }
|
||||||
|
|
||||||
@ -127,4 +127,9 @@ public class TrangaSettings
|
|||||||
Directory.CreateDirectory(new FileInfo(settingsFilePath).DirectoryName!);
|
Directory.CreateDirectory(new FileInfo(settingsFilePath).DirectoryName!);
|
||||||
File.WriteAllText(settingsFilePath, JsonConvert.SerializeObject(this));
|
File.WriteAllText(settingsFilePath, JsonConvert.SerializeObject(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetFullCoverPath(Manga manga)
|
||||||
|
{
|
||||||
|
return Path.Join(this.coverImageCache, manga.coverFileNameInCache);
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,7 +4,6 @@ services:
|
|||||||
image: glax/tranga-api:latest
|
image: glax/tranga-api:latest
|
||||||
container_name: tranga-api
|
container_name: tranga-api
|
||||||
volumes:
|
volumes:
|
||||||
- ./tranga:/usr/share/Tranga-API #1 when replacing ./tranga replace #2 with same value
|
|
||||||
- ./Manga:/Manga
|
- ./Manga:/Manga
|
||||||
ports:
|
ports:
|
||||||
- "6531:6531"
|
- "6531:6531"
|
||||||
@ -12,8 +11,6 @@ services:
|
|||||||
tranga-website:
|
tranga-website:
|
||||||
image: glax/tranga-website:latest
|
image: glax/tranga-website:latest
|
||||||
container_name: tranga-website
|
container_name: tranga-website
|
||||||
volumes:
|
|
||||||
- ./tranga/imageCache:/usr/share/nginx/html/imageCache:ro #2 when replacing Point to same value as #1/imageCache
|
|
||||||
ports:
|
ports:
|
||||||
- "9555:80"
|
- "9555:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
Loading…
Reference in New Issue
Block a user