18 Commits
0.6.1 ... 0.6.2

Author SHA1 Message Date
eddf50483f Fixed some nullable types 2023-05-22 21:44:52 +02:00
a71d65e666 Fix negative sleep time 2023-05-22 21:41:11 +02:00
9a640aed27 Rewrote CoverDownload check if exists. 2023-05-22 21:38:44 +02:00
30b6c4680b Better Rate-Limits
Added Logger to DownloadClient
2023-05-22 21:38:23 +02:00
7b6253de0f Create Publication Folder at start of DownloadNewChapters 2023-05-22 21:37:30 +02:00
5aa3214ce5 TrangaTask.ToString() rewrite for logs-readability.
LogMessages only include class-name without path
2023-05-22 21:37:02 +02:00
9b70994f71 Adjusted RateLimit 2023-05-22 18:55:26 +02:00
93cf341f2d Fixed Publication.InternalId 2023-05-22 18:28:42 +02:00
01cb74c088 First attempt at #18 Rate Limits 2023-05-22 18:15:59 +02:00
ec480dffad Merge pull request 'closes #7' (#17) from Issue_7 into master
Reviewed-on: #17
2023-05-22 17:21:42 +02:00
b7014cbff5 Merge pull request 'fixes #14' (#16) from Issue_14_ChapterIsDownlaoded into master
Reviewed-on: #16
2023-05-22 17:21:19 +02:00
0cab921402 Merge pull request 'fixes #11' (#15) from Issue_11 into master
Reviewed-on: #15
2023-05-22 17:20:54 +02:00
0e0ba1796e closes #7 2023-05-22 17:20:07 +02:00
27d8565dc1 fixes #14 2023-05-22 17:09:47 +02:00
79dc44d707 fixes 11 2023-05-22 17:04:31 +02:00
bb6a0ad0d4 Merge pull request 'fixes #9' (#13) from Issue_9 into master
Reviewed-on: #13
2023-05-22 16:53:40 +02:00
43db463ba6 fixes #9 2023-05-22 16:52:52 +02:00
9eb8ddbc40 Changed Publication:
downloadUrl is now publicationId, internal to Connector
posterUrl is now a URL to the file, instead of an id
2023-05-22 16:45:55 +02:00
8 changed files with 189 additions and 82 deletions

View File

@ -52,7 +52,7 @@ public abstract class LoggerBase : TextWriter
public override string ToString()
{
string dateTimeString = $"{logTime.ToShortDateString()} {logTime.ToLongTimeString()}";
return $"[{dateTimeString}] {caller,30} | {value}";
return $"[{dateTimeString}] {caller.Split(new char[]{'.','+'}).Last(),15} | {value}";
}
}
}

View File

@ -210,7 +210,11 @@ public static class Tranga_Cli
Console.WriteLine(header);
Console.WriteLine(new string('-', header.Length));
foreach (TrangaTask trangaTask in tasks)
Console.WriteLine($"{tIndex++:000}: {trangaTask}");
{
string[] taskSplit = trangaTask.ToString().Split(", ");
Console.WriteLine($"{tIndex++:000}: {taskSplit[0],-20} | {taskSplit[1],-20} | {taskSplit[2],-12} | {taskSplit[3],-10} | {(taskSplit.Length > 4 ? taskSplit[4] : ""),-15} | {(taskSplit.Length > 5 ? taskSplit[5] : "")}");
}
}
private static TrangaTask? SelectTask(TrangaTask[] tasks, Logger logger)
@ -244,7 +248,6 @@ public static class Tranga_Cli
try
{
int selectedTaskIndex = Convert.ToInt32(selectedTask);
logger.WriteLine("Tranga_CLI", "Sending Task to TaskManager");
return tasks[selectedTaskIndex];
}
catch (Exception e)
@ -478,6 +481,13 @@ public static class Tranga_Cli
Publication[] publications = taskManager.GetPublicationsFromConnector(connector, query ?? "");
if (publications.Length < 1)
{
logger.WriteLine("Tranga_CLI", "No publications returned");
Console.WriteLine($"No publications for query '{query}' returned;");
return null;
}
int pIndex = 0;
Console.WriteLine("Publications:");
foreach(Publication publication in publications)

View File

@ -12,15 +12,18 @@ namespace Tranga;
public abstract class Connector
{
internal string downloadLocation { get; } //Location of local files
protected DownloadClient downloadClient { get; }
protected DownloadClient downloadClient { get; init; }
protected Logger? logger;
protected Connector(string downloadLocation, uint downloadDelay, Logger? logger)
protected Connector(string downloadLocation, Logger? logger)
{
this.downloadLocation = downloadLocation;
this.downloadClient = new DownloadClient(downloadDelay);
this.logger = logger;
this.downloadClient = new DownloadClient(new Dictionary<byte, int>()
{
//RequestTypes for RateLimits
}, logger);
}
public abstract string name { get; } //Name of the Connector (e.g. Website)
@ -85,6 +88,7 @@ public abstract class Connector
new XElement("Tags", string.Join(',',publication.tags)),
new XElement("LanguageISO", publication.originalLanguage),
new XElement("Title", chapter.name),
new XElement("Writer", publication.author),
new XElement("Volume", chapter.volumeNumber),
new XElement("Number", chapter.chapterNumber) //TODO check if this is correct at some point
);
@ -106,7 +110,7 @@ public abstract class Connector
/// <returns>Filepath</returns>
protected string CreateFullFilepath(Publication publication, Chapter chapter)
{
return Path.Join(downloadLocation, publication.folderName, chapter.fileName);
return Path.Join(downloadLocation, publication.folderName, $"{chapter.fileName}.cbz");
}
/// <summary>
@ -115,9 +119,10 @@ public abstract class Connector
/// <param name="imageUrl"></param>
/// <param name="fullPath"></param>
/// <param name="downloadClient">DownloadClient of the connector</param>
protected static void DownloadImage(string imageUrl, string fullPath, DownloadClient downloadClient)
/// <param name="requestType">Requesttype for ratelimit</param>
protected static void DownloadImage(string imageUrl, string fullPath, DownloadClient downloadClient, byte requestType)
{
DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(imageUrl);
DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(imageUrl, requestType);
byte[] buffer = new byte[requestResult.result.Length];
requestResult.result.ReadExactly(buffer, 0, buffer.Length);
File.WriteAllBytes(fullPath, buffer);
@ -130,17 +135,16 @@ public abstract class Connector
/// <param name="saveArchiveFilePath">Full path to save archive to (without file ending .cbz)</param>
/// <param name="downloadClient">DownloadClient of the connector</param>
/// <param name="comicInfoPath">Path of the generate Chapter ComicInfo.xml, if it was generated</param>
protected static void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, DownloadClient downloadClient, Logger? logger, string? comicInfoPath = null)
/// <param name="requestType">RequestType for RateLimits</param>
protected static void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, DownloadClient downloadClient, byte requestType, Logger? logger, string? comicInfoPath = null)
{
logger?.WriteLine("Connector", "Downloading Images");
//Check if Publication Directory already exists
string[] splitPath = saveArchiveFilePath.Split(Path.DirectorySeparatorChar);
string directoryPath = Path.Combine(splitPath.Take(splitPath.Length - 1).ToArray());
string directoryPath = Path.GetDirectoryName(saveArchiveFilePath)!;
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);
string fullPath = $"{saveArchiveFilePath}.cbz";
if (File.Exists(fullPath)) //Don't download twice.
if (File.Exists(saveArchiveFilePath)) //Don't download twice.
return;
//Create a temporary folder to store images
@ -152,7 +156,7 @@ public abstract class Connector
{
string[] split = imageUrl.Split('.');
string extension = split[^1];
DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), downloadClient);
DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), downloadClient, requestType);
}
if(comicInfoPath is not null)
@ -160,40 +164,72 @@ public abstract class Connector
logger?.WriteLine("Connector", "Creating archive");
//ZIP-it and ship-it
ZipFile.CreateFromDirectory(tempFolder, fullPath);
ZipFile.CreateFromDirectory(tempFolder, saveArchiveFilePath);
Directory.Delete(tempFolder, true); //Cleanup
}
protected class DownloadClient
{
private readonly TimeSpan _requestSpeed;
private DateTime _lastRequest;
private static readonly HttpClient Client = new();
private readonly Dictionary<byte, DateTime> _lastExecutedRateLimit;
private readonly Dictionary<byte, TimeSpan> _rateLimit;
private Logger? logger;
/// <summary>
/// Creates a httpClient
/// </summary>
/// <param name="delay">minimum delay between requests (to avoid spam)</param>
public DownloadClient(uint delay)
/// <param name="rateLimitRequestsPerMinute">Rate limits for requests. byte is RequestType, int maximum requests per minute for RequestType</param>
public DownloadClient(Dictionary<byte, int> rateLimitRequestsPerMinute, Logger? logger)
{
_requestSpeed = TimeSpan.FromMilliseconds(delay);
_lastRequest = DateTime.Now.Subtract(_requestSpeed);
this.logger = logger;
_lastExecutedRateLimit = new();
_rateLimit = new();
foreach(KeyValuePair<byte, int> limit in rateLimitRequestsPerMinute)
_rateLimit.Add(limit.Key, TimeSpan.FromMinutes(1).Divide(limit.Value));
}
/// <summary>
/// Request Webpage
/// </summary>
/// <param name="url"></param>
/// <param name="requestType">For RateLimits: Same Endpoints use same type</param>
/// <returns>RequestResult with StatusCode and Stream of received data</returns>
public RequestResult MakeRequest(string url)
public RequestResult MakeRequest(string url, byte requestType)
{
while((DateTime.Now - _lastRequest) < _requestSpeed)
Thread.Sleep(10);
_lastRequest = DateTime.Now;
if (_rateLimit.TryGetValue(requestType, out TimeSpan value))
_lastExecutedRateLimit.TryAdd(requestType, DateTime.Now.Subtract(value));
else
{
logger?.WriteLine(this.GetType().ToString(), "RequestType not configured for rate-limit.");
return new RequestResult(HttpStatusCode.NotAcceptable, Stream.Null);
}
TimeSpan rateLimitTimeout = _rateLimit[requestType]
.Subtract(DateTime.Now.Subtract(_lastExecutedRateLimit[requestType]));
if(rateLimitTimeout > TimeSpan.Zero)
Thread.Sleep(rateLimitTimeout);
HttpResponseMessage? response = null;
while (response is null)
{
try
{
HttpRequestMessage requestMessage = new(HttpMethod.Get, url);
HttpResponseMessage response = Client.Send(requestMessage);
_lastExecutedRateLimit[requestType] = DateTime.Now;
response = Client.Send(requestMessage);
}
catch (HttpRequestException e)
{
logger?.WriteLine(this.GetType().ToString(), e.Message);
Thread.Sleep(_rateLimit[requestType] * 2);
}
}
Stream resultString = response.IsSuccessStatusCode ? response.Content.ReadAsStream() : Stream.Null;
if (!response.IsSuccessStatusCode)
logger?.WriteLine(this.GetType().ToString(), $"Request-Error {response.StatusCode}: {response.ReasonPhrase}");
return new RequestResult(response.StatusCode, resultString);
}

View File

@ -9,14 +9,26 @@ public class MangaDex : Connector
{
public override string name { get; }
public MangaDex(string downloadLocation, uint downloadDelay, Logger? logger) : base(downloadLocation, downloadDelay, logger)
private enum RequestType : byte
{
name = "MangaDex";
Manga,
Feed,
AtHomeServer,
Cover,
Author
}
public MangaDex(string downloadLocation, Logger? logger) : base(downloadLocation, 750, logger)
public MangaDex(string downloadLocation, Logger? logger) : base(downloadLocation, logger)
{
name = "MangaDex";
this.downloadClient = new DownloadClient(new Dictionary<byte, int>()
{
{(byte)RequestType.Manga, 250},
{(byte)RequestType.Feed, 250},
{(byte)RequestType.AtHomeServer, 40},
{(byte)RequestType.Cover, 250},
{(byte)RequestType.Author, 250}
}, logger);
}
public override Publication[] GetPublications(string publicationTitle = "")
@ -31,7 +43,7 @@ public class MangaDex : Connector
//Request next Page
DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(
$"https://api.mangadex.org/manga?limit={limit}&title={publicationTitle}&offset={offset}");
$"https://api.mangadex.org/manga?limit={limit}&title={publicationTitle}&offset={offset}", (byte)RequestType.Manga);
if (requestResult.statusCode != HttpStatusCode.OK)
break;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -49,6 +61,8 @@ public class MangaDex : Connector
JsonObject manga = (JsonObject)mangeNode!;
JsonObject attributes = manga["attributes"]!.AsObject();
string publicationId = manga["id"]!.GetValue<string>();
string title = attributes["title"]!.AsObject().ContainsKey("en") && attributes["title"]!["en"] is not null
? attributes["title"]!["en"]!.GetValue<string>()
: attributes["title"]![((IDictionary<string, JsonNode?>)attributes["title"]!.AsObject()).Keys.First()]!.GetValue<string>();
@ -75,12 +89,16 @@ public class MangaDex : Connector
tags.Add(tagObject["attributes"]!["name"]!["en"]!.GetValue<string>());
}
string? poster = null;
string? posterId = null;
string? authorId = null;
if (manga.ContainsKey("relationships") && manga["relationships"] is not null)
{
JsonArray relationships = manga["relationships"]!.AsArray();
poster = relationships.FirstOrDefault(relationship => relationship!["type"]!.GetValue<string>() == "cover_art")!["id"]!.GetValue<string>();
posterId = relationships.FirstOrDefault(relationship => relationship!["type"]!.GetValue<string>() == "cover_art")!["id"]!.GetValue<string>();
authorId = relationships.FirstOrDefault(relationship => relationship!["type"]!.GetValue<string>() == "author")!["id"]!.GetValue<string>();
}
string? coverUrl = GetCoverUrl(publicationId, posterId);
string? author = GetAuthor(authorId);
Dictionary<string, string> linksDict = new();
if (attributes.ContainsKey("links") && attributes["links"] is not null)
@ -102,17 +120,18 @@ public class MangaDex : Connector
string status = attributes["status"]!.GetValue<string>();
Publication pub = new Publication(
Publication pub = new (
title,
author,
description,
altTitlesDict,
tags.ToArray(),
poster,
coverUrl,
linksDict,
year,
originalLanguage,
status,
manga["id"]!.GetValue<string>()
publicationId
);
publications.Add(pub); //Add Publication (Manga) to result
}
@ -134,7 +153,7 @@ public class MangaDex : Connector
//Request next "Page"
DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest(
$"https://api.mangadex.org/manga/{publication.downloadUrl}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}");
$"https://api.mangadex.org/manga/{publication.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", (byte)RequestType.Feed);
if (requestResult.statusCode != HttpStatusCode.OK)
break;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -181,7 +200,7 @@ public class MangaDex : Connector
logger?.WriteLine(this.GetType().ToString(), $"Download Chapter {publication.sortName} {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'");
downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer);
if (requestResult.statusCode != HttpStatusCode.OK)
return;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -200,7 +219,47 @@ public class MangaDex : Connector
File.WriteAllText(comicInfoPath, CreateComicInfo(publication, chapter, logger));
//Download Chapter-Images
DownloadChapterImages(imageUrls.ToArray(), CreateFullFilepath(publication, chapter), downloadClient, logger, comicInfoPath);
DownloadChapterImages(imageUrls.ToArray(), CreateFullFilepath(publication, chapter), downloadClient, (byte)RequestType.AtHomeServer, logger, comicInfoPath);
}
private string? GetCoverUrl(string publicationId, string? posterId)
{
if (posterId is null)
{
logger?.WriteLine(this.GetType().ToString(), $"No posterId");
return null;
}
//Request information where to download Cover
DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest($"https://api.mangadex.org/cover/{posterId}", (byte)RequestType.Cover);
if (requestResult.statusCode != HttpStatusCode.OK)
return null;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
if (result is null)
return null;
string fileName = result["data"]!["attributes"]!["fileName"]!.GetValue<string>();
string coverUrl = $"https://uploads.mangadex.org/covers/{publicationId}/{fileName}";
return coverUrl;
}
private string? GetAuthor(string? authorId)
{
if (authorId is null)
return null;
DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest($"https://api.mangadex.org/author/{authorId}", (byte)RequestType.Author);
if (requestResult.statusCode != HttpStatusCode.OK)
return null;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
if (result is null)
return null;
string author = result["data"]!["attributes"]!["name"]!.GetValue<string>();
return author;
}
public override void DownloadCover(Publication publication)
@ -211,34 +270,26 @@ public class MangaDex : Connector
if(!Directory.Exists(publicationFolder))
Directory.CreateDirectory(publicationFolder);
DirectoryInfo dirInfo = new (publicationFolder);
foreach(FileInfo fileInfo in dirInfo.EnumerateFiles())
if (fileInfo.Name.Contains("cover."))
if (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover.")))
{
logger?.WriteLine(this.GetType().ToString(), $"Cover exists {publication.sortName}");
return;
}
//Request information where to download Cover
DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest($"https://api.mangadex.org/cover/{publication.posterUrl}");
if (requestResult.statusCode != HttpStatusCode.OK)
if (publication.posterUrl is null || publication.posterUrl!.Contains("http"))
{
logger?.WriteLine(this.GetType().ToString(), $"No Poster-URL in publication");
return;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
if (result is null)
return;
string fileName = result["data"]!["attributes"]!["fileName"]!.GetValue<string>();
string coverUrl = $"https://uploads.mangadex.org/covers/{publication.downloadUrl}/{fileName}";
}
//Get file-extension (jpg, png)
string[] split = coverUrl.Split('.');
string[] split = publication.posterUrl.Split('.');
string extension = split[^1];
string outFolderPath = Path.Join(downloadLocation, publication.folderName);
Directory.CreateDirectory(outFolderPath);
//Download cover-Image
DownloadImage(coverUrl, Path.Join(downloadLocation, publication.folderName, $"cover.{extension}"), this.downloadClient);
DownloadImage(publication.posterUrl, Path.Join(downloadLocation, publication.folderName, $"cover.{extension}"), this.downloadClient, (byte)RequestType.AtHomeServer);
}
}

View File

@ -9,7 +9,7 @@ namespace Tranga;
public readonly struct Publication
{
public string sortName { get; }
// ReSharper disable UnusedAutoPropertyAccessor.Global we need it, trust
public string? author { get; }
public Dictionary<string,string> altTitles { get; }
// ReSharper disable trice MemberCanBePrivate.Global, trust
public string? description { get; }
@ -20,12 +20,13 @@ public readonly struct Publication
public string? originalLanguage { get; }
public string status { get; }
public string folderName { get; }
public string downloadUrl { get; }
[JsonIgnore]public string internalId { get; }
public string publicationId { get; }
public string internalId { get; }
public Publication(string sortName, string? description, Dictionary<string,string> altTitles, string[] tags, string? posterUrl, Dictionary<string,string>? links, int? year, string? originalLanguage, string status, string downloadUrl)
public Publication(string sortName, string? author, string? description, Dictionary<string,string> altTitles, string[] tags, string? posterUrl, Dictionary<string,string>? links, int? year, string? originalLanguage, string status, string publicationId)
{
this.sortName = sortName;
this.author = author;
this.description = description;
this.altTitles = altTitles;
this.tags = tags;
@ -34,10 +35,10 @@ public readonly struct Publication
this.year = year;
this.originalLanguage = originalLanguage;
this.status = status;
this.downloadUrl = downloadUrl;
this.publicationId = publicationId;
this.folderName = string.Concat(sortName.Split(Path.GetInvalidPathChars().Concat(Path.GetInvalidFileNameChars()).ToArray()));
string onlyLowerAscii = this.sortName.ToLower().Where(Char.IsAscii).ToString()!;
this.internalId = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{onlyLowerAscii}{this.year}"));
string onlyLowerLetters = string.Concat(this.sortName.ToLower().Where(Char.IsLetter));
this.internalId = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{onlyLowerLetters}{this.year}"));
}
/// <returns>Serialized JSON String for series.json</returns>

View File

@ -92,13 +92,13 @@ public static class TaskExecutor
/// <param name="chapterCollection"></param>
private static void DownloadNewChapters(Connector connector, Publication publication, string language, ref Dictionary<Publication, List<Chapter>> chapterCollection)
{
List<Chapter> newChapters = UpdateChapters(connector, publication, language, ref chapterCollection);
connector.DownloadCover(publication);
//Check if Publication already has a Folder and a series.json
//Check if Publication already has a Folder
string publicationFolder = Path.Join(connector.downloadLocation, publication.folderName);
if(!Directory.Exists(publicationFolder))
Directory.CreateDirectory(publicationFolder);
List<Chapter> newChapters = UpdateChapters(connector, publication, language, ref chapterCollection);
connector.DownloadCover(publication);
string seriesInfoPath = Path.Join(publicationFolder, "series.json");
if(!File.Exists(seriesInfoPath))

View File

@ -25,7 +25,7 @@ public class TaskManager
/// <param name="komgaUsername">The Komga username</param>
/// <param name="komgaPassword">The Komga password</param>
/// <param name="logger"></param>
public TaskManager(string downloadFolderPath, string? workingDirectory = null, string? komgaBaseUrl = null, string? komgaUsername = null, string? komgaPassword = null, Logger? logger = null)
public TaskManager(string downloadFolderPath, string workingDirectory, string? komgaBaseUrl = null, string? komgaUsername = null, string? komgaPassword = null, Logger? logger = null)
{
this.logger = logger;
_allTasks = new HashSet<TrangaTask>();
@ -163,12 +163,14 @@ public class TaskManager
//Check if same task already exists
if (!_allTasks.Any(trangaTask => trangaTask.task == task && trangaTask.connectorName == connector.name &&
trangaTask.publication?.downloadUrl == publication?.downloadUrl))
trangaTask.publication?.internalId == publication?.internalId))
{
if(task != TrangaTask.Task.UpdatePublications)
_chapterCollection.TryAdd((Publication)publication!, new List<Chapter>());
_allTasks.Add(newTask);
}
else
logger?.WriteLine(this.GetType().ToString(), $"Publication already exists {publication?.internalId}");
}
logger?.WriteLine(this.GetType().ToString(), $"Added new Task {newTask.ToString()}");
ExportData();
@ -188,18 +190,25 @@ public class TaskManager
if (task == TrangaTask.Task.UpdateKomgaLibrary)
{
_allTasks.RemoveWhere(uTask => uTask.task == TrangaTask.Task.UpdateKomgaLibrary);
logger?.WriteLine(this.GetType().ToString(), $"Removed Task {task}");
logger?.WriteLine(this.GetType().ToString(), $"Removed Task {task} from all Tasks.");
}
else if (connectorName is null)
throw new ArgumentException($"connectorName can not be null for Task {task}");
else
{
foreach (List<TrangaTask> taskQueue in this._taskQueue.Values)
if(taskQueue.RemoveAll(trangaTask =>
trangaTask.task == task && trangaTask.connectorName == connectorName &&
trangaTask.publication?.internalId == publication?.internalId) > 0)
logger?.WriteLine(this.GetType().ToString(), $"Removed Task {task} {publication?.sortName} {publication?.internalId} from Queue.");
else
logger?.WriteLine(this.GetType().ToString(), $"Task {task} {publication?.sortName} {publication?.internalId} was not in Queue.");
if(_allTasks.RemoveWhere(trangaTask =>
trangaTask.task == task && trangaTask.connectorName == connectorName &&
trangaTask.publication?.downloadUrl == publication?.downloadUrl) > 0)
logger?.WriteLine(this.GetType().ToString(), $"Removed Task {task} {publication?.sortName} {publication?.downloadUrl}.");
trangaTask.publication?.internalId == publication?.internalId) > 0)
logger?.WriteLine(this.GetType().ToString(), $"Removed Task {task} {publication?.sortName} {publication?.internalId} from all Tasks.");
else
logger?.WriteLine(this.GetType().ToString(), $"No Task {task} {publication?.sortName} {publication?.downloadUrl} could be found.");
logger?.WriteLine(this.GetType().ToString(), $"No Task {task} {publication?.sortName} {publication?.internalId} could be found.");
}
ExportData();
}

View File

@ -56,6 +56,6 @@ public class TrangaTask
public override string ToString()
{
return $"{task,-20} | {lastExecuted,-20} | {reoccurrence,-12} | {state,-10} | {connectorName,-15} | {publication?.sortName}";
return $"{task}, {lastExecuted}, {reoccurrence}, {state} {(connectorName is not null ? $", {connectorName}" : "" )} {(publication is not null ? $", {publication?.sortName}": "")}";
}
}