diff --git a/Tranga/Connector.cs b/Tranga/Connector.cs index a3913fd..d0f6783 100644 --- a/Tranga/Connector.cs +++ b/Tranga/Connector.cs @@ -1,4 +1,5 @@ using System.IO.Compression; +using System.Net; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Xml.Linq; @@ -133,7 +134,7 @@ public abstract class Connector /// Chapter with Images to retrieve /// Will be used for progress-tracking /// - public abstract bool DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null); + public abstract HttpStatusCode DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null); /// /// Copies the already downloaded cover from cache to downloadLocation @@ -212,15 +213,15 @@ public abstract class Connector /// /// RequestType for Rate-Limit /// referrer used in html request header - private bool DownloadImage(string imageUrl, string fullPath, byte requestType, string? referrer = null) + private HttpStatusCode DownloadImage(string imageUrl, string fullPath, byte requestType, string? referrer = null) { DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(imageUrl, requestType, referrer); - if (!requestResult.success || requestResult.result == Stream.Null) - return false; + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.result == Stream.Null) + return requestResult.statusCode; byte[] buffer = new byte[requestResult.result.Length]; requestResult.result.ReadExactly(buffer, 0, buffer.Length); File.WriteAllBytes(fullPath, buffer); - return true; + return requestResult.statusCode; } /// @@ -233,10 +234,10 @@ public abstract class Connector /// RequestType for RateLimits /// Used in http request header /// - protected bool DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, byte requestType, DownloadChapterTask parentTask, string? comicInfoPath = null, string? referrer = null, CancellationToken? cancellationToken = null) + protected HttpStatusCode DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, byte requestType, DownloadChapterTask parentTask, string? comicInfoPath = null, string? referrer = null, CancellationToken? cancellationToken = null) { - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; logger?.WriteLine("Connector", $"Downloading Images for {saveArchiveFilePath}"); //Check if Publication Directory already exists string directoryPath = Path.GetDirectoryName(saveArchiveFilePath)!; @@ -244,7 +245,7 @@ public abstract class Connector Directory.CreateDirectory(directoryPath); if (File.Exists(saveArchiveFilePath)) //Don't download twice. - return false; + return HttpStatusCode.OK; //Create a temporary folder to store images string tempFolder = Directory.CreateTempSubdirectory().FullName; @@ -256,11 +257,12 @@ public abstract class Connector string[] split = imageUrl.Split('.'); string extension = split[^1]; logger?.WriteLine("Connector", $"Downloading Image {chapter + 1:000}/{imageUrls.Length:000} {parentTask.publication.sortName} {parentTask.publication.internalId} Vol.{parentTask.chapter.volumeNumber} Ch.{parentTask.chapter.chapterNumber} {parentTask.progress:P2}"); - if (!DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), requestType, referrer)) - return false; + HttpStatusCode status = DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), requestType, referrer); + if ((int)status < 200 || (int)status >= 300) + return status; parentTask.IncrementProgress(1.0 / imageUrls.Length); - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; } if(comicInfoPath is not null) @@ -272,7 +274,7 @@ public abstract class Connector if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) File.SetUnixFileMode(saveArchiveFilePath, GroupRead | GroupWrite | OtherRead | OtherWrite | UserRead | UserWrite); Directory.Delete(tempFolder, true); //Cleanup - return true; + return HttpStatusCode.OK; } protected string SaveCoverImageToCache(string url, byte requestType) @@ -332,7 +334,7 @@ public abstract class Connector else { logger?.WriteLine(this.GetType().ToString(), "RequestType not configured for rate-limit."); - return new RequestResult(false, Stream.Null); + return new RequestResult(HttpStatusCode.NotAcceptable, Stream.Null); } TimeSpan rateLimitTimeout = _rateLimit[requestType] @@ -362,19 +364,19 @@ public abstract class Connector if (!response.IsSuccessStatusCode) { logger?.WriteLine(this.GetType().ToString(), $"Request-Error {response.StatusCode}: {response.ReasonPhrase}"); - return new RequestResult(false, Stream.Null); + return new RequestResult(response.StatusCode, Stream.Null); } - return new RequestResult(true, response.Content.ReadAsStream()); + return new RequestResult(response.StatusCode, response.Content.ReadAsStream()); } public struct RequestResult { - public bool success { get; } + public HttpStatusCode statusCode { get; } public Stream result { get; } - public RequestResult(bool success, Stream result) + public RequestResult(HttpStatusCode statusCode, Stream result) { - this.success = success; + this.statusCode = statusCode; this.result = result; } } diff --git a/Tranga/Connectors/MangaDex.cs b/Tranga/Connectors/MangaDex.cs index e56f5da..422975d 100644 --- a/Tranga/Connectors/MangaDex.cs +++ b/Tranga/Connectors/MangaDex.cs @@ -46,7 +46,7 @@ public class MangaDex : Connector DownloadClient.RequestResult requestResult = downloadClient.MakeRequest( $"https://api.mangadex.org/manga?limit={limit}&title={publicationTitle}&offset={offset}", (byte)RequestType.Manga); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) break; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); @@ -165,7 +165,7 @@ public class MangaDex : Connector DownloadClient.RequestResult requestResult = downloadClient.MakeRequest( $"https://api.mangadex.org/manga/{publication.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", (byte)RequestType.Feed); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) break; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); @@ -207,19 +207,19 @@ public class MangaDex : Connector return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); } - public override bool DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) + public override HttpStatusCode DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) { - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}"); //Request URLs for Chapter-Images DownloadClient.RequestResult requestResult = downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer); - if (!requestResult.success) - return false; + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) + return requestResult.statusCode; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); if (result is null) - return false; + return HttpStatusCode.NoContent; string baseUrl = result["baseUrl"]!.GetValue(); string hash = result["chapter"]!["hash"]!.GetValue(); @@ -248,7 +248,7 @@ public class MangaDex : Connector //Request information where to download Cover DownloadClient.RequestResult requestResult = downloadClient.MakeRequest($"https://api.mangadex.org/cover/{posterId}", (byte)RequestType.CoverUrl); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); if (result is null) @@ -268,7 +268,7 @@ public class MangaDex : Connector { DownloadClient.RequestResult requestResult = downloadClient.MakeRequest($"https://api.mangadex.org/author/{authorId}", (byte)RequestType.Author); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return ret; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); if (result is null) diff --git a/Tranga/Connectors/Manganato.cs b/Tranga/Connectors/Manganato.cs index 83b6e6d..8378088 100644 --- a/Tranga/Connectors/Manganato.cs +++ b/Tranga/Connectors/Manganato.cs @@ -27,7 +27,7 @@ public class Manganato : Connector string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}"; DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(requestUrl, (byte)1); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); return ParsePublicationsFromHtml(requestResult.result); @@ -52,7 +52,7 @@ public class Manganato : Connector { DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(url, (byte)1); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); ret.Add(ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1])); @@ -131,7 +131,7 @@ public class Manganato : Connector string requestUrl = $"https://chapmanganato.com/{publication.publicationId}"; DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(requestUrl, (byte)1); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); //Return Chapters ordered by Chapter-Number @@ -169,16 +169,16 @@ public class Manganato : Connector return ret; } - public override bool DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) + public override HttpStatusCode DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) { - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}"); string requestUrl = chapter.url; DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(requestUrl, (byte)1); - if (!requestResult.success) - return false; + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) + return requestResult.statusCode; string[] imageUrls = ParseImageUrlsFromHtml(requestResult.result); diff --git a/Tranga/Connectors/Mangasee.cs b/Tranga/Connectors/Mangasee.cs index ec9f0fc..6753af6 100644 --- a/Tranga/Connectors/Mangasee.cs +++ b/Tranga/Connectors/Mangasee.cs @@ -80,7 +80,7 @@ public class Mangasee : Connector string requestUrl = $"https://mangasee123.com/_search.php"; DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(requestUrl, (byte)1); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); return ParsePublicationsFromHtml(requestResult.result, publicationTitle); @@ -110,7 +110,7 @@ public class Mangasee : Connector { DownloadClient.RequestResult requestResult = downloadClient.MakeRequest($"https://mangasee123.com/manga/{orderedItem.i}", (byte)1); - if (!requestResult.success) + if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); ret.Add(ParseSinglePublicationFromHtml(requestResult.result, orderedItem.s, orderedItem.i, orderedItem.a)); } @@ -209,17 +209,17 @@ public class Mangasee : Connector return ret.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); } - public override bool DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) + public override HttpStatusCode DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) { if (cancellationToken?.IsCancellationRequested ?? false) - return false; + return HttpStatusCode.RequestTimeout; while (this._browser is null && !(cancellationToken?.IsCancellationRequested??false)) { logger?.WriteLine(this.GetType().ToString(), "Waiting for headless browser to download..."); Thread.Sleep(1000); } if (cancellationToken?.IsCancellationRequested??false) - return false; + return HttpStatusCode.RequestTimeout; logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}"); IPage page = _browser!.NewPageAsync().Result; @@ -240,7 +240,6 @@ public class Mangasee : Connector return DownloadChapterImages(urls.ToArray(), GetArchiveFilePath(publication, chapter), (byte)1, parentTask, comicInfoPath, cancellationToken:cancellationToken); } - - return false; + return response.Status; } } \ No newline at end of file diff --git a/Tranga/TaskManager.cs b/Tranga/TaskManager.cs index e82c089..0795f0f 100644 --- a/Tranga/TaskManager.cs +++ b/Tranga/TaskManager.cs @@ -82,18 +82,21 @@ public class TaskManager } } - foreach (TrangaTask failedTask in _allTasks.Where(taskQuery => - taskQuery.state is TrangaTask.ExecutionState.Failed)) + TrangaTask[] failedDownloadChapterTasks = _allTasks.Where(taskQuery => + taskQuery.state is TrangaTask.ExecutionState.Failed && taskQuery is DownloadChapterTask).ToArray(); + foreach (TrangaTask failedDownloadChapterTask in failedDownloadChapterTasks) { - switch (failedTask.task) - { - case TrangaTask.Task.DownloadChapter: - DeleteTask(failedTask); - TrangaTask newTask = failedTask.Clone(); - failedTask.parentTask?.AddChildTask(newTask); - AddTask(newTask); - break; - } + DeleteTask(failedDownloadChapterTask); + TrangaTask newTask = failedDownloadChapterTask.Clone(); + failedDownloadChapterTask.parentTask?.AddChildTask(newTask); + AddTask(newTask); + } + + TrangaTask[] successfulDownloadChapterTasks = _allTasks.Where(taskQuery => + taskQuery.state is TrangaTask.ExecutionState.Success && taskQuery is DownloadChapterTask).ToArray(); + foreach(TrangaTask successfulDownloadChapterTask in successfulDownloadChapterTasks) + { + DeleteTask(successfulDownloadChapterTask); } if(waitingTasksCount != _allTasks.Count(task => task.state is TrangaTask.ExecutionState.Waiting)) diff --git a/Tranga/TrangaTask.cs b/Tranga/TrangaTask.cs index d2c0263..f0c92f2 100644 --- a/Tranga/TrangaTask.cs +++ b/Tranga/TrangaTask.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using System.Net; +using System.Text.Json.Serialization; using Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -32,7 +33,7 @@ public abstract class TrangaTask [Newtonsoft.Json.JsonIgnore]public TimeSpan executionApproximatelyRemaining => executionApproximatelyFinished.Subtract(DateTime.Now); [Newtonsoft.Json.JsonIgnore]public DateTime nextExecution => lastExecuted.Add(reoccurrence); - public enum ExecutionState { Waiting, Enqueued, Running, Failed } + public enum ExecutionState { Waiting, Enqueued, Running, Failed, Success } protected TrangaTask(Task task, TimeSpan reoccurrence, TrangaTask? parentTask = null) { @@ -53,7 +54,7 @@ public abstract class TrangaTask /// /// /// - protected abstract bool ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null); + protected abstract HttpStatusCode ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null); public abstract TrangaTask Clone(); @@ -73,18 +74,24 @@ public abstract class TrangaTask this.parentTask.state = ExecutionState.Running; this.executionStarted = DateTime.Now; this.lastChange = DateTime.Now; - bool success = ExecuteTask(taskManager, logger, cancellationToken); + HttpStatusCode statusCode = ExecuteTask(taskManager, logger, cancellationToken); while(childTasks.Any(ct => ct.state is ExecutionState.Enqueued or ExecutionState.Running)) Thread.Sleep(1000); - if (success) + if ((int)statusCode >= 200 && (int)statusCode < 300) { this.lastExecuted = DateTime.Now; - this.state = ExecutionState.Waiting; + if (this is DownloadChapterTask) + this.state = ExecutionState.Success; + else + this.state = ExecutionState.Waiting; } else { + if (this is DownloadChapterTask && statusCode == HttpStatusCode.NotFound) + this.state = ExecutionState.Success; + else + this.state = ExecutionState.Failed; this.lastExecuted = DateTime.MaxValue; - this.state = ExecutionState.Failed; } if(this.parentTask is not null) this.parentTask.state = ExecutionState.Waiting; diff --git a/Tranga/TrangaTasks/DownloadChapterTask.cs b/Tranga/TrangaTasks/DownloadChapterTask.cs index e60c51a..c870be6 100644 --- a/Tranga/TrangaTasks/DownloadChapterTask.cs +++ b/Tranga/TrangaTasks/DownloadChapterTask.cs @@ -1,4 +1,5 @@ -using Logging; +using System.Net; +using Logging; namespace Tranga.TrangaTasks; @@ -19,15 +20,14 @@ public class DownloadChapterTask : TrangaTask this.language = language; } - protected override bool ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) + protected override HttpStatusCode ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; Connector connector = taskManager.GetConnector(this.connectorName); connector.CopyCoverFromCacheToDownloadLocation(this.publication, taskManager.settings); - bool downloadSuccess = connector.DownloadChapter(this.publication, this.chapter, this, cancellationToken); - taskManager.DeleteTask(this); - if(downloadSuccess && parentTask is not null) + HttpStatusCode downloadSuccess = connector.DownloadChapter(this.publication, this.chapter, this, cancellationToken); + if((int)downloadSuccess >= 200 && (int)downloadSuccess < 300 && parentTask is not null) foreach(NotificationManager nm in taskManager.settings.notificationManagers) nm.SendNotification("New Chapter downloaded", $"{this.publication.sortName} {this.chapter.chapterNumber} {this.chapter.name}"); return downloadSuccess; diff --git a/Tranga/TrangaTasks/MonitorPublicationTask.cs b/Tranga/TrangaTasks/MonitorPublicationTask.cs index a8b794a..e36cf44 100644 --- a/Tranga/TrangaTasks/MonitorPublicationTask.cs +++ b/Tranga/TrangaTasks/MonitorPublicationTask.cs @@ -1,4 +1,5 @@ -using Logging; +using System.Net; +using Logging; namespace Tranga.TrangaTasks; @@ -14,10 +15,10 @@ public class MonitorPublicationTask : TrangaTask this.language = language; } - protected override bool ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) + protected override HttpStatusCode ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; Connector connector = taskManager.GetConnector(this.connectorName); //Check if Publication already has a Folder @@ -36,7 +37,7 @@ public class MonitorPublicationTask : TrangaTask taskManager.AddTask(newTask); } - return true; + return HttpStatusCode.OK; } public override TrangaTask Clone() diff --git a/Tranga/TrangaTasks/UpdateLibrariesTask.cs b/Tranga/TrangaTasks/UpdateLibrariesTask.cs index fa00b38..545ac9e 100644 --- a/Tranga/TrangaTasks/UpdateLibrariesTask.cs +++ b/Tranga/TrangaTasks/UpdateLibrariesTask.cs @@ -1,4 +1,5 @@ -using Logging; +using System.Net; +using Logging; namespace Tranga.TrangaTasks; @@ -8,13 +9,13 @@ public class UpdateLibrariesTask : TrangaTask { } - protected override bool ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) + protected override HttpStatusCode ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { - if (cancellationToken?.IsCancellationRequested??false) - return false; + if (cancellationToken?.IsCancellationRequested ?? false) + return HttpStatusCode.RequestTimeout; foreach(LibraryManager lm in taskManager.settings.libraryManagers) lm.UpdateLibrary(); - return true; + return HttpStatusCode.OK; } public override TrangaTask Clone()