Compare commits

..

No commits in common. "9f30e52713e3931057f19d4234a547466b45574b" and "e274c864f9f523f451f615ea89fb82986535a4b9" have entirely different histories.

13 changed files with 135 additions and 180 deletions

View File

@ -10,7 +10,7 @@ namespace Tranga;
public readonly struct Chapter public readonly struct Chapter
{ {
// ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once MemberCanBePrivate.Global
public Manga parentManga { get; } public Publication parentPublication { get; }
public string? name { get; } public string? name { get; }
public string? volumeNumber { get; } public string? volumeNumber { get; }
public string chapterNumber { get; } public string chapterNumber { get; }
@ -20,9 +20,9 @@ public readonly struct Chapter
private static readonly Regex LegalCharacters = new (@"([A-z]*[0-9]* *\.*-*,*\]*\[*'*\'*\)*\(*~*!*)*"); private static readonly Regex LegalCharacters = new (@"([A-z]*[0-9]* *\.*-*,*\]*\[*'*\'*\)*\(*~*!*)*");
private static readonly Regex IllegalStrings = new(@"Vol(ume)?.?", RegexOptions.IgnoreCase); private static readonly Regex IllegalStrings = new(@"Vol(ume)?.?", RegexOptions.IgnoreCase);
public Chapter(Manga parentManga, string? name, string? volumeNumber, string chapterNumber, string url) public Chapter(Publication parentPublication, string? name, string? volumeNumber, string chapterNumber, string url)
{ {
this.parentManga = parentManga; this.parentPublication = parentPublication;
this.name = name; this.name = name;
this.volumeNumber = volumeNumber; this.volumeNumber = volumeNumber;
this.chapterNumber = chapterNumber; this.chapterNumber = chapterNumber;
@ -38,7 +38,7 @@ public readonly struct Chapter
public override string ToString() public override string ToString()
{ {
return $"Chapter {parentManga.sortName} {parentManga.internalId} {chapterNumber} {name}"; return $"Chapter {parentPublication.sortName} {parentPublication.internalId} {chapterNumber} {name}";
} }
/// <summary> /// <summary>
@ -48,9 +48,9 @@ public readonly struct Chapter
internal bool CheckChapterIsDownloaded(string downloadLocation) internal bool CheckChapterIsDownloaded(string downloadLocation)
{ {
string newFilePath = GetArchiveFilePath(downloadLocation); string newFilePath = GetArchiveFilePath(downloadLocation);
if (!Directory.Exists(Path.Join(downloadLocation, parentManga.folderName))) if (!Directory.Exists(Path.Join(downloadLocation, parentPublication.folderName)))
return false; return false;
FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentManga.folderName)).GetFiles(); FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentPublication.folderName)).GetFiles();
Regex chapterInfoRex = new(@"Ch\.[0-9.]+"); Regex chapterInfoRex = new(@"Ch\.[0-9.]+");
Regex chapterRex = new(@"[0-9]+(\.[0-9]+)?"); Regex chapterRex = new(@"[0-9]+(\.[0-9]+)?");
@ -71,7 +71,7 @@ public readonly struct Chapter
/// <returns>Filepath</returns> /// <returns>Filepath</returns>
internal string GetArchiveFilePath(string downloadLocation) internal string GetArchiveFilePath(string downloadLocation)
{ {
return Path.Join(downloadLocation, parentManga.folderName, $"{parentManga.folderName} - {this.fileName}.cbz"); return Path.Join(downloadLocation, parentPublication.folderName, $"{parentPublication.folderName} - {this.fileName}.cbz");
} }
/// <summary> /// <summary>
@ -82,10 +82,10 @@ public readonly struct Chapter
internal string GetComicInfoXmlString() internal string GetComicInfoXmlString()
{ {
XElement comicInfo = new XElement("ComicInfo", XElement comicInfo = new XElement("ComicInfo",
new XElement("Tags", string.Join(',', parentManga.tags)), new XElement("Tags", string.Join(',', parentPublication.tags)),
new XElement("LanguageISO", parentManga.originalLanguage), new XElement("LanguageISO", parentPublication.originalLanguage),
new XElement("Title", this.name), new XElement("Title", this.name),
new XElement("Writer", string.Join(',', parentManga.authors)), new XElement("Writer", string.Join(',', parentPublication.authors)),
new XElement("Volume", this.volumeNumber), new XElement("Volume", this.volumeNumber),
new XElement("Number", this.chapterNumber) new XElement("Number", this.chapterNumber)
); );

View File

@ -11,7 +11,7 @@ public abstract class GlobalBase
protected TrangaSettings settings { get; init; } protected TrangaSettings settings { get; init; }
protected HashSet<NotificationConnector> notificationConnectors { get; init; } protected HashSet<NotificationConnector> notificationConnectors { get; init; }
protected HashSet<LibraryConnector> libraryConnectors { get; init; } protected HashSet<LibraryConnector> libraryConnectors { get; init; }
protected List<Manga> cachedPublications { get; init; } protected List<Publication> cachedPublications { get; init; }
protected GlobalBase(GlobalBase clone) protected GlobalBase(GlobalBase clone)
{ {

View File

@ -14,7 +14,7 @@ public class DownloadChapter : Job
protected override string GetId() protected override string GetId()
{ {
return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), chapter.parentManga.internalId, chapter.chapterNumber))); return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), chapter.parentPublication.internalId, chapter.chapterNumber)));
} }
public override string ToString() public override string ToString()
@ -28,7 +28,7 @@ public class DownloadChapter : Job
{ {
mangaConnector.DownloadChapter(chapter, this.progressToken); mangaConnector.DownloadChapter(chapter, this.progressToken);
UpdateLibraries(); UpdateLibraries();
SendNotifications("Chapter downloaded", $"{chapter.parentManga.sortName} - {chapter.chapterNumber}"); SendNotifications("Chapter downloaded", $"{chapter.parentPublication.sortName} - {chapter.chapterNumber}");
}); });
downloadTask.Start(); downloadTask.Start();
return Array.Empty<Job>(); return Array.Empty<Job>();

View File

@ -5,26 +5,26 @@ namespace Tranga.Jobs;
public class DownloadNewChapters : Job public class DownloadNewChapters : Job
{ {
public Manga manga { get; init; } public Publication publication { get; init; }
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, bool recurring = false, TimeSpan? recurrence = null) : base (clone, connector, recurring, recurrence) public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Publication publication, bool recurring = false, TimeSpan? recurrence = null) : base (clone, connector, recurring, recurrence)
{ {
this.manga = manga; this.publication = publication;
} }
protected override string GetId() protected override string GetId()
{ {
return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), manga.internalId))); return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), publication.internalId)));
} }
public override string ToString() public override string ToString()
{ {
return $"DownloadChapter {id} {manga}"; return $"DownloadChapter {id} {publication}";
} }
protected override IEnumerable<Job> ExecuteReturnSubTasksInternal() protected override IEnumerable<Job> ExecuteReturnSubTasksInternal()
{ {
Chapter[] chapters = mangaConnector.GetNewChapters(manga); Chapter[] chapters = mangaConnector.GetNewChapters(publication);
this.progressToken.increments = chapters.Length; this.progressToken.increments = chapters.Length;
List<Job> subJobs = new(); List<Job> subJobs = new();
foreach (Chapter chapter in chapters) foreach (Chapter chapter in chapters)

View File

@ -44,7 +44,7 @@ public class JobBoss : GlobalBase
{ {
if (jjob is not DownloadChapter job) if (jjob is not DownloadChapter job)
return false; return false;
return job.chapter.parentManga.internalId == internalId && return job.chapter.parentPublication.internalId == internalId &&
job.chapter.chapterNumber == chapterNumber; job.chapter.chapterNumber == chapterNumber;
}); });
else if (internalId is not null) else if (internalId is not null)
@ -52,43 +52,24 @@ public class JobBoss : GlobalBase
{ {
if (jjob is not DownloadNewChapters job) if (jjob is not DownloadNewChapters job)
return false; return false;
return job.manga.internalId == internalId; return job.publication.internalId == internalId;
}); });
return ret; return ret;
} }
public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Manga? publication = null, public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Publication? publication = null,
Chapter? chapter = null) Chapter? chapter = null)
{ {
return GetJobsLike(mangaConnector?.name, publication?.internalId, chapter?.chapterNumber); return GetJobsLike(mangaConnector?.name, publication?.internalId, chapter?.chapterNumber);
} }
public Job? GetJobById(string jobId)
{
if (this.jobs.FirstOrDefault(jjob => jjob.id == jobId) is { } job)
return job;
return null;
}
public bool TryGetJobById(string jobId, out Job? job)
{
if (this.jobs.FirstOrDefault(jjob => jjob.id == jobId) is { } ret)
{
job = ret;
return true;
}
job = null;
return false;
}
private bool QueueContainsJob(Job job) private bool QueueContainsJob(Job job)
{ {
mangaConnectorJobQueue.TryAdd(job.mangaConnector, new Queue<Job>()); mangaConnectorJobQueue.TryAdd(job.mangaConnector, new Queue<Job>());
return mangaConnectorJobQueue[job.mangaConnector].Contains(job); return mangaConnectorJobQueue[job.mangaConnector].Contains(job);
} }
public void AddJobToQueue(Job job) private void AddJobToQueue(Job job)
{ {
Log($"Adding Job to Queue. {job}"); Log($"Adding Job to Queue. {job}");
mangaConnectorJobQueue.TryAdd(job.mangaConnector, new Queue<Job>()); mangaConnectorJobQueue.TryAdd(job.mangaConnector, new Queue<Job>());

View File

@ -29,40 +29,40 @@ public abstract class MangaConnector : GlobalBase
/// </summary> /// </summary>
/// <param name="publicationTitle">Search-Query</param> /// <param name="publicationTitle">Search-Query</param>
/// <returns>Publications matching the query</returns> /// <returns>Publications matching the query</returns>
public abstract Manga[] GetPublications(string publicationTitle = ""); public abstract Publication[] GetPublications(string publicationTitle = "");
/// <summary> /// <summary>
/// Returns all Chapters of the publication in the provided language. /// Returns all Chapters of the publication in the provided language.
/// If the language is empty or null, returns all Chapters in all Languages. /// If the language is empty or null, returns all Chapters in all Languages.
/// </summary> /// </summary>
/// <param name="manga">Publication to get Chapters for</param> /// <param name="publication">Publication to get Chapters for</param>
/// <param name="language">Language of the Chapters</param> /// <param name="language">Language of the Chapters</param>
/// <returns>Array of Chapters matching Publication and Language</returns> /// <returns>Array of Chapters matching Publication and Language</returns>
public abstract Chapter[] GetChapters(Manga manga, string language="en"); public abstract Chapter[] GetChapters(Publication publication, string language="en");
/// <summary> /// <summary>
/// Updates the available Chapters of a Publication /// Updates the available Chapters of a Publication
/// </summary> /// </summary>
/// <param name="manga">Publication to check</param> /// <param name="publication">Publication to check</param>
/// <param name="language">Language to receive chapters for</param> /// <param name="language">Language to receive chapters for</param>
/// <returns>List of Chapters that were previously not in collection</returns> /// <returns>List of Chapters that were previously not in collection</returns>
public Chapter[] GetNewChapters(Manga manga, string language = "en") public Chapter[] GetNewChapters(Publication publication, string language = "en")
{ {
Log($"Getting new Chapters for {manga}"); Log($"Getting new Chapters for {publication}");
Chapter[] newChapters = this.GetChapters(manga, language); Chapter[] newChapters = this.GetChapters(publication, language);
NumberFormatInfo decimalPoint = new (){ NumberDecimalSeparator = "." }; NumberFormatInfo decimalPoint = new (){ NumberDecimalSeparator = "." };
Log($"Checking for duplicates {manga}"); Log($"Checking for duplicates {publication}");
List<Chapter> newChaptersList = newChapters.Where(nChapter => List<Chapter> newChaptersList = newChapters.Where(nChapter =>
float.Parse(nChapter.chapterNumber, decimalPoint) > manga.ignoreChaptersBelow && float.Parse(nChapter.chapterNumber, decimalPoint) > publication.ignoreChaptersBelow &&
!nChapter.CheckChapterIsDownloaded(settings.downloadLocation)).ToList(); !nChapter.CheckChapterIsDownloaded(settings.downloadLocation)).ToList();
Log($"{newChaptersList.Count} new chapters. {manga}"); Log($"{newChaptersList.Count} new chapters. {publication}");
return newChaptersList.ToArray(); return newChaptersList.ToArray();
} }
public Chapter[] SelectChapters(Manga manga, string searchTerm, string? language = null) public Chapter[] SelectChapters(Publication publication, string searchTerm, string? language = null)
{ {
Chapter[] availableChapters = this.GetChapters(manga, language??"en"); Chapter[] availableChapters = this.GetChapters(publication, language??"en");
Regex volumeRegex = new ("((v(ol)*(olume)*){1} *([0-9]+(-[0-9]+)?){1})", RegexOptions.IgnoreCase); Regex volumeRegex = new ("((v(ol)*(olume)*){1} *([0-9]+(-[0-9]+)?){1})", RegexOptions.IgnoreCase);
Regex chapterRegex = new ("((c(h)*(hapter)*){1} *([0-9]+(-[0-9]+)?){1})", RegexOptions.IgnoreCase); Regex chapterRegex = new ("((c(h)*(hapter)*){1} *([0-9]+(-[0-9]+)?){1})", RegexOptions.IgnoreCase);
Regex singleResultRegex = new("([0-9]+)", RegexOptions.IgnoreCase); Regex singleResultRegex = new("([0-9]+)", RegexOptions.IgnoreCase);
@ -138,20 +138,20 @@ public abstract class MangaConnector : GlobalBase
/// <summary> /// <summary>
/// Copies the already downloaded cover from cache to downloadLocation /// Copies the already downloaded cover from cache to downloadLocation
/// </summary> /// </summary>
/// <param name="manga">Publication to retrieve Cover for</param> /// <param name="publication">Publication to retrieve Cover for</param>
public void CopyCoverFromCacheToDownloadLocation(Manga manga) public void CopyCoverFromCacheToDownloadLocation(Publication publication)
{ {
Log($"Copy cover {manga}"); Log($"Copy cover {publication}");
//Check if Publication already has a Folder and cover //Check if Publication already has a Folder and cover
string publicationFolder = manga.CreatePublicationFolder(settings.downloadLocation); string publicationFolder = publication.CreatePublicationFolder(settings.downloadLocation);
DirectoryInfo dirInfo = new (publicationFolder); DirectoryInfo dirInfo = new (publicationFolder);
if (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover", StringComparison.InvariantCultureIgnoreCase))) if (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover", StringComparison.InvariantCultureIgnoreCase)))
{ {
Log($"Cover exists {manga}"); Log($"Cover exists {publication}");
return; return;
} }
string fileInCache = Path.Join(settings.coverImageCache, manga.coverFileNameInCache); string fileInCache = Path.Join(settings.coverImageCache, publication.coverFileNameInCache);
string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" ); string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" );
Log($"Cloning cover {fileInCache} -> {newFilePath}"); Log($"Cloning cover {fileInCache} -> {newFilePath}");
File.Copy(fileInCache, newFilePath, true); File.Copy(fileInCache, newFilePath, true);

View File

@ -31,13 +31,13 @@ public class MangaDex : MangaConnector
}); });
} }
public override Manga[] GetPublications(string publicationTitle = "") public override Publication[] GetPublications(string publicationTitle = "")
{ {
Log($"Searching Publications. Term=\"{publicationTitle}\""); Log($"Searching Publications. Term=\"{publicationTitle}\"");
const int limit = 100; //How many values we want returned at once const int limit = 100; //How many values we want returned at once
int offset = 0; //"Page" int offset = 0; //"Page"
int total = int.MaxValue; //How many total results are there, is updated on first request int total = int.MaxValue; //How many total results are there, is updated on first request
HashSet<Manga> publications = new(); HashSet<Publication> publications = new();
int loadedPublicationData = 0; int loadedPublicationData = 0;
while (offset < total) //As long as we haven't requested all "Pages" while (offset < total) //As long as we haven't requested all "Pages"
{ {
@ -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);
@ -125,13 +128,14 @@ public class MangaDex : MangaConnector
string status = attributes["status"]!.GetValue<string>(); string status = attributes["status"]!.GetValue<string>();
Manga pub = new ( Publication pub = new (
title, title,
authors, authors,
description, description,
altTitlesDict, altTitlesDict,
tags.ToArray(), tags.ToArray(),
coverUrl, coverUrl,
coverCacheName,
linksDict, linksDict,
year, year,
originalLanguage, originalLanguage,
@ -147,9 +151,9 @@ public class MangaDex : MangaConnector
return publications.ToArray(); return publications.ToArray();
} }
public override Chapter[] GetChapters(Manga manga, string language="en") public override Chapter[] GetChapters(Publication publication, string language="en")
{ {
Log($"Getting chapters {manga}"); Log($"Getting chapters {publication}");
const int limit = 100; //How many values we want returned at once const int limit = 100; //How many values we want returned at once
int offset = 0; //"Page" int offset = 0; //"Page"
int total = int.MaxValue; //How many total results are there, is updated on first request int total = int.MaxValue; //How many total results are there, is updated on first request
@ -160,7 +164,7 @@ public class MangaDex : MangaConnector
//Request next "Page" //Request next "Page"
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest( downloadClient.MakeRequest(
$"https://api.mangadex.org/manga/{manga.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", (byte)RequestType.Feed); $"https://api.mangadex.org/manga/{publication.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", (byte)RequestType.Feed);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
break; break;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result); JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -191,13 +195,13 @@ public class MangaDex : MangaConnector
: "null"; : "null";
if(chapterNum is not "null") if(chapterNum is not "null")
chapters.Add(new Chapter(manga, title, volume, chapterNum, chapterId)); chapters.Add(new Chapter(publication, title, volume, chapterNum, chapterId));
} }
} }
//Return Chapters ordered by Chapter-Number //Return Chapters ordered by Chapter-Number
NumberFormatInfo chapterNumberFormatInfo = new() { NumberDecimalSeparator = "." }; NumberFormatInfo chapterNumberFormatInfo = new() { NumberDecimalSeparator = "." };
Log($"Got {chapters.Count} chapters. {manga}"); Log($"Got {chapters.Count} chapters. {publication}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
@ -205,8 +209,7 @@ public class MangaDex : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Manga chapterParentManga = chapter.parentManga; Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}");
Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
//Request URLs for Chapter-Images //Request URLs for Chapter-Images
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer); downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer);
@ -227,8 +230,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);
} }

View File

@ -19,7 +19,7 @@ public class MangaKatana : MangaConnector
}); });
} }
public override Manga[] GetPublications(string publicationTitle = "") public override Publication[] GetPublications(string publicationTitle = "")
{ {
Log($"Searching Publications. Term=\"{publicationTitle}\""); Log($"Searching Publications. Term=\"{publicationTitle}\"");
string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower();
@ -27,7 +27,7 @@ public class MangaKatana : MangaConnector
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Publication>();
// ReSharper disable once MergeIntoPattern // ReSharper disable once MergeIntoPattern
// If a single result is found, the user will be redirected to the results directly instead of a result page // If a single result is found, the user will be redirected to the results directly instead of a result page
@ -38,13 +38,13 @@ public class MangaKatana : MangaConnector
return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1]) }; return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1]) };
} }
Manga[] publications = ParsePublicationsFromHtml(requestResult.result); Publication[] publications = ParsePublicationsFromHtml(requestResult.result);
cachedPublications.AddRange(publications); cachedPublications.AddRange(publications);
Log($"Retrieved {publications.Length} publications. Term=\"{publicationTitle}\""); Log($"Retrieved {publications.Length} publications. Term=\"{publicationTitle}\"");
return publications; return publications;
} }
private Manga[] ParsePublicationsFromHtml(Stream html) private Publication[] ParsePublicationsFromHtml(Stream html)
{ {
StreamReader reader = new(html); StreamReader reader = new(html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -52,7 +52,7 @@ public class MangaKatana : MangaConnector
document.LoadHtml(htmlString); document.LoadHtml(htmlString);
IEnumerable<HtmlNode> searchResults = document.DocumentNode.SelectNodes("//*[@id='book_list']/div"); IEnumerable<HtmlNode> searchResults = document.DocumentNode.SelectNodes("//*[@id='book_list']/div");
if (searchResults is null || !searchResults.Any()) if (searchResults is null || !searchResults.Any())
return Array.Empty<Manga>(); return Array.Empty<Publication>();
List<string> urls = new(); List<string> urls = new();
foreach (HtmlNode mangaResult in searchResults) foreach (HtmlNode mangaResult in searchResults)
{ {
@ -60,13 +60,13 @@ public class MangaKatana : MangaConnector
.First(a => a.Name == "href").Value); .First(a => a.Name == "href").Value);
} }
HashSet<Manga> ret = new(); HashSet<Publication> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(url, 1); downloadClient.MakeRequest(url, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Publication>();
ret.Add(ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1])); ret.Add(ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1]));
} }
@ -74,7 +74,7 @@ public class MangaKatana : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId) private Publication ParseSinglePublicationFromHtml(Stream html, string publicationId)
{ {
StreamReader reader = new(html); StreamReader reader = new(html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -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,14 +134,14 @@ 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 Publication(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
year, originalLanguage, status, publicationId); year, originalLanguage, status, publicationId);
} }
public override Chapter[] GetChapters(Manga manga, string language="en") public override Chapter[] GetChapters(Publication publication, string language="en")
{ {
Log($"Getting chapters {manga}"); Log($"Getting chapters {publication}");
string requestUrl = $"https://mangakatana.com/manga/{manga.publicationId}"; string requestUrl = $"https://mangakatana.com/manga/{publication.publicationId}";
// Leaving this in for verification if the page exists // Leaving this in for verification if the page exists
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, 1);
@ -151,12 +153,12 @@ public class MangaKatana : MangaConnector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
List<Chapter> chapters = ParseChaptersFromHtml(manga, requestUrl); List<Chapter> chapters = ParseChaptersFromHtml(publication, requestUrl);
Log($"Got {chapters.Count} chapters. {manga}"); Log($"Got {chapters.Count} chapters. {publication}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
private List<Chapter> ParseChaptersFromHtml(Manga manga, string mangaUrl) private List<Chapter> ParseChaptersFromHtml(Publication publication, string mangaUrl)
{ {
// Using HtmlWeb will include the chapters since they are loaded with js // Using HtmlWeb will include the chapters since they are loaded with js
HtmlWeb web = new(); HtmlWeb web = new();
@ -175,7 +177,7 @@ public class MangaKatana : MangaConnector
string chapterName = string.Concat(fullString.Split(':')[1..]); string chapterName = string.Concat(fullString.Split(':')[1..]);
string url = chapterInfo.Descendants("a").First() string url = chapterInfo.Descendants("a").First()
.GetAttributeValue("href", ""); .GetAttributeValue("href", "");
ret.Add(new Chapter(manga, chapterName, volumeNumber, chapterNumber, url)); ret.Add(new Chapter(publication, chapterName, volumeNumber, chapterNumber, url));
} }
return ret; return ret;
@ -185,8 +187,7 @@ public class MangaKatana : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Manga chapterParentManga = chapter.parentManga; Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}");
Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
string requestUrl = chapter.url; string requestUrl = chapter.url;
// Leaving this in to check if the page exists // Leaving this in to check if the page exists
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
@ -199,9 +200,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);
} }

View File

@ -19,7 +19,7 @@ public class Manganato : MangaConnector
}); });
} }
public override Manga[] GetPublications(string publicationTitle = "") public override Publication[] GetPublications(string publicationTitle = "")
{ {
Log($"Searching Publications. Term=\"{publicationTitle}\""); Log($"Searching Publications. Term=\"{publicationTitle}\"");
string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*")).ToLower(); string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*")).ToLower();
@ -27,15 +27,15 @@ public class Manganato : MangaConnector
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Publication>();
Manga[] publications = ParsePublicationsFromHtml(requestResult.result); Publication[] publications = ParsePublicationsFromHtml(requestResult.result);
cachedPublications.AddRange(publications); cachedPublications.AddRange(publications);
Log($"Retrieved {publications.Length} publications. Term=\"{publicationTitle}\""); Log($"Retrieved {publications.Length} publications. Term=\"{publicationTitle}\"");
return publications; return publications;
} }
private Manga[] ParsePublicationsFromHtml(Stream html) private Publication[] ParsePublicationsFromHtml(Stream html)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -49,13 +49,13 @@ public class Manganato : MangaConnector
.First(a => a.Name == "href").Value); .First(a => a.Name == "href").Value);
} }
HashSet<Manga> ret = new(); HashSet<Publication> ret = new();
foreach (string url in urls) foreach (string url in urls)
{ {
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(url, 1); downloadClient.MakeRequest(url, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Publication>();
ret.Add(ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1])); ret.Add(ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1]));
} }
@ -63,7 +63,7 @@ public class Manganato : MangaConnector
return ret.ToArray(); return ret.ToArray();
} }
private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId) private Publication ParseSinglePublicationFromHtml(Stream html, string publicationId)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -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,14 +122,14 @@ 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 Publication(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
year, originalLanguage, status, publicationId); year, originalLanguage, status, publicationId);
} }
public override Chapter[] GetChapters(Manga manga, string language="en") public override Chapter[] GetChapters(Publication publication, string language="en")
{ {
Log($"Getting chapters {manga}"); Log($"Getting chapters {publication}");
string requestUrl = $"https://chapmanganato.com/{manga.publicationId}"; string requestUrl = $"https://chapmanganato.com/{publication.publicationId}";
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
@ -138,12 +140,12 @@ public class Manganato : MangaConnector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
List<Chapter> chapters = ParseChaptersFromHtml(manga, requestResult.result); List<Chapter> chapters = ParseChaptersFromHtml(publication, requestResult.result);
Log($"Got {chapters.Count} chapters. {manga}"); Log($"Got {chapters.Count} chapters. {publication}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
private List<Chapter> ParseChaptersFromHtml(Manga manga, Stream html) private List<Chapter> ParseChaptersFromHtml(Publication publication, Stream html)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -162,7 +164,7 @@ public class Manganato : MangaConnector
string chapterName = string.Concat(fullString.Split(':')[1..]); string chapterName = string.Concat(fullString.Split(':')[1..]);
string url = chapterInfo.Descendants("a").First(d => d.HasClass("chapter-name")) string url = chapterInfo.Descendants("a").First(d => d.HasClass("chapter-name"))
.GetAttributeValue("href", ""); .GetAttributeValue("href", "");
ret.Add(new Chapter(manga, chapterName, volumeNumber, chapterNumber, url)); ret.Add(new Chapter(publication, chapterName, volumeNumber, chapterNumber, url));
} }
ret.Reverse(); ret.Reverse();
return ret; return ret;
@ -172,8 +174,7 @@ public class Manganato : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Manga chapterParentManga = chapter.parentManga; Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}");
Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
string requestUrl = chapter.url; string requestUrl = chapter.url;
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, 1);
@ -185,9 +186,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);
} }

View File

@ -69,22 +69,22 @@ public class Mangasee : MangaConnector
}); });
} }
public override Manga[] GetPublications(string publicationTitle = "") public override Publication[] GetPublications(string publicationTitle = "")
{ {
Log($"Searching Publications. Term=\"{publicationTitle}\""); Log($"Searching Publications. Term=\"{publicationTitle}\"");
string requestUrl = $"https://mangasee123.com/_search.php"; string requestUrl = $"https://mangasee123.com/_search.php";
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Publication>();
Manga[] publications = ParsePublicationsFromHtml(requestResult.result, publicationTitle); Publication[] publications = ParsePublicationsFromHtml(requestResult.result, publicationTitle);
cachedPublications.AddRange(publications); cachedPublications.AddRange(publications);
Log($"Retrieved {publications.Length} publications. Term=\"{publicationTitle}\""); Log($"Retrieved {publications.Length} publications. Term=\"{publicationTitle}\"");
return publications; return publications;
} }
private Manga[] ParsePublicationsFromHtml(Stream html, string publicationTitle) private Publication[] ParsePublicationsFromHtml(Stream html, string publicationTitle)
{ {
string jsonString = new StreamReader(html).ReadToEnd(); string jsonString = new StreamReader(html).ReadToEnd();
List<SearchResultItem> result = JsonConvert.DeserializeObject<List<SearchResultItem>>(jsonString)!; List<SearchResultItem> result = JsonConvert.DeserializeObject<List<SearchResultItem>>(jsonString)!;
@ -101,7 +101,7 @@ public class Mangasee : MangaConnector
Log($"Retrieved {queryFiltered.Count} publications."); Log($"Retrieved {queryFiltered.Count} publications.");
HashSet<Manga> ret = new(); HashSet<Publication> ret = new();
List<SearchResultItem> orderedFiltered = List<SearchResultItem> orderedFiltered =
queryFiltered.OrderBy(item => item.Value).ToDictionary(item => item.Key, item => item.Value).Keys.ToList(); queryFiltered.OrderBy(item => item.Value).ToDictionary(item => item.Key, item => item.Value).Keys.ToList();
@ -120,7 +120,7 @@ public class Mangasee : MangaConnector
} }
private Manga ParseSinglePublicationFromHtml(Stream html, string sortName, string publicationId, string[] a) private Publication ParseSinglePublicationFromHtml(Stream html, string sortName, string publicationId, string[] a)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
HtmlDocument document = new (); HtmlDocument document = new ();
@ -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 Publication(sortName, authors, description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links,
year, originalLanguage, status, publicationId); year, originalLanguage, status, publicationId);
} }
@ -214,10 +215,10 @@ public class Mangasee : MangaConnector
} }
} }
public override Chapter[] GetChapters(Manga manga, string language="en") public override Chapter[] GetChapters(Publication publication, string language="en")
{ {
Log($"Getting chapters {manga}"); Log($"Getting chapters {publication}");
XDocument doc = XDocument.Load($"https://mangasee123.com/rss/{manga.publicationId}.xml"); XDocument doc = XDocument.Load($"https://mangasee123.com/rss/{publication.publicationId}.xml");
XElement[] chapterItems = doc.Descendants("item").ToArray(); XElement[] chapterItems = doc.Descendants("item").ToArray();
List<Chapter> chapters = new(); List<Chapter> chapters = new();
foreach (XElement chapter in chapterItems) foreach (XElement chapter in chapterItems)
@ -228,7 +229,7 @@ public class Mangasee : MangaConnector
string url = chapter.Descendants("link").First().Value; string url = chapter.Descendants("link").First().Value;
url = url.Replace(Regex.Matches(url,"(-page-[0-9])")[0].ToString(),""); url = url.Replace(Regex.Matches(url,"(-page-[0-9])")[0].ToString(),"");
chapters.Add(new Chapter(manga, "", volumeNumber, chapterNumber, url)); chapters.Add(new Chapter(publication, "", volumeNumber, chapterNumber, url));
} }
//Return Chapters ordered by Chapter-Number //Return Chapters ordered by Chapter-Number
@ -236,7 +237,7 @@ public class Mangasee : MangaConnector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
Log($"Got {chapters.Count} chapters. {manga}"); Log($"Got {chapters.Count} chapters. {publication}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
@ -244,7 +245,6 @@ public class Mangasee : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Manga chapterParentManga = chapter.parentManga;
while (this._browser is null && !(progressToken?.cancellationRequested??false)) while (this._browser is null && !(progressToken?.cancellationRequested??false))
{ {
Log("Waiting for headless browser to download..."); Log("Waiting for headless browser to download...");
@ -253,7 +253,7 @@ public class Mangasee : MangaConnector
if (progressToken?.cancellationRequested??false) if (progressToken?.cancellationRequested??false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}");
IPage page = _browser!.NewPageAsync().Result; IPage page = _browser!.NewPageAsync().Result;
IResponse response = page.GoToAsync(chapter.url).Result; IResponse response = page.GoToAsync(chapter.url).Result;
if (response.Ok) if (response.Ok)
@ -270,9 +270,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;

View File

@ -9,7 +9,7 @@ namespace Tranga;
/// <summary> /// <summary>
/// Contains information on a Publication (Manga) /// Contains information on a Publication (Manga)
/// </summary> /// </summary>
public struct Manga public struct Publication
{ {
public string sortName { get; } public string sortName { get; }
public List<string> authors { get; } public List<string> authors { get; }
@ -19,8 +19,8 @@ public struct Manga
public string? description { get; } public string? description { get; }
public string[] tags { get; } public string[] tags { get; }
// ReSharper disable once UnusedAutoPropertyAccessor.Global // ReSharper disable once UnusedAutoPropertyAccessor.Global
public string? coverUrl { get; } public string? posterUrl { get; }
public string? coverFileNameInCache { get; set; } public string? coverFileNameInCache { get; }
// ReSharper disable once UnusedAutoPropertyAccessor.Global // ReSharper disable once UnusedAutoPropertyAccessor.Global
public Dictionary<string,string> links { get; } public Dictionary<string,string> links { get; }
// ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once MemberCanBePrivate.Global
@ -36,15 +36,15 @@ 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 Publication(string sortName, List<string> authors, string? description, Dictionary<string,string> altTitles, string[] tags, string? posterUrl, 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.posterUrl = posterUrl;
this.links = links ?? new Dictionary<string, string>(); this.links = links ?? new Dictionary<string, string>();
this.year = year; this.year = year;
this.originalLanguage = originalLanguage; this.originalLanguage = originalLanguage;

View File

@ -113,7 +113,7 @@ public class Server : GlobalBase
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/FromConnector": case "Publications/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.GetConnector(connectorName) is null)
@ -124,7 +124,7 @@ public class Server : GlobalBase
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
SendResponse(HttpStatusCode.OK, response, connector.GetPublications(title)); SendResponse(HttpStatusCode.OK, response, connector.GetPublications(title));
break; break;
case "Manga/Chapters": case "Publications/Chapters":
if(!requestVariables.TryGetValue("connector", out connectorName) || if(!requestVariables.TryGetValue("connector", out connectorName) ||
!requestVariables.TryGetValue("internalId", out string? internalId) || !requestVariables.TryGetValue("internalId", out string? internalId) ||
_parent.GetConnector(connectorName) is null || _parent.GetConnector(connectorName) is null ||
@ -134,10 +134,10 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
Manga manga = (Manga)_parent.GetPublicationById(internalId)!; Publication publication = (Publication)_parent.GetPublicationById(internalId)!;
SendResponse(HttpStatusCode.OK, response, connector.GetChapters(manga)); SendResponse(HttpStatusCode.OK, response, connector.GetChapters(publication));
break; break;
case "Jobs": case "Tasks":
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))
@ -148,7 +148,7 @@ public class Server : GlobalBase
} }
SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs); SendResponse(HttpStatusCode.OK, response, _parent._jobBoss.jobs);
break; break;
case "Jobs/Progress": case "Tasks/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))
@ -159,10 +159,10 @@ public class Server : GlobalBase
} }
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 "Tasks/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 "Tasks/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 "Settings": case "Settings":
@ -191,11 +191,11 @@ public class Server : GlobalBase
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query); Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
string? connectorName, internalId; string? connectorName, internalId;
MangaConnector connector; MangaConnector connector;
Manga manga; Publication publication;
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 "Jobs/MonitorManga": case "Tasks/MonitorManga":
if(!requestVariables.TryGetValue("connector", out connectorName) || if(!requestVariables.TryGetValue("connector", out connectorName) ||
!requestVariables.TryGetValue("internalId", out internalId) || !requestVariables.TryGetValue("internalId", out internalId) ||
!requestVariables.TryGetValue("interval", out string? intervalStr) || !requestVariables.TryGetValue("interval", out string? intervalStr) ||
@ -207,11 +207,11 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
manga = (Manga)_parent.GetPublicationById(internalId)!; publication = (Publication)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, true, interval)); _parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, publication, true, interval));
SendResponse(HttpStatusCode.Accepted, response); SendResponse(HttpStatusCode.Accepted, response);
break; break;
case "Jobs/DownloadNewChapters": case "Tasks/DownloadNewChapters":
if(!requestVariables.TryGetValue("connector", out connectorName) || if(!requestVariables.TryGetValue("connector", out connectorName) ||
!requestVariables.TryGetValue("internalId", out internalId) || !requestVariables.TryGetValue("internalId", out internalId) ||
_parent.GetConnector(connectorName) is null || _parent.GetConnector(connectorName) is null ||
@ -221,18 +221,8 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
manga = (Manga)_parent.GetPublicationById(internalId)!; publication = (Publication)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, false)); _parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, publication, false));
SendResponse(HttpStatusCode.Accepted, response);
break;
case "Jobs/StartNow":
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
!_parent._jobBoss.TryGetJobById(jobId, out Job? job))
{
SendResponse(HttpStatusCode.BadRequest, response);
break;
}
_parent._jobBoss.AddJobToQueue(job!);
SendResponse(HttpStatusCode.Accepted, response); SendResponse(HttpStatusCode.Accepted, response);
break; break;
case "Settings/UpdateDownloadLocation": case "Settings/UpdateDownloadLocation":
@ -335,21 +325,11 @@ public class Server : GlobalBase
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query); Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
string? connectorName, internalId; string? connectorName, internalId;
MangaConnector connector; MangaConnector connector;
Manga manga; Publication publication;
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 "Jobs": case "Tasks/DownloadChapter":
if (!requestVariables.TryGetValue("jobID", out string? jobId) ||
!_parent._jobBoss.TryGetJobById(jobId, out Job? job))
{
SendResponse(HttpStatusCode.BadRequest, response);
break;
}
_parent._jobBoss.RemoveJob(job!);
SendResponse(HttpStatusCode.Accepted, response);
break;
case "Jobs/DownloadChapter":
if(!requestVariables.TryGetValue("connector", out connectorName) || if(!requestVariables.TryGetValue("connector", out connectorName) ||
!requestVariables.TryGetValue("internalId", out internalId) || !requestVariables.TryGetValue("internalId", out internalId) ||
!requestVariables.TryGetValue("chapterNumber", out string? chapterNumber) || !requestVariables.TryGetValue("chapterNumber", out string? chapterNumber) ||
@ -362,7 +342,7 @@ public class Server : GlobalBase
_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 "Tasks/MonitorManga":
if(!requestVariables.TryGetValue("connector", out connectorName) || if(!requestVariables.TryGetValue("connector", out connectorName) ||
!requestVariables.TryGetValue("internalId", out internalId) || !requestVariables.TryGetValue("internalId", out internalId) ||
_parent.GetConnector(connectorName) is null || _parent.GetConnector(connectorName) is null ||
@ -372,11 +352,11 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
manga = (Manga)_parent.GetPublicationById(internalId)!; publication = (Publication)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, manga)); _parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, publication));
SendResponse(HttpStatusCode.Accepted, response); SendResponse(HttpStatusCode.Accepted, response);
break; break;
case "Jobs/DownloadNewChapters": case "Tasks/DownloadNewChapters":
if(!requestVariables.TryGetValue("connector", out connectorName) || if(!requestVariables.TryGetValue("connector", out connectorName) ||
!requestVariables.TryGetValue("internalId", out internalId) || !requestVariables.TryGetValue("internalId", out internalId) ||
_parent.GetConnector(connectorName) is null || _parent.GetConnector(connectorName) is null ||
@ -386,8 +366,8 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
manga = (Manga)_parent.GetPublicationById(internalId)!; publication = (Publication)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, manga)); _parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, publication));
SendResponse(HttpStatusCode.Accepted, response); SendResponse(HttpStatusCode.Accepted, response);
break; break;
case "NotificationConnectors": case "NotificationConnectors":

View File

@ -39,7 +39,7 @@ public partial class Tranga : GlobalBase
return connectors; return connectors;
} }
public Manga? GetPublicationById(string internalId) public Publication? GetPublicationById(string internalId)
{ {
if (cachedPublications.Exists(publication => publication.internalId == internalId)) if (cachedPublications.Exists(publication => publication.internalId == internalId))
return cachedPublications.First(publication => publication.internalId == internalId); return cachedPublications.First(publication => publication.internalId == internalId);