Compare commits

...

3 Commits

Author SHA1 Message Date
9f30e52713 Added new API-Calls:
POST: Jobs/StartNow
DELETE: Jobs
2023-08-31 13:12:03 +02:00
1fd36c91d6 Renamed Publication.cs to Manga.cs
Renamed Request-Paths "Tasks" to "Jobs"
2023-08-31 12:16:02 +02:00
e663163de8 Download Covers only when Downloading Chapters 2023-08-31 12:14:03 +02:00
13 changed files with 180 additions and 135 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 Publication parentPublication { get; } public Manga parentManga { 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(Publication parentPublication, string? name, string? volumeNumber, string chapterNumber, string url) public Chapter(Manga parentManga, string? name, string? volumeNumber, string chapterNumber, string url)
{ {
this.parentPublication = parentPublication; this.parentManga = parentManga;
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 {parentPublication.sortName} {parentPublication.internalId} {chapterNumber} {name}"; return $"Chapter {parentManga.sortName} {parentManga.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, parentPublication.folderName))) if (!Directory.Exists(Path.Join(downloadLocation, parentManga.folderName)))
return false; return false;
FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentPublication.folderName)).GetFiles(); FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentManga.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, parentPublication.folderName, $"{parentPublication.folderName} - {this.fileName}.cbz"); return Path.Join(downloadLocation, parentManga.folderName, $"{parentManga.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(',', parentPublication.tags)), new XElement("Tags", string.Join(',', parentManga.tags)),
new XElement("LanguageISO", parentPublication.originalLanguage), new XElement("LanguageISO", parentManga.originalLanguage),
new XElement("Title", this.name), new XElement("Title", this.name),
new XElement("Writer", string.Join(',', parentPublication.authors)), new XElement("Writer", string.Join(',', parentManga.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<Publication> cachedPublications { get; init; } protected List<Manga> 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.parentPublication.internalId, chapter.chapterNumber))); return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), chapter.parentManga.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.parentPublication.sortName} - {chapter.chapterNumber}"); SendNotifications("Chapter downloaded", $"{chapter.parentManga.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 Publication publication { get; init; } public Manga manga { get; init; }
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Publication publication, bool recurring = false, TimeSpan? recurrence = null) : base (clone, connector, recurring, recurrence) public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, bool recurring = false, TimeSpan? recurrence = null) : base (clone, connector, recurring, recurrence)
{ {
this.publication = publication; this.manga = manga;
} }
protected override string GetId() protected override string GetId()
{ {
return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), publication.internalId))); return Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(this.GetType().ToString(), manga.internalId)));
} }
public override string ToString() public override string ToString()
{ {
return $"DownloadChapter {id} {publication}"; return $"DownloadChapter {id} {manga}";
} }
protected override IEnumerable<Job> ExecuteReturnSubTasksInternal() protected override IEnumerable<Job> ExecuteReturnSubTasksInternal()
{ {
Chapter[] chapters = mangaConnector.GetNewChapters(publication); Chapter[] chapters = mangaConnector.GetNewChapters(manga);
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.parentPublication.internalId == internalId && return job.chapter.parentManga.internalId == internalId &&
job.chapter.chapterNumber == chapterNumber; job.chapter.chapterNumber == chapterNumber;
}); });
else if (internalId is not null) else if (internalId is not null)
@ -52,24 +52,43 @@ public class JobBoss : GlobalBase
{ {
if (jjob is not DownloadNewChapters job) if (jjob is not DownloadNewChapters job)
return false; return false;
return job.publication.internalId == internalId; return job.manga.internalId == internalId;
}); });
return ret; return ret;
} }
public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Publication? publication = null, public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Manga? 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);
} }
private void AddJobToQueue(Job job) public 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

@ -9,7 +9,7 @@ namespace Tranga;
/// <summary> /// <summary>
/// Contains information on a Publication (Manga) /// Contains information on a Publication (Manga)
/// </summary> /// </summary>
public struct Publication public struct Manga
{ {
public string sortName { get; } public string sortName { get; }
public List<string> authors { get; } public List<string> authors { get; }
@ -19,8 +19,8 @@ public struct Publication
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? posterUrl { get; } public string? coverUrl { get; }
public string? coverFileNameInCache { get; } public string? coverFileNameInCache { get; set; }
// 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 Publication
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 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) 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)
{ {
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 = coverFileNameInCache; this.coverFileNameInCache = null;
this.posterUrl = posterUrl; this.coverUrl = coverUrl;
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

@ -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 Publication[] GetPublications(string publicationTitle = ""); public abstract Manga[] 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="publication">Publication to get Chapters for</param> /// <param name="manga">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(Publication publication, string language="en"); public abstract Chapter[] GetChapters(Manga manga, string language="en");
/// <summary> /// <summary>
/// Updates the available Chapters of a Publication /// Updates the available Chapters of a Publication
/// </summary> /// </summary>
/// <param name="publication">Publication to check</param> /// <param name="manga">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(Publication publication, string language = "en") public Chapter[] GetNewChapters(Manga manga, string language = "en")
{ {
Log($"Getting new Chapters for {publication}"); Log($"Getting new Chapters for {manga}");
Chapter[] newChapters = this.GetChapters(publication, language); Chapter[] newChapters = this.GetChapters(manga, language);
NumberFormatInfo decimalPoint = new (){ NumberDecimalSeparator = "." }; NumberFormatInfo decimalPoint = new (){ NumberDecimalSeparator = "." };
Log($"Checking for duplicates {publication}"); Log($"Checking for duplicates {manga}");
List<Chapter> newChaptersList = newChapters.Where(nChapter => List<Chapter> newChaptersList = newChapters.Where(nChapter =>
float.Parse(nChapter.chapterNumber, decimalPoint) > publication.ignoreChaptersBelow && float.Parse(nChapter.chapterNumber, decimalPoint) > manga.ignoreChaptersBelow &&
!nChapter.CheckChapterIsDownloaded(settings.downloadLocation)).ToList(); !nChapter.CheckChapterIsDownloaded(settings.downloadLocation)).ToList();
Log($"{newChaptersList.Count} new chapters. {publication}"); Log($"{newChaptersList.Count} new chapters. {manga}");
return newChaptersList.ToArray(); return newChaptersList.ToArray();
} }
public Chapter[] SelectChapters(Publication publication, string searchTerm, string? language = null) public Chapter[] SelectChapters(Manga manga, string searchTerm, string? language = null)
{ {
Chapter[] availableChapters = this.GetChapters(publication, language??"en"); Chapter[] availableChapters = this.GetChapters(manga, 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="publication">Publication to retrieve Cover for</param> /// <param name="manga">Publication to retrieve Cover for</param>
public void CopyCoverFromCacheToDownloadLocation(Publication publication) public void CopyCoverFromCacheToDownloadLocation(Manga manga)
{ {
Log($"Copy cover {publication}"); Log($"Copy cover {manga}");
//Check if Publication already has a Folder and cover //Check if Publication already has a Folder and cover
string publicationFolder = publication.CreatePublicationFolder(settings.downloadLocation); string publicationFolder = manga.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 {publication}"); Log($"Cover exists {manga}");
return; return;
} }
string fileInCache = Path.Join(settings.coverImageCache, publication.coverFileNameInCache); string fileInCache = Path.Join(settings.coverImageCache, manga.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 Publication[] GetPublications(string publicationTitle = "") public override Manga[] 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<Publication> publications = new(); HashSet<Manga> 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,9 +102,6 @@ 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);
@ -128,14 +125,13 @@ public class MangaDex : MangaConnector
string status = attributes["status"]!.GetValue<string>(); string status = attributes["status"]!.GetValue<string>();
Publication pub = new ( Manga pub = new (
title, title,
authors, authors,
description, description,
altTitlesDict, altTitlesDict,
tags.ToArray(), tags.ToArray(),
coverUrl, coverUrl,
coverCacheName,
linksDict, linksDict,
year, year,
originalLanguage, originalLanguage,
@ -151,9 +147,9 @@ public class MangaDex : MangaConnector
return publications.ToArray(); return publications.ToArray();
} }
public override Chapter[] GetChapters(Publication publication, string language="en") public override Chapter[] GetChapters(Manga manga, string language="en")
{ {
Log($"Getting chapters {publication}"); Log($"Getting chapters {manga}");
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
@ -164,7 +160,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/{publication.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", (byte)RequestType.Feed); $"https://api.mangadex.org/manga/{manga.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);
@ -195,13 +191,13 @@ public class MangaDex : MangaConnector
: "null"; : "null";
if(chapterNum is not "null") if(chapterNum is not "null")
chapters.Add(new Chapter(publication, title, volume, chapterNum, chapterId)); chapters.Add(new Chapter(manga, 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. {publication}"); Log($"Got {chapters.Count} chapters. {manga}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
@ -209,7 +205,8 @@ public class MangaDex : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}"); Manga chapterParentManga = chapter.parentManga;
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);
@ -230,6 +227,8 @@ 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 Publication[] GetPublications(string publicationTitle = "") public override Manga[] 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<Publication>(); return Array.Empty<Manga>();
// 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]) };
} }
Publication[] publications = ParsePublicationsFromHtml(requestResult.result); Manga[] 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 Publication[] ParsePublicationsFromHtml(Stream html) private Manga[] 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<Publication>(); return Array.Empty<Manga>();
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<Publication> ret = new(); HashSet<Manga> 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<Publication>(); return Array.Empty<Manga>();
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 Publication ParseSinglePublicationFromHtml(Stream html, string publicationId) private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId)
{ {
StreamReader reader = new(html); StreamReader reader = new(html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -119,8 +119,6 @@ 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);
@ -134,14 +132,14 @@ public class MangaKatana : MangaConnector
year = Convert.ToInt32(yearString); year = Convert.ToInt32(yearString);
} }
return new Publication(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, return new Manga(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, links,
year, originalLanguage, status, publicationId); year, originalLanguage, status, publicationId);
} }
public override Chapter[] GetChapters(Publication publication, string language="en") public override Chapter[] GetChapters(Manga manga, string language="en")
{ {
Log($"Getting chapters {publication}"); Log($"Getting chapters {manga}");
string requestUrl = $"https://mangakatana.com/manga/{publication.publicationId}"; string requestUrl = $"https://mangakatana.com/manga/{manga.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);
@ -153,12 +151,12 @@ public class MangaKatana : MangaConnector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
List<Chapter> chapters = ParseChaptersFromHtml(publication, requestUrl); List<Chapter> chapters = ParseChaptersFromHtml(manga, requestUrl);
Log($"Got {chapters.Count} chapters. {publication}"); Log($"Got {chapters.Count} chapters. {manga}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
private List<Chapter> ParseChaptersFromHtml(Publication publication, string mangaUrl) private List<Chapter> ParseChaptersFromHtml(Manga manga, 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();
@ -177,7 +175,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(publication, chapterName, volumeNumber, chapterNumber, url)); ret.Add(new Chapter(manga, chapterName, volumeNumber, chapterNumber, url));
} }
return ret; return ret;
@ -187,7 +185,8 @@ public class MangaKatana : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}"); Manga chapterParentManga = chapter.parentManga;
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 =
@ -200,6 +199,9 @@ 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 Publication[] GetPublications(string publicationTitle = "") public override Manga[] 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<Publication>(); return Array.Empty<Manga>();
Publication[] publications = ParsePublicationsFromHtml(requestResult.result); Manga[] 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 Publication[] ParsePublicationsFromHtml(Stream html) private Manga[] 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<Publication> ret = new(); HashSet<Manga> 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<Publication>(); return Array.Empty<Manga>();
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 Publication ParseSinglePublicationFromHtml(Stream html, string publicationId) private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -111,8 +111,6 @@ 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'))
@ -122,14 +120,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 Publication(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, return new Manga(sortName, authors.ToList(), description, altTitles, tags.ToArray(), posterUrl, links,
year, originalLanguage, status, publicationId); year, originalLanguage, status, publicationId);
} }
public override Chapter[] GetChapters(Publication publication, string language="en") public override Chapter[] GetChapters(Manga manga, string language="en")
{ {
Log($"Getting chapters {publication}"); Log($"Getting chapters {manga}");
string requestUrl = $"https://chapmanganato.com/{publication.publicationId}"; string requestUrl = $"https://chapmanganato.com/{manga.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)
@ -140,12 +138,12 @@ public class Manganato : MangaConnector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
List<Chapter> chapters = ParseChaptersFromHtml(publication, requestResult.result); List<Chapter> chapters = ParseChaptersFromHtml(manga, requestResult.result);
Log($"Got {chapters.Count} chapters. {publication}"); Log($"Got {chapters.Count} chapters. {manga}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
private List<Chapter> ParseChaptersFromHtml(Publication publication, Stream html) private List<Chapter> ParseChaptersFromHtml(Manga manga, Stream html)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
string htmlString = reader.ReadToEnd(); string htmlString = reader.ReadToEnd();
@ -164,7 +162,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(publication, chapterName, volumeNumber, chapterNumber, url)); ret.Add(new Chapter(manga, chapterName, volumeNumber, chapterNumber, url));
} }
ret.Reverse(); ret.Reverse();
return ret; return ret;
@ -174,7 +172,8 @@ public class Manganato : MangaConnector
{ {
if (progressToken?.cancellationRequested ?? false) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
Log($"Retrieving chapter-info {chapter} {chapter.parentPublication}"); Manga chapterParentManga = chapter.parentManga;
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);
@ -186,6 +185,9 @@ 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 Publication[] GetPublications(string publicationTitle = "") public override Manga[] 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<Publication>(); return Array.Empty<Manga>();
Publication[] publications = ParsePublicationsFromHtml(requestResult.result, publicationTitle); Manga[] 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 Publication[] ParsePublicationsFromHtml(Stream html, string publicationTitle) private Manga[] 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<Publication> ret = new(); HashSet<Manga> 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 Publication ParseSinglePublicationFromHtml(Stream html, string sortName, string publicationId, string[] a) private Manga ParseSinglePublicationFromHtml(Stream html, string sortName, string publicationId, string[] a)
{ {
StreamReader reader = new (html); StreamReader reader = new (html);
HtmlDocument document = new (); HtmlDocument document = new ();
@ -133,7 +133,6 @@ 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"))
@ -171,7 +170,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 Publication(sortName, authors, description, altTitles, tags.ToArray(), posterUrl, coverFileNameInCache, links, return new Manga(sortName, authors, description, altTitles, tags.ToArray(), posterUrl, links,
year, originalLanguage, status, publicationId); year, originalLanguage, status, publicationId);
} }
@ -215,10 +214,10 @@ public class Mangasee : MangaConnector
} }
} }
public override Chapter[] GetChapters(Publication publication, string language="en") public override Chapter[] GetChapters(Manga manga, string language="en")
{ {
Log($"Getting chapters {publication}"); Log($"Getting chapters {manga}");
XDocument doc = XDocument.Load($"https://mangasee123.com/rss/{publication.publicationId}.xml"); XDocument doc = XDocument.Load($"https://mangasee123.com/rss/{manga.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)
@ -229,7 +228,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(publication, "", volumeNumber, chapterNumber, url)); chapters.Add(new Chapter(manga, "", volumeNumber, chapterNumber, url));
} }
//Return Chapters ordered by Chapter-Number //Return Chapters ordered by Chapter-Number
@ -237,7 +236,7 @@ public class Mangasee : MangaConnector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
Log($"Got {chapters.Count} chapters. {publication}"); Log($"Got {chapters.Count} chapters. {manga}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
@ -245,6 +244,7 @@ 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} {chapter.parentPublication}"); Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
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,6 +270,9 @@ 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

@ -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 "Publications/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.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 "Publications/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 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)!;
Publication publication = (Publication)_parent.GetPublicationById(internalId)!; Manga manga = (Manga)_parent.GetPublicationById(internalId)!;
SendResponse(HttpStatusCode.OK, response, connector.GetChapters(publication)); SendResponse(HttpStatusCode.OK, response, connector.GetChapters(manga));
break; break;
case "Tasks": 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))
@ -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 "Tasks/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))
@ -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 "Tasks/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 "Tasks/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 "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;
Publication publication; 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 "Tasks/MonitorManga": case "Jobs/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)!;
publication = (Publication)_parent.GetPublicationById(internalId)!; manga = (Manga)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, publication, true, interval)); _parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, true, interval));
SendResponse(HttpStatusCode.Accepted, response); SendResponse(HttpStatusCode.Accepted, response);
break; break;
case "Tasks/DownloadNewChapters": case "Jobs/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,8 +221,18 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
publication = (Publication)_parent.GetPublicationById(internalId)!; manga = (Manga)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, publication, false)); _parent._jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, 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":
@ -325,11 +335,21 @@ 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;
Publication publication; 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 "Tasks/DownloadChapter": case "Jobs":
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) ||
@ -342,7 +362,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 "Tasks/MonitorManga": case "Jobs/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 ||
@ -352,11 +372,11 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
publication = (Publication)_parent.GetPublicationById(internalId)!; manga = (Manga)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, publication)); _parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, manga));
SendResponse(HttpStatusCode.Accepted, response); SendResponse(HttpStatusCode.Accepted, response);
break; break;
case "Tasks/DownloadNewChapters": case "Jobs/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 ||
@ -366,8 +386,8 @@ public class Server : GlobalBase
break; break;
} }
connector = _parent.GetConnector(connectorName)!; connector = _parent.GetConnector(connectorName)!;
publication = (Publication)_parent.GetPublicationById(internalId)!; manga = (Manga)_parent.GetPublicationById(internalId)!;
_parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, publication)); _parent._jobBoss.RemoveJobs(_parent._jobBoss.GetJobsLike(connector, manga));
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 Publication? GetPublicationById(string internalId) public Manga? 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);