diff --git a/Tranga-CLI/Tranga_Cli.cs b/Tranga-CLI/Tranga_Cli.cs index b05e894..7fda55a 100644 --- a/Tranga-CLI/Tranga_Cli.cs +++ b/Tranga-CLI/Tranga_Cli.cs @@ -8,19 +8,57 @@ public static class Tranga_Cli { public static void Main(string[] args) { - string folderPath = Directory.GetCurrentDirectory(); - string settingsPath = Path.Join(Directory.GetCurrentDirectory(), "lastPath.setting"); + TaskManager.SettingsData settings; + string settingsPath = Path.Join(Directory.GetCurrentDirectory(), "data.json"); if (File.Exists(settingsPath)) - folderPath = File.ReadAllText(settingsPath); + settings = TaskManager.ImportData(Directory.GetCurrentDirectory()); + else + settings = new TaskManager.SettingsData(Directory.GetCurrentDirectory(), null, new HashSet()); + - Console.WriteLine($"Output folder path [{folderPath}]:"); + Console.WriteLine($"Output folder path [{settings.downloadLocation}]:"); string? tmpPath = Console.ReadLine(); while(tmpPath is null) tmpPath = Console.ReadLine(); if(tmpPath.Length > 0) - folderPath = tmpPath; - File.WriteAllText(settingsPath, folderPath); + settings.downloadLocation = tmpPath; + Console.WriteLine($"Komga BaseURL [{settings.komga?.baseUrl}]:"); + string? tmpUrl = Console.ReadLine(); + while (tmpUrl is null) + tmpUrl = Console.ReadLine(); + if (tmpUrl.Length > 0) + { + Console.WriteLine("Username:"); + string? tmpUser = Console.ReadLine(); + while (tmpUser is null || tmpUser.Length < 1) + tmpUser = Console.ReadLine(); + + Console.WriteLine("Password:"); + string tmpPass = string.Empty; + ConsoleKey key; + do + { + var keyInfo = Console.ReadKey(intercept: true); + key = keyInfo.Key; + + if (key == ConsoleKey.Backspace && tmpPass.Length > 0) + { + Console.Write("\b \b"); + tmpPass = tmpPass[0..^1]; + } + else if (!char.IsControl(keyInfo.KeyChar)) + { + Console.Write("*"); + tmpPass += keyInfo.KeyChar; + } + } while (key != ConsoleKey.Enter); + + settings.komga = new Komga(tmpUrl, tmpUser, tmpPass); + } + + //For now only TaskManager mode + /* Console.Write("Mode (D: Interactive only, T: TaskManager):"); ConsoleKeyInfo mode = Console.ReadKey(); while (mode.Key != ConsoleKey.D && mode.Key != ConsoleKey.T) @@ -28,14 +66,14 @@ public static class Tranga_Cli Console.WriteLine(); if(mode.Key == ConsoleKey.D) - DownloadNow(folderPath); + DownloadNow(settings); else if (mode.Key == ConsoleKey.T) - TaskMode(folderPath); + TaskMode(settings);*/ + TaskMode(settings); } - private static void TaskMode(string folderPath) + private static void TaskMode(TaskManager.SettingsData settings) { - TaskManager.SettingsData settings = TaskManager.ImportData(Directory.GetCurrentDirectory()); TaskManager taskManager = new TaskManager(settings); ConsoleKey selection = ConsoleKey.NoName; int menu = 0; @@ -50,13 +88,18 @@ public static class Tranga_Cli menu = 0; break; case 2: - Connector connector = SelectConnector(folderPath, taskManager.GetAvailableConnectors().Values.ToArray()); TrangaTask.Task task = SelectTask(); + + Connector? connector = null; + if(task != TrangaTask.Task.UpdateKomgaLibrary) + connector = SelectConnector(settings.downloadLocation, taskManager.GetAvailableConnectors().Values.ToArray()); + Publication? publication = null; - if(task != TrangaTask.Task.UpdatePublications) - publication = SelectPublication(connector); + if(task != TrangaTask.Task.UpdatePublications && task != TrangaTask.Task.UpdateKomgaLibrary) + publication = SelectPublication(connector!); + TimeSpan reoccurrence = SelectReoccurrence(); - TrangaTask newTask = taskManager.AddTask(task, connector.name, publication, reoccurrence, "en"); + TrangaTask newTask = taskManager.AddTask(task, connector?.name, publication, reoccurrence, "en"); Console.WriteLine(newTask); Console.WriteLine("Press any key."); Console.ReadKey(); @@ -80,8 +123,7 @@ public static class Tranga_Cli while (query is null || query.Length < 1) query = Console.ReadLine(); PrintTasks(taskManager.GetAllTasks().Where(qTask => - ((Publication)qTask.publication!).sortName.ToLower() - .Contains(query, StringComparison.OrdinalIgnoreCase)).ToArray()); + qTask.ToString().ToLower().Contains(query, StringComparison.OrdinalIgnoreCase)).ToArray()); Console.WriteLine("Press any key."); Console.ReadKey(); menu = 0; @@ -93,7 +135,7 @@ public static class Tranga_Cli menu = 0; break; default: - selection = Menu(taskManager, folderPath); + selection = Menu(taskManager, settings.downloadLocation); switch (selection) { case ConsoleKey.L: @@ -129,6 +171,8 @@ public static class Tranga_Cli { Console.WriteLine("Force quit (Even with running tasks?) y/N"); selection = Console.ReadKey().Key; + while(selection != ConsoleKey.Y && selection != ConsoleKey.N) + selection = Console.ReadKey().Key; taskManager.Shutdown(selection == ConsoleKey.Y); }else // ReSharper disable once RedundantArgumentDefaultValue Better readability @@ -233,9 +277,9 @@ public static class Tranga_Cli return TimeSpan.Parse(Console.ReadLine()!, new CultureInfo("en-US")); } - private static void DownloadNow(string folderPath) + private static void DownloadNow(TaskManager.SettingsData settings) { - Connector connector = SelectConnector(folderPath); + Connector connector = SelectConnector(settings.downloadLocation); Publication publication = SelectPublication(connector); diff --git a/Tranga/Komga.cs b/Tranga/Komga.cs index 964b7b4..6cfa3f5 100644 --- a/Tranga/Komga.cs +++ b/Tranga/Komga.cs @@ -1,20 +1,31 @@ -using System.Text.Json; +using System.Net.Http.Headers; using System.Text.Json.Nodes; +using Newtonsoft.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace Tranga; public class Komga { - private string baseUrl { get; } + [System.Text.Json.Serialization.JsonRequired]public string baseUrl { get; } + [System.Text.Json.Serialization.JsonRequired]public string auth { get; } - public Komga(string baseUrl) + public Komga(string baseUrl, string username, string password) { this.baseUrl = baseUrl; + this.auth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"{username}:{password}")); + } + + [JsonConstructor] + public Komga(string baseUrl, string auth) + { + this.baseUrl = baseUrl; + this.auth = auth; } public KomgaLibrary[] GetLibraries() { - Stream data = NetClient.MakeRequest($"{baseUrl}/api/v1/libraries"); + Stream data = NetClient.MakeRequest($"{baseUrl}/api/v1/libraries", auth); JsonArray? result = JsonSerializer.Deserialize(data); if (result is null) return Array.Empty(); @@ -34,7 +45,7 @@ public class Komga public bool UpdateLibrary(string libraryId) { - return NetClient.MakePost($"{baseUrl}/api/v1/libraries/{libraryId}/scan"); + return NetClient.MakePost($"{baseUrl}/api/v1/libraries/{libraryId}/scan", auth); } public struct KomgaLibrary @@ -51,19 +62,37 @@ public class Komga private static class NetClient { - public static Stream MakeRequest(string url) + public static Stream MakeRequest(string url, string auth) { HttpClient client = new(); - HttpRequestMessage requestMessage = new(HttpMethod.Get, url); + HttpRequestMessage requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(url), + Headers = + { + { "Accept", "application/json" }, + { "Authorization", new AuthenticationHeaderValue("Basic", auth).ToString() } + } + }; HttpResponseMessage response = client.Send(requestMessage); Stream resultString = response.IsSuccessStatusCode ? response.Content.ReadAsStream() : Stream.Null; return resultString; } - public static bool MakePost(string url) + public static bool MakePost(string url, string auth) { HttpClient client = new(); - HttpRequestMessage requestMessage = new(HttpMethod.Post, url); + HttpRequestMessage requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Post, + RequestUri = new Uri(url), + Headers = + { + { "Accept", "application/json" }, + { "Authorization", new AuthenticationHeaderValue("Basic", auth).ToString() } + } + }; HttpResponseMessage response = client.Send(requestMessage); return response.IsSuccessStatusCode; } diff --git a/Tranga/TaskExecutor.cs b/Tranga/TaskExecutor.cs index 0f44a18..82a4bbd 100644 --- a/Tranga/TaskExecutor.cs +++ b/Tranga/TaskExecutor.cs @@ -7,20 +7,22 @@ /// public static class TaskExecutor { - /// /// Executes TrangaTask. /// - /// List of all available Connectors + /// Parent /// Task to execute /// Current chapterCollection to update /// Is thrown when there is no Connector available with the name of the TrangaTask.connectorName - public static void Execute(Connector[] connectors, TrangaTask trangaTask, Dictionary> chapterCollection) + public static void Execute(TaskManager taskManager, TrangaTask trangaTask, Dictionary> chapterCollection) { - //Get Connector from list of available Connectors and the required Connector of the TrangaTask - Connector? connector = connectors.FirstOrDefault(c => c.name == trangaTask.connectorName); - if (connector is null) - throw new ArgumentException($"Connector {trangaTask.connectorName} is not a known connector."); + Connector? connector = null; + if (trangaTask.task != TrangaTask.Task.UpdateKomgaLibrary) + { + //Get Connector from list of available Connectors and the required Connector of the TrangaTask + if (!taskManager.GetAvailableConnectors().TryGetValue(trangaTask.connectorName, out connector)) + throw new ArgumentException($"Connector {trangaTask.connectorName} is not a known connector."); + } if (trangaTask.isBeingExecuted) return; @@ -31,19 +33,37 @@ public static class TaskExecutor switch (trangaTask.task) { case TrangaTask.Task.DownloadNewChapters: - DownloadNewChapters(connector, (Publication)trangaTask.publication!, trangaTask.language, chapterCollection); + DownloadNewChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, chapterCollection); break; case TrangaTask.Task.UpdateChapters: - UpdateChapters(connector, (Publication)trangaTask.publication!, trangaTask.language, chapterCollection); + UpdateChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, chapterCollection); break; case TrangaTask.Task.UpdatePublications: - UpdatePublications(connector, chapterCollection); + UpdatePublications(connector!, chapterCollection); + break; + case TrangaTask.Task.UpdateKomgaLibrary: + UpdateKomgaLibrary(taskManager); break; } trangaTask.isBeingExecuted = false; } + /// + /// Updates all Komga-Libraries + /// + /// Parent + private static void UpdateKomgaLibrary(TaskManager taskManager) + { + if (taskManager.komga is null) + return; + Komga komga = taskManager.komga; + + Komga.KomgaLibrary[] allLibraries = komga.GetLibraries(); + foreach (Komga.KomgaLibrary lib in allLibraries) + komga.UpdateLibrary(lib.id); + } + /// /// Updates the available Publications from a Connector (all of them) /// diff --git a/Tranga/TaskManager.cs b/Tranga/TaskManager.cs index a58fba0..b0018e3 100644 --- a/Tranga/TaskManager.cs +++ b/Tranga/TaskManager.cs @@ -14,19 +14,21 @@ public class TaskManager private bool _continueRunning = true; private readonly Connector[] _connectors; private string downloadLocation { get; } - private string? komgaBaseUrl { get; } + + public Komga? komga { get; private set; } /// Local path to save data (Manga) to /// The Url of the Komga-instance that you want to update - public TaskManager(string folderPath, string? komgaBaseUrl = null) + public TaskManager(string folderPath, string? komgaBaseUrl = null, string? komgaUsername = null, string? komgaPassword = null) { + this.downloadLocation = folderPath; + + if (komgaBaseUrl != null && komgaUsername != null && komgaPassword != null) + this.komga = new Komga(komgaBaseUrl, komgaUsername, komgaPassword); this._connectors = new Connector[]{ new MangaDex(folderPath) }; _chapterCollection = new(); _allTasks = new HashSet(); - this.downloadLocation = folderPath; - this.komgaBaseUrl = komgaBaseUrl; - Thread taskChecker = new(TaskCheckerThread); taskChecker.Start(); } @@ -36,7 +38,7 @@ public class TaskManager this._connectors = new Connector[]{ new MangaDex(settings.downloadLocation) }; _chapterCollection = new(); this.downloadLocation = settings.downloadLocation; - this.komgaBaseUrl = settings.komgaUrl; + this.komga = settings.komga; _allTasks = settings.allTasks; Thread taskChecker = new(TaskCheckerThread); taskChecker.Start(); @@ -49,7 +51,7 @@ public class TaskManager foreach (TrangaTask task in _allTasks) { if(task.ShouldExecute()) - TaskExecutor.Execute(this._connectors, task, this._chapterCollection); //TODO Might crash here, when adding new Task while another Task is running. Check later + TaskExecutor.Execute(this, task, this._chapterCollection); //TODO Might crash here, when adding new Task while another Task is running. Check later } Thread.Sleep(1000); } @@ -66,7 +68,7 @@ public class TaskManager Task t = new Task(() => { - TaskExecutor.Execute(this._connectors, task, this._chapterCollection); + TaskExecutor.Execute(this, task, this._chapterCollection); }); t.Start(); } @@ -80,24 +82,44 @@ public class TaskManager /// Time-Interval between Executions /// language, should Task require parameter. Can be empty /// Is thrown when connectorName is not a available Connector - public TrangaTask AddTask(TrangaTask.Task task, string connectorName, Publication? publication, TimeSpan reoccurrence, + public TrangaTask AddTask(TrangaTask.Task task, string? connectorName, Publication? publication, TimeSpan reoccurrence, string language = "") { - //Get appropriate Connector from available Connectors for TrangaTask - Connector? connector = _connectors.FirstOrDefault(c => c.name == connectorName); - if (connector is null) - throw new ArgumentException($"Connector {connectorName} is not a known connector."); - - TrangaTask newTask = new TrangaTask(connector.name, task, publication, reoccurrence, language); - //Check if same task already exists - if (!_allTasks.Any(trangaTask => trangaTask.task != task && trangaTask.connectorName != connector.name && - trangaTask.publication?.downloadUrl != publication?.downloadUrl)) + if (task != TrangaTask.Task.UpdateKomgaLibrary && connectorName is null) + throw new ArgumentException($"connectorName can not be null for task {task}"); + + TrangaTask newTask; + if (task == TrangaTask.Task.UpdateKomgaLibrary) { - if(task != TrangaTask.Task.UpdatePublications) - _chapterCollection.Add((Publication)publication!, new List()); - _allTasks.Add(newTask); - ExportData(Directory.GetCurrentDirectory()); + newTask = new TrangaTask(task, null, null, reoccurrence, language); + + //Check if same task already exists + // ReSharper disable once SimplifyLinqExpressionUseAll readabilty + if (!_allTasks.Any(trangaTask => trangaTask.task == task)) + { + _allTasks.Add(newTask); + } } + else + { + //Get appropriate Connector from available Connectors for TrangaTask + Connector? connector = _connectors.FirstOrDefault(c => c.name == connectorName); + if (connector is null) + throw new ArgumentException($"Connector {connectorName} is not a known connector."); + + newTask = new TrangaTask(task, connector.name, publication, reoccurrence, language); + + //Check if same task already exists + if (!_allTasks.Any(trangaTask => trangaTask.task == task && trangaTask.connectorName == connector.name && + trangaTask.publication?.downloadUrl == publication?.downloadUrl)) + { + if(task != TrangaTask.Task.UpdatePublications) + _chapterCollection.Add((Publication)publication!, new List()); + _allTasks.Add(newTask); + } + } + ExportData(Directory.GetCurrentDirectory()); + return newTask; } @@ -134,6 +156,12 @@ public class TaskManager { return this._chapterCollection.Keys.ToArray(); } + + public void NewKomga(Komga? pKomga) + { + this.komga = pKomga; + this.ExportData(Directory.GetCurrentDirectory()); + } /// /// Shuts down the taskManager. @@ -167,7 +195,7 @@ public class TaskManager private void ExportData(string exportFolderPath) { - SettingsData data = new SettingsData(this.downloadLocation, this.komgaBaseUrl, this._allTasks); + SettingsData data = new SettingsData(this.downloadLocation, this.komga, this._allTasks); string exportPath = Path.Join(exportFolderPath, "data.json"); string serializedData = JsonConvert.SerializeObject(data); @@ -176,14 +204,14 @@ public class TaskManager public class SettingsData { - public string downloadLocation { get; } - public string? komgaUrl { get; } + public string downloadLocation { get; set; } + public Komga? komga { get; set; } public HashSet allTasks { get; } - public SettingsData(string downloadLocation, string? komgaUrl, HashSet allTasks) + public SettingsData(string downloadLocation, Komga? komga, HashSet allTasks) { this.downloadLocation = downloadLocation; - this.komgaUrl = komgaUrl; + this.komga = komga; this.allTasks = allTasks; } } diff --git a/Tranga/TrangaTask.cs b/Tranga/TrangaTask.cs index fc33466..686c1fd 100644 --- a/Tranga/TrangaTask.cs +++ b/Tranga/TrangaTask.cs @@ -11,16 +11,20 @@ public class TrangaTask // ReSharper disable once MemberCanBePrivate.Global I want it thaaat way public TimeSpan reoccurrence { get; } public DateTime lastExecuted { get; set; } - public string connectorName { get; } + public string? connectorName { get; } public Task task { get; } public Publication? publication { get; } public string language { get; } [JsonIgnore]public bool isBeingExecuted { get; set; } - public TrangaTask(string connectorName, Task task, Publication? publication, TimeSpan reoccurrence, string language = "") + public TrangaTask(Task task, string? connectorName, Publication? publication, TimeSpan reoccurrence, string language = "") { - if (task != Task.UpdatePublications && publication is null) - throw new ArgumentException($"Publication has to be not null for task {task}"); + if(task != Task.UpdateKomgaLibrary && connectorName is null) + throw new ArgumentException($"connectorName can not be null for task {task}"); + + if (publication is null && task != Task.UpdatePublications && task != Task.UpdateKomgaLibrary) + throw new ArgumentException($"Publication can not be null for task {task}"); + this.publication = publication; this.reoccurrence = reoccurrence; this.lastExecuted = DateTime.Now.Subtract(reoccurrence); @@ -39,7 +43,8 @@ public class TrangaTask { UpdatePublications, UpdateChapters, - DownloadNewChapters + DownloadNewChapters, + UpdateKomgaLibrary } public override string ToString()