33 Commits
0.5.1 ... 0.6.1

Author SHA1 Message Date
972cba69ec JsonIgnore
And better working directory stuff
2023-05-22 02:06:49 +02:00
962fe9529e Merge remote-tracking branch 'origin/master' 2023-05-22 01:53:36 +02:00
da1b0cb1cd Change to CommonApplicationFolder as applicationPath 2023-05-22 01:53:27 +02:00
7f88e57e47 Change to CommonApplicationFolder as applicationPath 2023-05-22 01:49:53 +02:00
8865bf284f Corrected applicationFolder in API 2023-05-22 01:42:53 +02:00
5fc2de5fcb logging 2023-05-22 01:20:32 +02:00
4bae223d95 Custom UniqueIdentifier. 2023-05-22 00:33:58 +02:00
0486168b43 AddMangaTaskToQueue Shortcut 2023-05-22 00:15:08 +02:00
b64ab5c6d4 Created TrangaSettings
Different files for settings, tasks, and known publications
Komga connector is stored in TrangaSettings
2023-05-22 00:13:24 +02:00
578fa5e6be JsonIgnore 2023-05-21 23:27:28 +02:00
4d33e78123 unused variable 2023-05-21 22:24:23 +02:00
52ac3e4e4e Proper Mapping for deleting and dequeueing 2023-05-21 22:24:12 +02:00
8b99a98e24 Merge pull request 'api-testing' (#5) from api-testing into master
Reviewed-on: #5
2023-05-21 22:04:06 +02:00
cf171d5c38 Bring CLI in line with new Methods 2023-05-21 22:02:35 +02:00
6d49b4b934 Swagger 2023-05-21 22:02:19 +02:00
b55d2a2d06 no duplicate keys 2023-05-21 22:02:05 +02:00
737eebf599 bring /settings/update in line with new methods 2023-05-21 22:01:56 +02:00
aef01b684c Fixed null on settings.komga 2023-05-21 22:01:40 +02:00
53bff61174 Added Swagger 2023-05-21 22:01:28 +02:00
431a602a40 Added Method UpdateSettings to SettingsData
Added Method UpdateSettings to TaskManager (to export data after update)
2023-05-21 22:01:04 +02:00
9afb81cee2 string and json 2023-05-21 21:24:18 +02:00
ea69b355b5 No duplicate keys 2023-05-21 21:24:04 +02:00
84dbc36bbf dont add duplicates 2023-05-21 21:23:51 +02:00
455c87b2e1 New API 2023-05-21 21:12:32 +02:00
df991e3da6 Remove APi for testing 2023-05-21 17:59:24 +02:00
13c96fd09a Create Appdata Directories for API 2023-05-21 16:51:14 +02:00
6f1a6a43ee API: Edit Settings 2023-05-21 16:49:55 +02:00
e2afc09c4a API: GetSettings 2023-05-21 16:46:34 +02:00
e9db7cfacc API: List Settings 2023-05-21 16:41:02 +02:00
755167c39a API: StartTask
API: Get Task Queue
API: Task Enqueue
API: Task Dequeue
2023-05-21 16:39:54 +02:00
1cff93fbac Use settings-file for API
Added API-call to list TaskTypes
Working? CreateTask API-call
Working? RemoveTask API-call
2023-05-21 16:23:35 +02:00
6c775d6e0c Moved check into if statement 2023-05-21 16:22:40 +02:00
876b1ab78b Added internalId to Publication 2023-05-21 16:22:14 +02:00
11 changed files with 284 additions and 175 deletions

View File

@ -1,81 +1,110 @@
using System.Text.Json; using Logging;
using Tranga; using Tranga;
TaskManager taskManager = new (Directory.GetCurrentDirectory()); string applicationFolderPath =
var builder = WebApplication.CreateBuilder(args); Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Tranga-API");
var app = builder.Build(); 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, "settings.json");
app.MapGet("/GetConnectors", () => JsonSerializer.Serialize(taskManager.GetAvailableConnectors().Values.ToArray())); Directory.CreateDirectory(applicationFolderPath);
Directory.CreateDirectory(logsFolderPath);
app.MapGet("/GetPublications", (string connectorName, string? publicationName) =>
{
Connector connector = taskManager.GetConnector(connectorName);
Publication[] publications;
if (publicationName is not null)
publications = connector.GetPublications(publicationName);
else
publications = connector.GetPublications();
return JsonSerializer.Serialize(publications);
});
app.MapGet("/ListTasks", () => JsonSerializer.Serialize(taskManager.GetAllTasks()));
app.MapGet("/CreateTask",
(TrangaTask.Task task, string? connectorName, string? publicationName, TimeSpan reoccurrence, string? language) =>
{
switch (task)
{
case TrangaTask.Task.UpdateKomgaLibrary:
taskManager.AddTask(TrangaTask.Task.UpdateKomgaLibrary, null, null, reoccurrence);
break;
case TrangaTask.Task.DownloadNewChapters:
try
{
Connector connector = taskManager.GetConnector(connectorName);
Publication[] publications;
if (publicationName is not null)
publications = connector.GetPublications(publicationName);
else
publications = connector.GetPublications();
Publication? publication = publications.FirstOrDefault(pub => pub.downloadUrl == publicationName);
if (publication is null)
JsonSerializer.Serialize($"Publication {publicationName} is unknown.");
taskManager.AddTask(TrangaTask.Task.DownloadNewChapters, connectorName, publication, reoccurrence, language ?? "");
return JsonSerializer.Serialize("Success");
}
catch (Exception e)
{
return JsonSerializer.Serialize(e.Message);
}
default: return JsonSerializer.Serialize("Not Implemented");
}
return JsonSerializer.Serialize("Not Implemented");
});
app.MapGet("/RemoveTask", (TrangaTask.Task task, string? connectorName, string? publicationName) =>
{
switch (task)
{
case TrangaTask.Task.UpdateKomgaLibrary:
taskManager.DeleteTask(TrangaTask.Task.UpdateKomgaLibrary, null, null);
return JsonSerializer.Serialize("Success");
case TrangaTask.Task.DownloadNewChapters:
Publication? publication = taskManager.GetAllPublications().FirstOrDefault(pub => pub.downloadUrl == publicationName);
if (publication is null)
JsonSerializer.Serialize($"Publication {publicationName} is unknown.");
taskManager.DeleteTask(TrangaTask.Task.DownloadNewChapters, connectorName, publication);
return JsonSerializer.Serialize("Success");
default: return JsonSerializer.Serialize("Not Implemented"); Console.WriteLine($"Logfile-Path: {logFilePath}");
} Console.WriteLine($"Settings-File-Path: {settingsFilePath}");
Logger logger = new(new[] { Logger.LoggerType.FileLogger }, null, null, logFilePath);
logger.WriteLine("Tranga_CLI", "Loading Taskmanager.");
TrangaSettings settings;
if (File.Exists(settingsFilePath))
settings = TrangaSettings.LoadSettings(settingsFilePath);
else
settings = new TrangaSettings(Directory.GetCurrentDirectory(), applicationFolderPath, null);
TaskManager taskManager = new (settings, logger);
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers().AddNewtonsoftJson();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwagger();
app.UseSwaggerUI();
app.MapGet("/GetAvailableControllers", () => taskManager.GetAvailableConnectors());
app.MapGet("/GetKnownPublications", () => taskManager.GetAllPublications());
app.MapGet("/GetPublicationsFromConnector", (string connectorName, string title) =>
{
Connector? connector = taskManager.GetAvailableConnectors().FirstOrDefault(con => con.Key == connectorName).Value;
if (connector is null)
return Array.Empty<Publication>();
if(title.Length < 4)
return Array.Empty<Publication>();
return taskManager.GetPublicationsFromConnector(connector, title);
}); });
app.MapGet("/Tasks/GetTaskTypes", () => Enum.GetNames(typeof(TrangaTask.Task)));
app.MapPost("/Tasks/Create", (string taskType, string? connectorName, string? publicationId, string reoccurrenceTime, string? language) =>
{
Publication? publication = taskManager.GetAllPublications().FirstOrDefault(pub => pub.internalId == publicationId);
TrangaTask.Task task = Enum.Parse<TrangaTask.Task>(taskType);
taskManager.AddTask(task, connectorName, publication, TimeSpan.Parse(reoccurrenceTime), language??"");
});
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);
taskManager.DeleteTask(task, connectorName, publication);
});
app.MapGet("/Tasks/GetList", () => taskManager.GetAllTasks());
app.MapPost("/Tasks/Start", (string taskType, string? connectorName, string? publicationId) =>
{
TrangaTask.Task pTask = Enum.Parse<TrangaTask.Task>(taskType);
TrangaTask? task = taskManager.GetAllTasks().FirstOrDefault(tTask =>
tTask.task == pTask && tTask.publication?.internalId == publicationId && tTask.connectorName == connectorName);
if (task is null)
return;
taskManager.ExecuteTaskNow(task);
});
app.MapGet("/Tasks/GetRunningTasks",
() => taskManager.GetAllTasks().Where(task => task.state is TrangaTask.ExecutionState.Running));
app.MapGet("/Queue/GetList",
() => taskManager.GetAllTasks().Where(task => task.state is TrangaTask.ExecutionState.Enqueued));
app.MapPost("/Queue/Enqueue", (string taskType, string? connectorName, string? publicationId) =>
{
TrangaTask.Task pTask = Enum.Parse<TrangaTask.Task>(taskType);
TrangaTask? task = taskManager.GetAllTasks().FirstOrDefault(tTask =>
tTask.task == pTask && tTask.publication?.internalId == publicationId && tTask.connectorName == connectorName);
if (task is null)
return;
taskManager.AddTaskToQueue(task);
});
app.MapDelete("/Queue/Dequeue", (string taskType, string? connectorName, string? publicationId) =>
{
TrangaTask.Task pTask = Enum.Parse<TrangaTask.Task>(taskType);
TrangaTask? task = taskManager.GetAllTasks().FirstOrDefault(tTask =>
tTask.task == pTask && tTask.publication?.internalId == publicationId && tTask.connectorName == connectorName);
if (task is null)
return;
taskManager.RemoveTaskFromQueue(task);
});
app.MapGet("/Settings/Get", () => taskManager.settings);
app.MapPost("/Settings/Update", (string? downloadLocation, string? komgaUrl, string? komgaAuth) => taskManager.UpdateSettings(downloadLocation, komgaUrl, komgaAuth) );
app.Run(); app.Run();

View File

@ -3,8 +3,8 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:14826", "applicationUrl": "http://localhost:1716",
"sslPort": 44333 "sslPort": 44391
} }
}, },
"profiles": { "profiles": {
@ -12,7 +12,7 @@
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "http://localhost:5119", "applicationUrl": "http://localhost:5177",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
@ -21,7 +21,7 @@
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "https://localhost:7070;http://localhost:5119", "applicationUrl": "https://localhost:7036;http://localhost:5177",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

View File

@ -15,7 +15,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Logging\Logging.csproj" />
<ProjectReference Include="..\Tranga\Tranga.csproj" /> <ProjectReference Include="..\Tranga\Tranga.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
</Project> </Project>

View File

@ -1 +0,0 @@
[{"reoccurrence":"00:00:00","lastExecuted":"2023-05-19T17:34:40.5349215+02:00","connectorName":"MangaDex","task":0,"publication":{"sortName":null,"description":null,"tags":null,"posterUrl":null,"year":null,"originalLanguage":null,"status":null,"folderName":null,"downloadUrl":null},"language":"en"}]

View File

@ -14,10 +14,10 @@ public static class Tranga_Cli
{ {
public static void Main(string[] args) 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 logsFolderPath = Path.Join(applicationFolderPath, "logs");
string logFilePath = Path.Join(logsFolderPath, $"log-{DateTime.Now:dd-M-yyyy-HH-mm-ss}.txt"); 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(applicationFolderPath);
Directory.CreateDirectory(logsFolderPath); Directory.CreateDirectory(logsFolderPath);
@ -28,11 +28,11 @@ public static class Tranga_Cli
Logger logger = new(new[] { Logger.LoggerType.FileLogger }, null, null, logFilePath); Logger logger = new(new[] { Logger.LoggerType.FileLogger }, null, null, logFilePath);
logger.WriteLine("Tranga_CLI", "Loading Taskmanager."); logger.WriteLine("Tranga_CLI", "Loading Taskmanager.");
TaskManager.SettingsData settings; TrangaSettings settings;
if (File.Exists(settingsFilePath)) if (File.Exists(settingsFilePath))
settings = TaskManager.LoadData(settingsFilePath); settings = TrangaSettings.LoadSettings(settingsFilePath);
else 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"); logger.WriteLine("Tranga_CLI", "User Input");
@ -40,7 +40,7 @@ public static class Tranga_Cli
string? tmpPath = Console.ReadLine(); string? tmpPath = Console.ReadLine();
while(tmpPath is null) while(tmpPath is null)
tmpPath = Console.ReadLine(); tmpPath = Console.ReadLine();
if(tmpPath.Length > 0) if (tmpPath.Length > 0)
settings.downloadLocation = tmpPath; settings.downloadLocation = tmpPath;
Console.WriteLine($"Komga BaseURL [{settings.komga?.baseUrl}]:"); Console.WriteLine($"Komga BaseURL [{settings.komga?.baseUrl}]:");
@ -73,7 +73,7 @@ public static class Tranga_Cli
tmpPass += keyInfo.KeyChar; tmpPass += keyInfo.KeyChar;
} }
} while (key != ConsoleKey.Enter); } while (key != ConsoleKey.Enter);
settings.komga = new Komga(tmpUrl, tmpUser, tmpPass, logger); settings.komga = new Komga(tmpUrl, tmpUser, tmpPass, logger);
} }
@ -81,7 +81,7 @@ public static class Tranga_Cli
TaskMode(settings, logger); 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); TaskManager taskManager = new (settings, logger);
ConsoleKey selection = ConsoleKey.EraseEndOfFile; ConsoleKey selection = ConsoleKey.EraseEndOfFile;
@ -143,7 +143,7 @@ public static class Tranga_Cli
TailLog(logger); TailLog(logger);
Console.ReadKey(); Console.ReadKey();
break; break;
case ConsoleKey.M: case ConsoleKey.G:
RemoveTaskFromQueue(taskManager, logger); RemoveTaskFromQueue(taskManager, logger);
Console.WriteLine("Press any key."); Console.WriteLine("Press any key.");
Console.ReadKey(); Console.ReadKey();
@ -153,6 +153,11 @@ public static class Tranga_Cli
Console.WriteLine("Press any key."); Console.WriteLine("Press any key.");
Console.ReadKey(); Console.ReadKey();
break; break;
case ConsoleKey.M:
AddMangaTaskToQueue(taskManager, logger);
Console.WriteLine("Press any key.");
Console.ReadKey();
break;
} }
PrintMenu(taskManager, taskManager.settings.downloadLocation, logger); PrintMenu(taskManager, taskManager.settings.downloadLocation, logger);
} }
@ -186,8 +191,8 @@ public static class Tranga_Cli
Console.WriteLine(); Console.WriteLine();
Console.WriteLine($"{"C: Create Task",-30}{"L: List tasks",-30}{"B: Enqueue Task", -30}"); 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($"{"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($"{"E: Execute Task now",-30}{"R: List Running Tasks", -30}{"G: Remove Task from Queue", -30}");
Console.WriteLine(); Console.WriteLine($"{"M: New Download Manga Task",-30}{"", -30}{"", -30}");
Console.WriteLine($"{"",-30}{"F: Show Log",-30}{"Q: Exit",-30}"); Console.WriteLine($"{"",-30}{"F: Show Log",-30}{"Q: Exit",-30}");
} }
@ -250,6 +255,25 @@ public static class Tranga_Cli
return null; 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) private static void AddTaskToQueue(TaskManager taskManager, Logger logger)
{ {
@ -300,7 +324,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"); logger.WriteLine("Tranga_CLI", "Menu: Creating Task");
TrangaTask.Task? tmpTask = SelectTaskType(logger); TrangaTask.Task? tmpTask = SelectTaskType(logger);
@ -319,7 +343,7 @@ public static class Tranga_Cli
Publication? publication = null; Publication? publication = null;
if (task != TrangaTask.Task.UpdatePublications && task != TrangaTask.Task.UpdateKomgaLibrary) if (task != TrangaTask.Task.UpdatePublications && task != TrangaTask.Task.UpdateKomgaLibrary)
{ {
publication = SelectPublication(connector!, logger); publication = SelectPublication(taskManager, connector!, logger);
if (publication is null) if (publication is null)
return; return;
} }
@ -443,7 +467,7 @@ public static class Tranga_Cli
return null; 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"); logger.WriteLine("Tranga_CLI", "Menu: Select Publication");
@ -452,7 +476,7 @@ public static class Tranga_Cli
Console.WriteLine("Publication search query (leave empty for all):"); Console.WriteLine("Publication search query (leave empty for all):");
string? query = Console.ReadLine(); string? query = Console.ReadLine();
Publication[] publications = connector.GetPublications(query ?? ""); Publication[] publications = taskManager.GetPublicationsFromConnector(connector, query ?? "");
int pIndex = 0; int pIndex = 0;
Console.WriteLine("Publications:"); Console.WriteLine("Publications:");

View File

@ -4,10 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tranga", ".\Tranga\Tranga.c
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tranga-CLI", "Tranga-CLI\Tranga-CLI.csproj", "{4899E3B2-B259-479A-B43E-042D043E9501}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tranga-CLI", "Tranga-CLI\Tranga-CLI.csproj", "{4899E3B2-B259-479A-B43E-042D043E9501}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tranga-API", "Tranga-API\Tranga-API.csproj", "{6284C936-4E90-486B-BC46-0AFAD85AD8EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Logging\Logging.csproj", "{415BE889-BB7D-426F-976F-8D977876A462}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Logging\Logging.csproj", "{415BE889-BB7D-426F-976F-8D977876A462}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tranga-API", "Tranga-API\Tranga-API.csproj", "{48F4E495-75BC-4402-8E03-DEC5B79D7E83}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -22,13 +22,13 @@ Global
{4899E3B2-B259-479A-B43E-042D043E9501}.Debug|Any CPU.Build.0 = Debug|Any CPU {4899E3B2-B259-479A-B43E-042D043E9501}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4899E3B2-B259-479A-B43E-042D043E9501}.Release|Any CPU.ActiveCfg = Release|Any CPU {4899E3B2-B259-479A-B43E-042D043E9501}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4899E3B2-B259-479A-B43E-042D043E9501}.Release|Any CPU.Build.0 = Release|Any CPU {4899E3B2-B259-479A-B43E-042D043E9501}.Release|Any CPU.Build.0 = Release|Any CPU
{6284C936-4E90-486B-BC46-0AFAD85AD8EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6284C936-4E90-486B-BC46-0AFAD85AD8EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6284C936-4E90-486B-BC46-0AFAD85AD8EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6284C936-4E90-486B-BC46-0AFAD85AD8EE}.Release|Any CPU.Build.0 = Release|Any CPU
{415BE889-BB7D-426F-976F-8D977876A462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {415BE889-BB7D-426F-976F-8D977876A462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{415BE889-BB7D-426F-976F-8D977876A462}.Debug|Any CPU.Build.0 = Debug|Any CPU {415BE889-BB7D-426F-976F-8D977876A462}.Debug|Any CPU.Build.0 = Debug|Any CPU
{415BE889-BB7D-426F-976F-8D977876A462}.Release|Any CPU.ActiveCfg = Release|Any CPU {415BE889-BB7D-426F-976F-8D977876A462}.Release|Any CPU.ActiveCfg = Release|Any CPU
{415BE889-BB7D-426F-976F-8D977876A462}.Release|Any CPU.Build.0 = Release|Any CPU {415BE889-BB7D-426F-976F-8D977876A462}.Release|Any CPU.Build.0 = Release|Any CPU
{48F4E495-75BC-4402-8E03-DEC5B79D7E83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{48F4E495-75BC-4402-8E03-DEC5B79D7E83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48F4E495-75BC-4402-8E03-DEC5B79D7E83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48F4E495-75BC-4402-8E03-DEC5B79D7E83}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -58,14 +58,12 @@ public class MangaDex : Connector
: null; : null;
JsonArray altTitlesObject = attributes["altTitles"]!.AsArray(); JsonArray altTitlesObject = attributes["altTitles"]!.AsArray();
string[,] altTitles = new string[altTitlesObject.Count, 2]; Dictionary<string, string> altTitlesDict = new();
int titleIndex = 0;
foreach (JsonNode? altTitleNode in altTitlesObject) foreach (JsonNode? altTitleNode in altTitlesObject)
{ {
JsonObject altTitleObject = (JsonObject)altTitleNode!; JsonObject altTitleObject = (JsonObject)altTitleNode!;
string key = ((IDictionary<string, JsonNode?>)altTitleObject).Keys.ToArray()[0]; string key = ((IDictionary<string, JsonNode?>)altTitleObject).Keys.ToArray()[0];
altTitles[titleIndex, 0] = key; altTitlesDict.TryAdd(key, altTitleObject[key]!.GetValue<string>());
altTitles[titleIndex++, 1] = altTitleObject[key]!.GetValue<string>();
} }
JsonArray tagsObject = attributes["tags"]!.AsArray(); JsonArray tagsObject = attributes["tags"]!.AsArray();
@ -84,16 +82,13 @@ public class MangaDex : Connector
poster = relationships.FirstOrDefault(relationship => relationship!["type"]!.GetValue<string>() == "cover_art")!["id"]!.GetValue<string>(); poster = relationships.FirstOrDefault(relationship => relationship!["type"]!.GetValue<string>() == "cover_art")!["id"]!.GetValue<string>();
} }
string[,]? links = null; Dictionary<string, string> linksDict = new();
if (attributes.ContainsKey("links") && attributes["links"] is not null) if (attributes.ContainsKey("links") && attributes["links"] is not null)
{ {
JsonObject linksObject = attributes["links"]!.AsObject(); JsonObject linksObject = attributes["links"]!.AsObject();
links = new string[linksObject.Count, 2];
int linkIndex = 0;
foreach (string key in ((IDictionary<string, JsonNode?>)linksObject).Keys) foreach (string key in ((IDictionary<string, JsonNode?>)linksObject).Keys)
{ {
links[linkIndex, 0] = key; linksDict.Add(key, linksObject[key]!.GetValue<string>());
links[linkIndex++, 1] = linksObject[key]!.GetValue<string>();
} }
} }
@ -110,10 +105,10 @@ public class MangaDex : Connector
Publication pub = new Publication( Publication pub = new Publication(
title, title,
description, description,
altTitles, altTitlesDict,
tags.ToArray(), tags.ToArray(),
poster, poster,
links, linksDict,
year, year,
originalLanguage, originalLanguage,
status, status,
@ -218,7 +213,10 @@ public class MangaDex : Connector
DirectoryInfo dirInfo = new (publicationFolder); DirectoryInfo dirInfo = new (publicationFolder);
foreach(FileInfo fileInfo in dirInfo.EnumerateFiles()) foreach(FileInfo fileInfo in dirInfo.EnumerateFiles())
if (fileInfo.Name.Contains("cover.")) if (fileInfo.Name.Contains("cover."))
{
logger?.WriteLine(this.GetType().ToString(), $"Cover exists {publication.sortName}");
return; return;
}
//Request information where to download Cover //Request information where to download Cover
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json; using System.Text;
using Newtonsoft.Json;
namespace Tranga; namespace Tranga;
@ -9,31 +10,34 @@ public readonly struct Publication
{ {
public string sortName { get; } public string sortName { get; }
// ReSharper disable UnusedAutoPropertyAccessor.Global we need it, trust // ReSharper disable UnusedAutoPropertyAccessor.Global we need it, trust
[JsonIgnore]public string[,] altTitles { get; } public Dictionary<string,string> altTitles { get; }
// ReSharper disable trice MemberCanBePrivate.Global, trust // ReSharper disable trice MemberCanBePrivate.Global, trust
public string? description { get; } public string? description { get; }
public string[] tags { get; } public string[] tags { get; }
public string? posterUrl { get; } public string? posterUrl { get; }
[JsonIgnore]public string[,]? links { get; } public Dictionary<string,string> links { get; }
public int? year { get; } public int? year { get; }
public string? originalLanguage { get; } public string? originalLanguage { get; }
public string status { get; } public string status { get; }
public string folderName { get; } public string folderName { get; }
public string downloadUrl { get; } public string downloadUrl { get; }
[JsonIgnore]public string internalId { get; }
public Publication(string sortName, string? description, string[,] altTitles, string[] tags, string? posterUrl, string[,]? links, int? year, string? originalLanguage, string status, string downloadUrl) 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)
{ {
this.sortName = sortName; this.sortName = sortName;
this.description = description; this.description = description;
this.altTitles = altTitles; this.altTitles = altTitles;
this.tags = tags; this.tags = tags;
this.posterUrl = posterUrl; this.posterUrl = posterUrl;
this.links = links; this.links = links ?? new Dictionary<string, string>();
this.year = year; this.year = year;
this.originalLanguage = originalLanguage; this.originalLanguage = originalLanguage;
this.status = status; this.status = status;
this.downloadUrl = downloadUrl; this.downloadUrl = downloadUrl;
this.folderName = string.Concat(sortName.Split(Path.GetInvalidPathChars().Concat(Path.GetInvalidFileNameChars()).ToArray())); 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}"));
} }
/// <returns>Serialized JSON String for series.json</returns> /// <returns>Serialized JSON String for series.json</returns>

View File

@ -17,7 +17,7 @@ public static class TaskExecutor
/// <param name="chapterCollection">Current chapterCollection to update</param> /// <param name="chapterCollection">Current chapterCollection to update</param>
/// <param name="logger"></param> /// <param name="logger"></param>
/// <exception cref="ArgumentException">Is thrown when there is no Connector available with the name of the TrangaTask.connectorName</exception> /// <exception cref="ArgumentException">Is thrown when there is no Connector available with the name of the TrangaTask.connectorName</exception>
public static void Execute(TaskManager taskManager, TrangaTask trangaTask, Dictionary<Publication, List<Chapter>> chapterCollection, Logger? logger) public static void Execute(TaskManager taskManager, TrangaTask trangaTask, Logger? logger)
{ {
//Only execute task if it is not already being executed. //Only execute task if it is not already being executed.
if (trangaTask.state == TrangaTask.ExecutionState.Running) if (trangaTask.state == TrangaTask.ExecutionState.Running)
@ -37,13 +37,13 @@ public static class TaskExecutor
switch (trangaTask.task) switch (trangaTask.task)
{ {
case TrangaTask.Task.DownloadNewChapters: case TrangaTask.Task.DownloadNewChapters:
DownloadNewChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, chapterCollection); DownloadNewChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, ref taskManager._chapterCollection);
break; break;
case TrangaTask.Task.UpdateChapters: case TrangaTask.Task.UpdateChapters:
UpdateChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, chapterCollection); UpdateChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, ref taskManager._chapterCollection);
break; break;
case TrangaTask.Task.UpdatePublications: case TrangaTask.Task.UpdatePublications:
UpdatePublications(connector!, chapterCollection); UpdatePublications(connector!, ref taskManager._chapterCollection);
break; break;
case TrangaTask.Task.UpdateKomgaLibrary: case TrangaTask.Task.UpdateKomgaLibrary:
UpdateKomgaLibrary(taskManager); UpdateKomgaLibrary(taskManager);
@ -75,7 +75,7 @@ public static class TaskExecutor
/// </summary> /// </summary>
/// <param name="connector">Connector to receive Publications from</param> /// <param name="connector">Connector to receive Publications from</param>
/// <param name="chapterCollection"></param> /// <param name="chapterCollection"></param>
private static void UpdatePublications(Connector connector, Dictionary<Publication, List<Chapter>> chapterCollection) private static void UpdatePublications(Connector connector, ref Dictionary<Publication, List<Chapter>> chapterCollection)
{ {
Publication[] publications = connector.GetPublications(); Publication[] publications = connector.GetPublications();
foreach (Publication publication in publications) foreach (Publication publication in publications)
@ -90,9 +90,9 @@ public static class TaskExecutor
/// <param name="publication">Publication to check</param> /// <param name="publication">Publication to check</param>
/// <param name="language">Language to receive chapters for</param> /// <param name="language">Language to receive chapters for</param>
/// <param name="chapterCollection"></param> /// <param name="chapterCollection"></param>
private static void DownloadNewChapters(Connector connector, Publication publication, string language, Dictionary<Publication, List<Chapter>> chapterCollection) private static void DownloadNewChapters(Connector connector, Publication publication, string language, ref Dictionary<Publication, List<Chapter>> chapterCollection)
{ {
List<Chapter> newChapters = UpdateChapters(connector, publication, language, chapterCollection); List<Chapter> newChapters = UpdateChapters(connector, publication, language, ref chapterCollection);
connector.DownloadCover(publication); connector.DownloadCover(publication);
//Check if Publication already has a Folder and a series.json //Check if Publication already has a Folder and a series.json
@ -116,7 +116,7 @@ public static class TaskExecutor
/// <param name="language">Language to receive chapters for</param> /// <param name="language">Language to receive chapters for</param>
/// <param name="chapterCollection"></param> /// <param name="chapterCollection"></param>
/// <returns>List of Chapters that were previously not in collection</returns> /// <returns>List of Chapters that were previously not in collection</returns>
private static List<Chapter> UpdateChapters(Connector connector, Publication publication, string language, Dictionary<Publication, List<Chapter>> chapterCollection) private static List<Chapter> UpdateChapters(Connector connector, Publication publication, string language, ref Dictionary<Publication, List<Chapter>> chapterCollection)
{ {
List<Chapter> newChaptersList = new(); List<Chapter> newChaptersList = new();
chapterCollection.TryAdd(publication, newChaptersList); //To ensure publication is actually in collection chapterCollection.TryAdd(publication, newChaptersList); //To ensure publication is actually in collection

View File

@ -10,30 +10,31 @@ namespace Tranga;
/// </summary> /// </summary>
public class TaskManager public class TaskManager
{ {
private readonly Dictionary<Publication, List<Chapter>> _chapterCollection = new(); public Dictionary<Publication, List<Chapter>> _chapterCollection = new();
private readonly HashSet<TrangaTask> _allTasks; private HashSet<TrangaTask> _allTasks;
private bool _continueRunning = true; private bool _continueRunning = true;
private readonly Connector[] _connectors; private readonly Connector[] _connectors;
private readonly Dictionary<Connector, List<TrangaTask>> _taskQueue = new(); private readonly Dictionary<Connector, List<TrangaTask>> _taskQueue = new();
public SettingsData settings { get; } public TrangaSettings settings { get; }
private Logger? logger { get; } private Logger? logger { get; }
public Komga? komga => settings.komga;
public Komga? komga { get; }
/// <param name="downloadFolderPath">Local path to save data (Manga) to</param> /// <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="komgaBaseUrl">The Url of the Komga-instance that you want to update</param>
/// <param name="komgaUsername">The Komga username</param> /// <param name="komgaUsername">The Komga username</param>
/// <param name="komgaPassword">The Komga password</param> /// <param name="komgaPassword">The Komga password</param>
/// <param name="logger"></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 = null, string? komgaBaseUrl = null, string? komgaUsername = null, string? komgaPassword = null, Logger? logger = null)
{ {
this.logger = logger; this.logger = logger;
_allTasks = new HashSet<TrangaTask>(); _allTasks = new HashSet<TrangaTask>();
Komga? newKomga = null;
if (komgaBaseUrl != null && komgaUsername != null && komgaPassword != null) if (komgaBaseUrl != null && komgaUsername != null && komgaPassword != null)
this.komga = new Komga(komgaBaseUrl, komgaUsername, komgaPassword, logger); newKomga = new Komga(komgaBaseUrl, komgaUsername, komgaPassword, logger);
this.settings = new SettingsData(downloadFolderPath, settingsFilePath, this.komga, this._allTasks); this.settings = new TrangaSettings(downloadFolderPath, workingDirectory, newKomga);
ExportData(); ExportData();
this._connectors = new Connector[]{ new MangaDex(downloadFolderPath, logger) }; this._connectors = new Connector[]{ new MangaDex(downloadFolderPath, logger) };
@ -44,17 +45,27 @@ public class TaskManager
taskChecker.Start(); taskChecker.Start();
} }
public TaskManager(SettingsData settings, Logger? logger = null) public void UpdateSettings(string? downloadLocation, string? komgaUrl, string? komgaAuth)
{
Komga? komga = null;
if (komgaUrl is not null && komgaAuth is not null)
komga = new Komga(komgaUrl, komgaAuth, null);
settings.downloadLocation = downloadLocation ?? settings.downloadLocation;
settings.komga = komga ?? komga;
ExportData();
}
public TaskManager(TrangaSettings settings, Logger? logger = null)
{ {
this.logger = logger; this.logger = logger;
this._connectors = new Connector[]{ new MangaDex(settings.downloadLocation, logger) }; this._connectors = new Connector[]{ new MangaDex(settings.downloadLocation, logger) };
this.settings = settings;
ExportData();
foreach(Connector cConnector in this._connectors) foreach(Connector cConnector in this._connectors)
_taskQueue.Add(cConnector, new List<TrangaTask>()); _taskQueue.Add(cConnector, new List<TrangaTask>());
this.komga = settings.komga; _allTasks = new HashSet<TrangaTask>();
_allTasks = settings.allTasks;
this.settings = settings;
ImportData();
ExportData();
Thread taskChecker = new(TaskCheckerThread); Thread taskChecker = new(TaskCheckerThread);
taskChecker.Start(); taskChecker.Start();
} }
@ -107,7 +118,7 @@ public class TaskManager
logger?.WriteLine(this.GetType().ToString(), $"Forcing Execution: {task}"); logger?.WriteLine(this.GetType().ToString(), $"Forcing Execution: {task}");
Task t = new Task(() => Task t = new Task(() =>
{ {
TaskExecutor.Execute(this, task, this._chapterCollection, logger); TaskExecutor.Execute(this, task, logger);
}); });
t.Start(); t.Start();
} }
@ -125,8 +136,6 @@ public class TaskManager
string language = "") string language = "")
{ {
logger?.WriteLine(this.GetType().ToString(), $"Adding new Task {task} {connectorName} {publication?.sortName}"); logger?.WriteLine(this.GetType().ToString(), $"Adding new Task {task} {connectorName} {publication?.sortName}");
if (task != TrangaTask.Task.UpdateKomgaLibrary && connectorName is null)
throw new ArgumentException($"connectorName can not be null for task {task}");
TrangaTask newTask; TrangaTask newTask;
if (task == TrangaTask.Task.UpdateKomgaLibrary) if (task == TrangaTask.Task.UpdateKomgaLibrary)
@ -142,6 +151,9 @@ public class TaskManager
} }
else else
{ {
if(connectorName is null)
throw new ArgumentException($"connectorName can not be null for task {task}");
//Get appropriate Connector from available Connectors for TrangaTask //Get appropriate Connector from available Connectors for TrangaTask
Connector? connector = _connectors.FirstOrDefault(c => c.name == connectorName); Connector? connector = _connectors.FirstOrDefault(c => c.name == connectorName);
if (connector is null) if (connector is null)
@ -154,7 +166,7 @@ public class TaskManager
trangaTask.publication?.downloadUrl == publication?.downloadUrl)) trangaTask.publication?.downloadUrl == publication?.downloadUrl))
{ {
if(task != TrangaTask.Task.UpdatePublications) if(task != TrangaTask.Task.UpdatePublications)
_chapterCollection.Add((Publication)publication!, new List<Chapter>()); _chapterCollection.TryAdd((Publication)publication!, new List<Chapter>());
_allTasks.Add(newTask); _allTasks.Add(newTask);
} }
} }
@ -227,6 +239,17 @@ public class TaskManager
_allTasks.CopyTo(ret); _allTasks.CopyTo(ret);
return ret; return ret;
} }
public Publication[] GetPublicationsFromConnector(Connector connector, string? title = null)
{
Publication[] ret = connector.GetPublications(title ?? "");
foreach (Publication publication in ret)
{
if(!_chapterCollection.Any(pub => pub.Key.sortName == publication.sortName))
this._chapterCollection.TryAdd(publication, new List<Chapter>());
}
return ret;
}
/// <returns>All added Publications</returns> /// <returns>All added Publications</returns>
public Publication[] GetAllPublications() public Publication[] GetAllPublications()
@ -269,19 +292,25 @@ public class TaskManager
Environment.Exit(0); Environment.Exit(0);
} }
/// <summary> private void ImportData()
/// 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)
{ {
if (!File.Exists(importFilePath)) logger?.WriteLine(this.GetType().ToString(), "Importing Data");
return new SettingsData(Directory.GetCurrentDirectory(), null, null, new HashSet<TrangaTask>()); 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); if (File.Exists(settings.knownPublicationsPath))
SettingsData data = JsonConvert.DeserializeObject<SettingsData>(toRead)!; {
logger?.WriteLine(this.GetType().ToString(), $"Importing known publications from {settings.knownPublicationsPath}");
return data; buffer = File.ReadAllText(settings.knownPublicationsPath);
Publication[] publications = JsonConvert.DeserializeObject<Publication[]>(buffer)!;
foreach (Publication publication in publications)
this._chapterCollection.TryAdd(publication, new List<Chapter>());
}
} }
/// <summary> /// <summary>
@ -289,27 +318,15 @@ public class TaskManager
/// </summary> /// </summary>
private void ExportData() 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));
string serializedData = JsonConvert.SerializeObject(settings);
File.WriteAllText(settings.settingsFilePath, serializedData); logger?.WriteLine(this.GetType().ToString(), $"Exporting tasks to {settings.tasksFilePath}");
File.WriteAllText(settings.tasksFilePath, JsonConvert.SerializeObject(this._allTasks));
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; set; }
public string settingsFilePath { get; }
public Komga? komga { get; 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;
}
}
} }

31
Tranga/TrangaSettings.cs Normal file
View 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;
}
}