mirror of
https://github.com/C9Glax/tranga.git
synced 2025-06-14 15:27:53 +02:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
eddf50483f | |||
a71d65e666 | |||
9a640aed27 | |||
30b6c4680b | |||
7b6253de0f | |||
5aa3214ce5 | |||
9b70994f71 | |||
93cf341f2d | |||
01cb74c088 | |||
ec480dffad | |||
b7014cbff5 | |||
0cab921402 | |||
0e0ba1796e | |||
27d8565dc1 | |||
79dc44d707 | |||
bb6a0ad0d4 | |||
43db463ba6 | |||
9eb8ddbc40 | |||
972cba69ec | |||
962fe9529e | |||
da1b0cb1cd | |||
7f88e57e47 | |||
8865bf284f | |||
5fc2de5fcb | |||
4bae223d95 | |||
0486168b43 | |||
b64ab5c6d4 | |||
578fa5e6be | |||
4d33e78123 | |||
52ac3e4e4e |
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
|
||||
using Logging;
|
||||
using Tranga;
|
||||
|
||||
string applicationFolderPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Tranga-API");
|
||||
string applicationFolderPath =
|
||||
Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Tranga-API");
|
||||
string logsFolderPath = Path.Join(applicationFolderPath, "logs");
|
||||
string logFilePath = Path.Join(logsFolderPath, $"log-{DateTime.Now:dd-M-yyyy-HH-mm-ss}.txt");
|
||||
string settingsFilePath = Path.Join(applicationFolderPath, "data.json");
|
||||
string settingsFilePath = Path.Join(applicationFolderPath, "settings.json");
|
||||
|
||||
Directory.CreateDirectory(applicationFolderPath);
|
||||
Directory.CreateDirectory(logsFolderPath);
|
||||
@ -16,11 +16,11 @@ Console.WriteLine($"Settings-File-Path: {settingsFilePath}");
|
||||
Logger logger = new(new[] { Logger.LoggerType.FileLogger }, null, null, logFilePath);
|
||||
|
||||
logger.WriteLine("Tranga_CLI", "Loading Taskmanager.");
|
||||
TaskManager.SettingsData settings;
|
||||
TrangaSettings settings;
|
||||
if (File.Exists(settingsFilePath))
|
||||
settings = TaskManager.LoadData(settingsFilePath);
|
||||
settings = TrangaSettings.LoadSettings(settingsFilePath);
|
||||
else
|
||||
settings = new TaskManager.SettingsData(Directory.GetCurrentDirectory(), settingsFilePath, null, new HashSet<TrangaTask>());
|
||||
settings = new TrangaSettings(Directory.GetCurrentDirectory(), applicationFolderPath, null);
|
||||
|
||||
TaskManager taskManager = new (settings, logger);
|
||||
|
||||
@ -58,7 +58,7 @@ app.MapPost("/Tasks/Create", (string taskType, string? connectorName, string? pu
|
||||
taskManager.AddTask(task, connectorName, publication, TimeSpan.Parse(reoccurrenceTime), language??"");
|
||||
});
|
||||
|
||||
app.MapPost("/Tasks/Delete", (string taskType, string? connectorName, string? publicationId) =>
|
||||
app.MapDelete("/Tasks/Delete", (string taskType, string? connectorName, string? publicationId) =>
|
||||
{
|
||||
Publication? publication = taskManager.GetAllPublications().FirstOrDefault(pub => pub.internalId == publicationId);
|
||||
TrangaTask.Task task = Enum.Parse<TrangaTask.Task>(taskType);
|
||||
@ -93,7 +93,7 @@ app.MapPost("/Queue/Enqueue", (string taskType, string? connectorName, string? p
|
||||
taskManager.AddTaskToQueue(task);
|
||||
});
|
||||
|
||||
app.MapPost("/Queue/Dequeue", (string taskType, string? connectorName, string? publicationId) =>
|
||||
app.MapDelete("/Queue/Dequeue", (string taskType, string? connectorName, string? publicationId) =>
|
||||
{
|
||||
TrangaTask.Task pTask = Enum.Parse<TrangaTask.Task>(taskType);
|
||||
TrangaTask? task = taskManager.GetAllTasks().FirstOrDefault(tTask =>
|
||||
@ -103,20 +103,8 @@ app.MapPost("/Queue/Dequeue", (string taskType, string? connectorName, string? p
|
||||
taskManager.RemoveTaskFromQueue(task);
|
||||
});
|
||||
|
||||
app.MapGet("/Settings/Get", () => new Settings(taskManager.settings));
|
||||
app.MapGet("/Settings/Get", () => taskManager.settings);
|
||||
|
||||
app.MapPost("/Settings/Update", (string? downloadLocation, string? komgaUrl, string? komgaAuth) => taskManager.UpdateSettings(downloadLocation, komgaUrl, komgaAuth) );
|
||||
|
||||
app.Run();
|
||||
|
||||
class Settings
|
||||
{
|
||||
public string downloadLocation { get; }
|
||||
public Komga? komga { get; }
|
||||
|
||||
public Settings(TaskManager.SettingsData settings)
|
||||
{
|
||||
this.downloadLocation = settings.downloadLocation;
|
||||
this.komga = settings.komga;
|
||||
}
|
||||
}
|
@ -14,10 +14,10 @@ public static class Tranga_Cli
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
string applicationFolderPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Tranga");
|
||||
string applicationFolderPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Tranga");
|
||||
string logsFolderPath = Path.Join(applicationFolderPath, "logs");
|
||||
string logFilePath = Path.Join(logsFolderPath, $"log-{DateTime.Now:dd-M-yyyy-HH-mm-ss}.txt");
|
||||
string settingsFilePath = Path.Join(applicationFolderPath, "data.json");
|
||||
string settingsFilePath = Path.Join(applicationFolderPath, "settings.json");
|
||||
|
||||
Directory.CreateDirectory(applicationFolderPath);
|
||||
Directory.CreateDirectory(logsFolderPath);
|
||||
@ -28,11 +28,11 @@ public static class Tranga_Cli
|
||||
Logger logger = new(new[] { Logger.LoggerType.FileLogger }, null, null, logFilePath);
|
||||
|
||||
logger.WriteLine("Tranga_CLI", "Loading Taskmanager.");
|
||||
TaskManager.SettingsData settings;
|
||||
TrangaSettings settings;
|
||||
if (File.Exists(settingsFilePath))
|
||||
settings = TaskManager.LoadData(settingsFilePath);
|
||||
settings = TrangaSettings.LoadSettings(settingsFilePath);
|
||||
else
|
||||
settings = new TaskManager.SettingsData(Directory.GetCurrentDirectory(), settingsFilePath, null, new HashSet<TrangaTask>());
|
||||
settings = new TrangaSettings(Directory.GetCurrentDirectory(), applicationFolderPath, null);
|
||||
|
||||
|
||||
logger.WriteLine("Tranga_CLI", "User Input");
|
||||
@ -40,8 +40,8 @@ public static class Tranga_Cli
|
||||
string? tmpPath = Console.ReadLine();
|
||||
while(tmpPath is null)
|
||||
tmpPath = Console.ReadLine();
|
||||
if(tmpPath.Length > 0)
|
||||
settings.UpdateSettings(pDownloadLocation: tmpPath, null);
|
||||
if (tmpPath.Length > 0)
|
||||
settings.downloadLocation = tmpPath;
|
||||
|
||||
Console.WriteLine($"Komga BaseURL [{settings.komga?.baseUrl}]:");
|
||||
string? tmpUrl = Console.ReadLine();
|
||||
@ -74,14 +74,14 @@ public static class Tranga_Cli
|
||||
}
|
||||
} while (key != ConsoleKey.Enter);
|
||||
|
||||
settings.UpdateSettings(null, new Komga(tmpUrl, tmpUser, tmpPass, logger));
|
||||
settings.komga = new Komga(tmpUrl, tmpUser, tmpPass, logger);
|
||||
}
|
||||
|
||||
logger.WriteLine("Tranga_CLI", "Loaded.");
|
||||
TaskMode(settings, logger);
|
||||
}
|
||||
|
||||
private static void TaskMode(TaskManager.SettingsData settings, Logger logger)
|
||||
private static void TaskMode(TrangaSettings settings, Logger logger)
|
||||
{
|
||||
TaskManager taskManager = new (settings, logger);
|
||||
ConsoleKey selection = ConsoleKey.EraseEndOfFile;
|
||||
@ -143,7 +143,7 @@ public static class Tranga_Cli
|
||||
TailLog(logger);
|
||||
Console.ReadKey();
|
||||
break;
|
||||
case ConsoleKey.M:
|
||||
case ConsoleKey.G:
|
||||
RemoveTaskFromQueue(taskManager, logger);
|
||||
Console.WriteLine("Press any key.");
|
||||
Console.ReadKey();
|
||||
@ -153,6 +153,11 @@ public static class Tranga_Cli
|
||||
Console.WriteLine("Press any key.");
|
||||
Console.ReadKey();
|
||||
break;
|
||||
case ConsoleKey.M:
|
||||
AddMangaTaskToQueue(taskManager, logger);
|
||||
Console.WriteLine("Press any key.");
|
||||
Console.ReadKey();
|
||||
break;
|
||||
}
|
||||
PrintMenu(taskManager, taskManager.settings.downloadLocation, logger);
|
||||
}
|
||||
@ -186,8 +191,8 @@ public static class Tranga_Cli
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"{"C: Create Task",-30}{"L: List tasks",-30}{"B: Enqueue Task", -30}");
|
||||
Console.WriteLine($"{"D: Delete Task",-30}{"S: Search Tasks", -30}{"K: List Task Queue", -30}");
|
||||
Console.WriteLine($"{"E: Execute Task now",-30}{"R: List Running Tasks", -30}{"M: Remove Task from Queue", -30}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"{"E: Execute Task now",-30}{"R: List Running Tasks", -30}{"G: Remove Task from Queue", -30}");
|
||||
Console.WriteLine($"{"M: New Download Manga Task",-30}{"", -30}{"", -30}");
|
||||
Console.WriteLine($"{"",-30}{"F: Show Log",-30}{"Q: Exit",-30}");
|
||||
}
|
||||
|
||||
@ -204,8 +209,12 @@ public static class Tranga_Cli
|
||||
$"{"",-5}{"Task",-20} | {"Last Executed",-20} | {"Reoccurrence",-12} | {"State",-10} | {"Connector",-15} | Publication/Manga";
|
||||
Console.WriteLine(header);
|
||||
Console.WriteLine(new string('-', header.Length));
|
||||
foreach(TrangaTask trangaTask in tasks)
|
||||
Console.WriteLine($"{tIndex++:000}: {trangaTask}");
|
||||
foreach (TrangaTask trangaTask in tasks)
|
||||
{
|
||||
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)
|
||||
@ -239,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)
|
||||
@ -251,6 +259,25 @@ public static class Tranga_Cli
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void AddMangaTaskToQueue(TaskManager taskManager, Logger logger)
|
||||
{
|
||||
Console.Clear();
|
||||
logger.WriteLine("Tranga_CLI", "Menu: Add Manga Download to queue");
|
||||
|
||||
Connector? connector = SelectConnector(taskManager.settings.downloadLocation, taskManager.GetAvailableConnectors().Values.ToArray(), logger);
|
||||
if (connector is null)
|
||||
return;
|
||||
|
||||
Publication? publication = SelectPublication(taskManager, connector!, logger);
|
||||
if (publication is null)
|
||||
return;
|
||||
|
||||
TimeSpan reoccurrence = SelectReoccurrence(logger);
|
||||
logger.WriteLine("Tranga_CLI", "Sending Task to TaskManager");
|
||||
TrangaTask newTask = taskManager.AddTask(TrangaTask.Task.DownloadNewChapters, connector?.name, publication, reoccurrence, "en");
|
||||
Console.WriteLine(newTask);
|
||||
}
|
||||
|
||||
private static void AddTaskToQueue(TaskManager taskManager, Logger logger)
|
||||
{
|
||||
Console.Clear();
|
||||
@ -300,7 +327,7 @@ public static class Tranga_Cli
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateTask(TaskManager taskManager, TaskManager.SettingsData settings, Logger logger)
|
||||
private static void CreateTask(TaskManager taskManager, TrangaSettings settings, Logger logger)
|
||||
{
|
||||
logger.WriteLine("Tranga_CLI", "Menu: Creating Task");
|
||||
TrangaTask.Task? tmpTask = SelectTaskType(logger);
|
||||
@ -319,7 +346,7 @@ public static class Tranga_Cli
|
||||
Publication? publication = null;
|
||||
if (task != TrangaTask.Task.UpdatePublications && task != TrangaTask.Task.UpdateKomgaLibrary)
|
||||
{
|
||||
publication = SelectPublication(connector!, logger);
|
||||
publication = SelectPublication(taskManager, connector!, logger);
|
||||
if (publication is null)
|
||||
return;
|
||||
}
|
||||
@ -443,7 +470,7 @@ public static class Tranga_Cli
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Publication? SelectPublication(Connector connector, Logger logger)
|
||||
private static Publication? SelectPublication(TaskManager taskManager, Connector connector, Logger logger)
|
||||
{
|
||||
logger.WriteLine("Tranga_CLI", "Menu: Select Publication");
|
||||
|
||||
@ -452,7 +479,14 @@ public static class Tranga_Cli
|
||||
Console.WriteLine("Publication search query (leave empty for all):");
|
||||
string? query = Console.ReadLine();
|
||||
|
||||
Publication[] publications = connector.GetPublications(query ?? "");
|
||||
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:");
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
HttpRequestMessage requestMessage = new(HttpMethod.Get, url);
|
||||
HttpResponseMessage response = Client.Send(requestMessage);
|
||||
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);
|
||||
_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);
|
||||
}
|
||||
|
||||
|
@ -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,15 +89,18 @@ 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();
|
||||
string[,]? links = null;
|
||||
if (attributes.ContainsKey("links") && attributes["links"] is not null)
|
||||
{
|
||||
JsonObject linksObject = attributes["links"]!.AsObject();
|
||||
@ -103,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
|
||||
}
|
||||
@ -135,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);
|
||||
@ -182,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);
|
||||
@ -201,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)
|
||||
@ -212,31 +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."))
|
||||
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 (dirInfo.EnumerateFiles().Any(info => info.Name.Contains("cover.")))
|
||||
{
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Cover exists {publication.sortName}");
|
||||
return;
|
||||
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
|
||||
if (result is null)
|
||||
}
|
||||
|
||||
if (publication.posterUrl is null || publication.posterUrl!.Contains("http"))
|
||||
{
|
||||
logger?.WriteLine(this.GetType().ToString(), $"No Poster-URL in publication");
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Tranga;
|
||||
|
||||
@ -8,28 +9,24 @@ namespace Tranga;
|
||||
public readonly struct Publication
|
||||
{
|
||||
public string sortName { get; }
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global we need it, trust
|
||||
[JsonIgnore]public Dictionary<string,string> altTitles { get; }
|
||||
public string? author { get; }
|
||||
public Dictionary<string,string> altTitles { get; }
|
||||
// ReSharper disable trice MemberCanBePrivate.Global, trust
|
||||
public string? description { get; }
|
||||
public string[] tags { get; }
|
||||
public string? posterUrl { get; }
|
||||
[JsonIgnore]public Dictionary<string,string> links { get; }
|
||||
public Dictionary<string,string> links { get; }
|
||||
public int? year { get; }
|
||||
public string? originalLanguage { get; }
|
||||
public string status { get; }
|
||||
public string folderName { get; }
|
||||
public string downloadUrl { get; }
|
||||
public string publicationId { get; }
|
||||
public string internalId { get; }
|
||||
|
||||
public readonly struct ValueTuple
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
@ -38,9 +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()));
|
||||
this.internalId = Guid.NewGuid().ToString();
|
||||
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>
|
||||
|
@ -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))
|
||||
|
@ -11,28 +11,30 @@ namespace Tranga;
|
||||
public class TaskManager
|
||||
{
|
||||
public Dictionary<Publication, List<Chapter>> _chapterCollection = new();
|
||||
private readonly HashSet<TrangaTask> _allTasks;
|
||||
private HashSet<TrangaTask> _allTasks;
|
||||
private bool _continueRunning = true;
|
||||
private readonly Connector[] _connectors;
|
||||
private readonly Dictionary<Connector, List<TrangaTask>> _taskQueue = new();
|
||||
public SettingsData settings { get; }
|
||||
public TrangaSettings settings { get; }
|
||||
private Logger? logger { get; }
|
||||
public Komga? komga { get; }
|
||||
public Komga? komga => settings.komga;
|
||||
|
||||
/// <param name="downloadFolderPath">Local path to save data (Manga) to</param>
|
||||
/// <param name="settingsFilePath">Path to the settings file (data.json)</param>
|
||||
/// <param name="workingDirectory">Path to the working directory</param>
|
||||
/// <param name="komgaBaseUrl">The Url of the Komga-instance that you want to update</param>
|
||||
/// <param name="komgaUsername">The Komga username</param>
|
||||
/// <param name="komgaPassword">The Komga password</param>
|
||||
/// <param name="logger"></param>
|
||||
public TaskManager(string downloadFolderPath, string? settingsFilePath = 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>();
|
||||
if (komgaBaseUrl != null && komgaUsername != null && komgaPassword != null)
|
||||
this.komga = new Komga(komgaBaseUrl, komgaUsername, komgaPassword, logger);
|
||||
|
||||
this.settings = new SettingsData(downloadFolderPath, settingsFilePath, this.komga, this._allTasks);
|
||||
Komga? newKomga = null;
|
||||
if (komgaBaseUrl != null && komgaUsername != null && komgaPassword != null)
|
||||
newKomga = new Komga(komgaBaseUrl, komgaUsername, komgaPassword, logger);
|
||||
|
||||
this.settings = new TrangaSettings(downloadFolderPath, workingDirectory, newKomga);
|
||||
ExportData();
|
||||
|
||||
this._connectors = new Connector[]{ new MangaDex(downloadFolderPath, logger) };
|
||||
@ -48,21 +50,22 @@ public class TaskManager
|
||||
Komga? komga = null;
|
||||
if (komgaUrl is not null && komgaAuth is not null)
|
||||
komga = new Komga(komgaUrl, komgaAuth, null);
|
||||
settings.UpdateSettings(downloadLocation, komga);
|
||||
settings.downloadLocation = downloadLocation ?? settings.downloadLocation;
|
||||
settings.komga = komga ?? komga;
|
||||
ExportData();
|
||||
}
|
||||
|
||||
public TaskManager(SettingsData settings, Logger? logger = null)
|
||||
public TaskManager(TrangaSettings settings, Logger? logger = null)
|
||||
{
|
||||
this.logger = logger;
|
||||
this._connectors = new Connector[]{ new MangaDex(settings.downloadLocation, logger) };
|
||||
this.settings = settings;
|
||||
ExportData();
|
||||
|
||||
foreach(Connector cConnector in this._connectors)
|
||||
_taskQueue.Add(cConnector, new List<TrangaTask>());
|
||||
this.komga = settings.komga;
|
||||
_allTasks = settings.allTasks;
|
||||
_allTasks = new HashSet<TrangaTask>();
|
||||
|
||||
this.settings = settings;
|
||||
ImportData();
|
||||
ExportData();
|
||||
Thread taskChecker = new(TaskCheckerThread);
|
||||
taskChecker.Start();
|
||||
}
|
||||
@ -160,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();
|
||||
@ -185,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();
|
||||
}
|
||||
@ -289,19 +301,25 @@ public class TaskManager
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads stored data (settings, tasks) from file
|
||||
/// </summary>
|
||||
/// <param name="importFilePath">working directory, filename has to be data.json</param>
|
||||
public static SettingsData LoadData(string importFilePath)
|
||||
private void ImportData()
|
||||
{
|
||||
if (!File.Exists(importFilePath))
|
||||
return new SettingsData(Directory.GetCurrentDirectory(), null, null, new HashSet<TrangaTask>());
|
||||
logger?.WriteLine(this.GetType().ToString(), "Importing Data");
|
||||
string buffer;
|
||||
if (File.Exists(settings.tasksFilePath))
|
||||
{
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Importing tasks from {settings.tasksFilePath}");
|
||||
buffer = File.ReadAllText(settings.tasksFilePath);
|
||||
this._allTasks = JsonConvert.DeserializeObject<HashSet<TrangaTask>>(buffer)!;
|
||||
}
|
||||
|
||||
string toRead = File.ReadAllText(importFilePath);
|
||||
SettingsData data = JsonConvert.DeserializeObject<SettingsData>(toRead)!;
|
||||
|
||||
return data;
|
||||
if (File.Exists(settings.knownPublicationsPath))
|
||||
{
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Importing known publications from {settings.knownPublicationsPath}");
|
||||
buffer = File.ReadAllText(settings.knownPublicationsPath);
|
||||
Publication[] publications = JsonConvert.DeserializeObject<Publication[]>(buffer)!;
|
||||
foreach (Publication publication in publications)
|
||||
this._chapterCollection.TryAdd(publication, new List<Chapter>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -309,38 +327,15 @@ public class TaskManager
|
||||
/// </summary>
|
||||
private void ExportData()
|
||||
{
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Exporting data to {settings.settingsFilePath}");
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Exporting settings to {settings.settingsFilePath}");
|
||||
File.WriteAllText(settings.settingsFilePath, JsonConvert.SerializeObject(settings));
|
||||
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Exporting tasks to {settings.tasksFilePath}");
|
||||
File.WriteAllText(settings.tasksFilePath, JsonConvert.SerializeObject(this._allTasks));
|
||||
|
||||
|
||||
string serializedData = JsonConvert.SerializeObject(settings);
|
||||
|
||||
File.WriteAllText(settings.settingsFilePath, serializedData);
|
||||
logger?.WriteLine(this.GetType().ToString(), $"Exporting known publications to {settings.knownPublicationsPath}");
|
||||
File.WriteAllText(settings.knownPublicationsPath, JsonConvert.SerializeObject(this._chapterCollection.Keys.ToArray()));
|
||||
}
|
||||
|
||||
public class SettingsData
|
||||
{
|
||||
public string downloadLocation { get; private set; }
|
||||
public string settingsFilePath { get; }
|
||||
public Komga? komga { get; private set; }
|
||||
public HashSet<TrangaTask> allTasks { get; }
|
||||
|
||||
public SettingsData(string downloadLocation, string? settingsFilePath, Komga? komga, HashSet<TrangaTask> allTasks)
|
||||
{
|
||||
this.settingsFilePath = settingsFilePath ??
|
||||
Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"Tranga", "data.json");
|
||||
this.downloadLocation = downloadLocation;
|
||||
this.komga = komga;
|
||||
this.allTasks = allTasks;
|
||||
}
|
||||
|
||||
public void UpdateSettings(string? pDownloadLocation, Komga? pKomga)
|
||||
{
|
||||
if(pDownloadLocation is not null)
|
||||
this.downloadLocation = pDownloadLocation;
|
||||
if(pKomga is not null)
|
||||
this.komga = pKomga;
|
||||
}
|
||||
}
|
||||
}
|
31
Tranga/TrangaSettings.cs
Normal file
31
Tranga/TrangaSettings.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Tranga;
|
||||
|
||||
public class TrangaSettings
|
||||
{
|
||||
public string downloadLocation { get; set; }
|
||||
public string workingDirectory { get; set; }
|
||||
[JsonIgnore]public string settingsFilePath => Path.Join(workingDirectory, "settings.json");
|
||||
[JsonIgnore]public string tasksFilePath => Path.Join(workingDirectory, "tasks.json");
|
||||
[JsonIgnore]public string knownPublicationsPath => Path.Join(workingDirectory, "knownPublications.json");
|
||||
public Komga? komga { get; set; }
|
||||
|
||||
public TrangaSettings(string downloadLocation, string workingDirectory, Komga? komga)
|
||||
{
|
||||
this.workingDirectory = workingDirectory;
|
||||
this.downloadLocation = downloadLocation;
|
||||
this.komga = komga;
|
||||
}
|
||||
|
||||
public static TrangaSettings LoadSettings(string importFilePath)
|
||||
{
|
||||
if (!File.Exists(importFilePath))
|
||||
return new TrangaSettings(Path.Join(Directory.GetCurrentDirectory(), "Downloads"), Directory.GetCurrentDirectory(), null);
|
||||
|
||||
string toRead = File.ReadAllText(importFilePath);
|
||||
TrangaSettings settings = JsonConvert.DeserializeObject<TrangaSettings>(toRead)!;
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
@ -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}": "")}";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user