diff --git a/Tranga/Connector.cs b/Tranga/Connector.cs index 3331517..2f91f79 100644 --- a/Tranga/Connector.cs +++ b/Tranga/Connector.cs @@ -127,7 +127,8 @@ public abstract class Connector /// Publication that contains Chapter /// Chapter with Images to retrieve /// Will be used for progress-tracking - public abstract void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask); + /// + public abstract void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null); /// /// Copies the already downloaded cover from cache to downloadLocation @@ -227,8 +228,10 @@ public abstract class Connector /// Path of the generate Chapter ComicInfo.xml, if it was generated /// RequestType for RateLimits /// Used in http request header - protected void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, byte requestType, DownloadChapterTask parentTask, string? comicInfoPath = null, string? referrer = null) + protected void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, byte requestType, DownloadChapterTask parentTask, string? comicInfoPath = null, string? referrer = null, CancellationToken? cancellationToken = null) { + if (cancellationToken?.IsCancellationRequested??false) + return; logger?.WriteLine("Connector", $"Downloading Images for {saveArchiveFilePath}"); //Check if Publication Directory already exists string directoryPath = Path.GetDirectoryName(saveArchiveFilePath)!; @@ -250,6 +253,8 @@ public abstract class Connector 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}"); DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), requestType, referrer); parentTask.IncrementProgress(1f / imageUrls.Length); + if (cancellationToken?.IsCancellationRequested??false) + return; } if(comicInfoPath is not null) diff --git a/Tranga/Connectors/MangaDex.cs b/Tranga/Connectors/MangaDex.cs index 6f192ee..faa72e4 100644 --- a/Tranga/Connectors/MangaDex.cs +++ b/Tranga/Connectors/MangaDex.cs @@ -207,8 +207,10 @@ public class MangaDex : Connector return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); } - public override void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask) + public override void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) { + if (cancellationToken?.IsCancellationRequested??false) + return; logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}"); //Request URLs for Chapter-Images DownloadClient.RequestResult requestResult = @@ -231,7 +233,7 @@ public class MangaDex : Connector File.WriteAllText(comicInfoPath, GetComicInfoXmlString(publication, chapter, logger)); //Download Chapter-Images - DownloadChapterImages(imageUrls.ToArray(), GetArchiveFilePath(publication, chapter), (byte)RequestType.AtHomeServer, parentTask, comicInfoPath); + DownloadChapterImages(imageUrls.ToArray(), GetArchiveFilePath(publication, chapter), (byte)RequestType.AtHomeServer, parentTask, comicInfoPath, cancellationToken:cancellationToken); } private string? GetCoverUrl(string publicationId, string? posterId) diff --git a/Tranga/Connectors/Manganato.cs b/Tranga/Connectors/Manganato.cs index 4e5b2d1..5085320 100644 --- a/Tranga/Connectors/Manganato.cs +++ b/Tranga/Connectors/Manganato.cs @@ -169,8 +169,10 @@ public class Manganato : Connector return ret; } - public override void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask) + public override void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) { + if (cancellationToken?.IsCancellationRequested??false) + return; logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}"); string requestUrl = chapter.url; DownloadClient.RequestResult requestResult = @@ -183,7 +185,7 @@ public class Manganato : Connector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, GetComicInfoXmlString(publication, chapter, logger)); - DownloadChapterImages(imageUrls, GetArchiveFilePath(publication, chapter), (byte)1, parentTask, comicInfoPath, "https://chapmanganato.com/"); + DownloadChapterImages(imageUrls, GetArchiveFilePath(publication, chapter), (byte)1, parentTask, comicInfoPath, "https://chapmanganato.com/", cancellationToken); } private string[] ParseImageUrlsFromHtml(Stream html) diff --git a/Tranga/Connectors/Mangasee.cs b/Tranga/Connectors/Mangasee.cs index 33c4256..76214c2 100644 --- a/Tranga/Connectors/Mangasee.cs +++ b/Tranga/Connectors/Mangasee.cs @@ -209,16 +209,20 @@ public class Mangasee : Connector return ret.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); } - public override void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask) + public override void DownloadChapter(Publication publication, Chapter chapter, DownloadChapterTask parentTask, CancellationToken? cancellationToken = null) { - while (this._browser is null) + if (cancellationToken?.IsCancellationRequested??false) + return; + 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; logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}"); - IPage page = _browser.NewPageAsync().Result; + IPage page = _browser!.NewPageAsync().Result; IResponse response = page.GoToAsync(chapter.url).Result; if (response.Ok) { @@ -234,7 +238,7 @@ public class Mangasee : Connector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, GetComicInfoXmlString(publication, chapter, logger)); - DownloadChapterImages(urls.ToArray(), GetArchiveFilePath(publication, chapter), (byte)1, parentTask, comicInfoPath); + DownloadChapterImages(urls.ToArray(), GetArchiveFilePath(publication, chapter), (byte)1, parentTask, comicInfoPath, cancellationToken:cancellationToken); } } } \ No newline at end of file diff --git a/Tranga/TaskManager.cs b/Tranga/TaskManager.cs index 1f5edd5..2de3788 100644 --- a/Tranga/TaskManager.cs +++ b/Tranga/TaskManager.cs @@ -129,16 +129,18 @@ public class TaskManager { logger?.WriteLine(this.GetType().ToString(), $"Disposing failed task {removeTask.Key}."); removeTask.Key.parentTask?.DecrementProgress(removeTask.Key.progress); - //removeTask.Value.Dispose(); Currently not available, however since task is removed from _allTasks should work. Memory leak however... + removeTask.Value.Dispose(); toRemove.Add(removeTask.Key); } } foreach (DownloadChapterTask taskToRemove in toRemove) { DeleteTask(taskToRemove); - AddTask(new DownloadChapterTask(taskToRemove.task, taskToRemove.connectorName, + DownloadChapterTask newTask = new DownloadChapterTask(taskToRemove.task, taskToRemove.connectorName, taskToRemove.publication, taskToRemove.chapter, taskToRemove.language, - taskToRemove.parentTask)); + taskToRemove.parentTask); + AddTask(newTask); + taskToRemove.parentTask?.ReplaceFailedChildTask(taskToRemove, newTask); } if(allTasksWaitingLength != _allTasks.Count(task => task.state is TrangaTask.ExecutionState.Waiting)) @@ -155,10 +157,11 @@ public class TaskManager public void ExecuteTaskNow(TrangaTask task) { task.state = TrangaTask.ExecutionState.Running; + CancellationToken cToken = new CancellationToken(); Task t = new(() => { - task.Execute(this, this.logger); - }); + task.Execute(this, this.logger, cToken); + }, cToken); if(task.GetType() == typeof(DownloadChapterTask)) _runningDownloadChapterTasks.Add((DownloadChapterTask)task, t); t.Start(); diff --git a/Tranga/TrangaTask.cs b/Tranga/TrangaTask.cs index 274a461..73d70fd 100644 --- a/Tranga/TrangaTask.cs +++ b/Tranga/TrangaTask.cs @@ -71,20 +71,22 @@ public abstract class TrangaTask /// /// /// - protected abstract void ExecuteTask(TaskManager taskManager, Logger? logger); + /// + protected abstract void ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null); /// /// Execute the task /// /// Should be the parent taskManager /// - public void Execute(TaskManager taskManager, Logger? logger) + /// + public void Execute(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { logger?.WriteLine(this.GetType().ToString(), $"Executing Task {this}"); this.state = ExecutionState.Running; this.executionStarted = DateTime.Now; this.lastChange = DateTime.Now; - ExecuteTask(taskManager, logger); + ExecuteTask(taskManager, logger, cancellationToken); this.lastExecuted = DateTime.Now; this.state = ExecutionState.Waiting; logger?.WriteLine(this.GetType().ToString(), $"Finished Executing Task {this}"); diff --git a/Tranga/TrangaTasks/DownloadChapterTask.cs b/Tranga/TrangaTasks/DownloadChapterTask.cs index f424ed4..0638a34 100644 --- a/Tranga/TrangaTasks/DownloadChapterTask.cs +++ b/Tranga/TrangaTasks/DownloadChapterTask.cs @@ -20,10 +20,12 @@ public class DownloadChapterTask : TrangaTask this.parentTask = parentTask; } - protected override void ExecuteTask(TaskManager taskManager, Logger? logger) + protected override void ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { + if (cancellationToken?.IsCancellationRequested??false) + return; Connector connector = taskManager.GetConnector(this.connectorName); - connector.DownloadChapter(this.publication, this.chapter, this); + connector.DownloadChapter(this.publication, this.chapter, this, cancellationToken); taskManager.DeleteTask(this); } diff --git a/Tranga/TrangaTasks/DownloadNewChaptersTask.cs b/Tranga/TrangaTasks/DownloadNewChaptersTask.cs index 1d1caa0..448a65b 100644 --- a/Tranga/TrangaTasks/DownloadNewChaptersTask.cs +++ b/Tranga/TrangaTasks/DownloadNewChaptersTask.cs @@ -8,46 +8,59 @@ public class DownloadNewChaptersTask : TrangaTask public string connectorName { get; } public Publication publication { get; } public string language { get; } - [JsonIgnore]private int childTaskAmount { get; set; } + [JsonIgnore]private HashSet childTasks { get; } public DownloadNewChaptersTask(Task task, string connectorName, Publication publication, TimeSpan reoccurrence, string language = "en") : base(task, reoccurrence) { this.connectorName = connectorName; this.publication = publication; this.language = language; - childTaskAmount = 0; + this.childTasks = new(); } public new float IncrementProgress(float amount) { - this.progress += amount / this.childTaskAmount; + this.progress += amount / this.childTasks.Count; this.lastChange = DateTime.Now; return this.progress; } public new float DecrementProgress(float amount) { - this.progress -= amount / this.childTaskAmount; + this.progress -= amount / this.childTasks.Count; this.lastChange = DateTime.Now; return this.progress; } - protected override void ExecuteTask(TaskManager taskManager, Logger? logger) + protected override void ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { + if (cancellationToken?.IsCancellationRequested??false) + return; Publication pub = publication!; Connector connector = taskManager.GetConnector(this.connectorName); //Check if Publication already has a Folder pub.CreatePublicationFolder(taskManager.settings.downloadLocation); List newChapters = GetNewChaptersList(connector, pub, language!, ref taskManager.chapterCollection); - this.childTaskAmount = newChapters.Count; connector.CopyCoverFromCacheToDownloadLocation(pub, taskManager.settings); pub.SaveSeriesInfoJson(connector.downloadLocation); foreach (Chapter newChapter in newChapters) - taskManager.AddTask(new DownloadChapterTask(Task.DownloadChapter, this.connectorName!, pub, newChapter, this.language, this)); + { + DownloadChapterTask newTask = new (Task.DownloadChapter, this.connectorName!, pub, newChapter, this.language, this); + taskManager.AddTask(newTask); + this.childTasks.Add(newTask); + } + } + + public void ReplaceFailedChildTask(DownloadChapterTask failed, DownloadChapterTask newTask) + { + if (!this.childTasks.Contains(failed)) + throw new ArgumentException($"Task {failed} is not childTask of {this}"); + this.childTasks.Remove(failed); + this.childTasks.Add(newTask); } /// diff --git a/Tranga/TrangaTasks/UpdateLibrariesTask.cs b/Tranga/TrangaTasks/UpdateLibrariesTask.cs index ef94a13..c8d4f04 100644 --- a/Tranga/TrangaTasks/UpdateLibrariesTask.cs +++ b/Tranga/TrangaTasks/UpdateLibrariesTask.cs @@ -8,8 +8,10 @@ public class UpdateLibrariesTask : TrangaTask { } - protected override void ExecuteTask(TaskManager taskManager, Logger? logger) + protected override void ExecuteTask(TaskManager taskManager, Logger? logger, CancellationToken? cancellationToken = null) { + if (cancellationToken?.IsCancellationRequested??false) + return; foreach(LibraryManager lm in taskManager.settings.libraryManagers) lm.UpdateLibrary(); this.progress = 1f;