diff --git a/API/API.csproj b/API/API.csproj
index a9eb97e..24d1e9c 100644
--- a/API/API.csproj
+++ b/API/API.csproj
@@ -19,6 +19,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/API/APIEndpointRecords/DownloadAvailableChaptersJobRecord.cs b/API/APIEndpointRecords/DownloadAvailableChaptersJobRecord.cs
new file mode 100644
index 0000000..39ca18a
--- /dev/null
+++ b/API/APIEndpointRecords/DownloadAvailableChaptersJobRecord.cs
@@ -0,0 +1,5 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace API.APIEndpointRecords;
+
+public record DownloadAvailableChaptersJobRecord([Required]string language, [Required]ulong recurrenceTimeMs, [Required]string localLibraryId);
\ No newline at end of file
diff --git a/API/APIEndpointRecords/DownloadAvailableJobsRecord.cs b/API/APIEndpointRecords/DownloadAvailableJobsRecord.cs
deleted file mode 100644
index a272df3..0000000
--- a/API/APIEndpointRecords/DownloadAvailableJobsRecord.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace API.APIEndpointRecords;
-
-public record DownloadAvailableJobsRecord([Required]ulong recurrenceTimeMs, [Required]string localLibraryId);
\ No newline at end of file
diff --git a/API/APIEndpointRecords/LunaseaRecord.cs b/API/APIEndpointRecords/LunaseaRecord.cs
deleted file mode 100644
index 9bd1a53..0000000
--- a/API/APIEndpointRecords/LunaseaRecord.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Text.RegularExpressions;
-
-namespace API.APIEndpointRecords;
-
-public record LunaseaRecord(string id)
-{
- private static Regex validateRex = new(@"(?:device|user)\/[0-9a-zA-Z\-]+");
- public bool Validate()
- {
- if (id == string.Empty)
- return false;
- if (!validateRex.IsMatch(id))
- return false;
- return true;
- }
-}
\ No newline at end of file
diff --git a/API/Controllers/JobController.cs b/API/Controllers/JobController.cs
index 1f4309a..c68f1c0 100644
--- a/API/Controllers/JobController.cs
+++ b/API/Controllers/JobController.cs
@@ -1,16 +1,19 @@
using API.APIEndpointRecords;
using API.Schema;
+using API.Schema.Contexts;
using API.Schema.Jobs;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
+// ReSharper disable InconsistentNaming
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{version:apiVersion}/[controller]")]
-public class JobController(PgsqlContext context) : Controller
+public class JobController(PgsqlContext context, ILog Log) : Controller
{
///
/// Returns all Jobs
@@ -102,7 +105,7 @@ public class JobController(PgsqlContext context) : Controller
/// ID of Manga
/// Job-Configuration
/// Job-IDs
- /// Could not find Library with ID
+ /// Could not find ToLibrary with ID
/// Could not find Manga with ID
/// Error during Database Operation
[HttpPut("DownloadAvailableChaptersJob/{MangaId}")]
@@ -110,7 +113,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult CreateDownloadAvailableChaptersJob(string MangaId, [FromBody]DownloadAvailableJobsRecord record)
+ public IActionResult CreateDownloadAvailableChaptersJob(string MangaId, [FromBody]DownloadAvailableChaptersJobRecord record)
{
if (context.Mangas.Find(MangaId) is not { } m)
return NotFound();
@@ -126,13 +129,17 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
- Job job = new DownloadAvailableChaptersJob(record.recurrenceTimeMs, MangaId);
- Job dep = new RetrieveChaptersJob(record.recurrenceTimeMs, MangaId, job.JobId);
- job.DependsOnJobsIds?.Add(dep.JobId);
- return AddJobs([dep, job]);
+ Job retrieveChapters = new RetrieveChaptersJob(m, record.language, record.recurrenceTimeMs);
+ Job updateFilesDownloaded =
+ new UpdateChaptersDownloadedJob(m, record.recurrenceTimeMs, dependsOnJobs: [retrieveChapters]);
+ Job downloadChapters = new DownloadAvailableChaptersJob(m, record.recurrenceTimeMs, dependsOnJobs: [retrieveChapters, updateFilesDownloaded]);
+ retrieveChapters.ParentJob = downloadChapters;
+ updateFilesDownloaded.ParentJob = retrieveChapters;
+ return AddJobs([retrieveChapters, downloadChapters, updateFilesDownloaded]);
}
///
@@ -148,14 +155,14 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult CreateNewDownloadChapterJob(string ChapterId)
{
- if(context.Chapters.Find(ChapterId) is null)
+ if(context.Chapters.Find(ChapterId) is not { } c)
return NotFound();
- Job job = new DownloadSingleChapterJob(ChapterId);
+ Job job = new DownloadSingleChapterJob(c);
return AddJobs([job]);
}
///
- /// Create a new UpdateFilesDownloadedJob
+ /// Create a new UpdateChaptersDownloadedJob
///
/// ID of the Manga
/// Job-IDs
@@ -167,9 +174,9 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateFilesDownloadedJob(string MangaId)
{
- if(context.Mangas.Find(MangaId) is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
- Job job = new UpdateFilesDownloadedJob(0, MangaId);
+ Job job = new UpdateChaptersDownloadedJob(m, 0);
return AddJobs([job]);
}
@@ -183,8 +190,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateAllFilesDownloadedJob()
{
- List ids = context.Mangas.Select(m => m.MangaId).ToList();
- List jobs = ids.Select(id => new UpdateFilesDownloadedJob(0, id)).ToList();
+ List jobs = context.Mangas.Select(m => new UpdateChaptersDownloadedJob(m, 0, null, null)).ToList();
try
{
context.Jobs.AddRange(jobs);
@@ -193,12 +199,13 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
///
- /// Create a new UpdateMetadataJob
+ /// Not Implemented: Create a new UpdateMetadataJob
///
/// ID of the Manga
/// Job-IDs
@@ -210,14 +217,11 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateMetadataJob(string MangaId)
{
- if(context.Mangas.Find(MangaId) is null)
- return NotFound();
- Job job = new UpdateMetadataJob(0, MangaId);
- return AddJobs([job]);
+ return StatusCode(Status501NotImplemented);
}
///
- /// Create a new UpdateMetadataJob for all Manga
+ /// Not Implemented: Create a new UpdateMetadataJob for all Manga
///
/// Job-IDs
/// Error during Database Operation
@@ -226,18 +230,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateAllMetadataJob()
{
- List ids = context.Mangas.Select(m => m.MangaId).ToList();
- List jobs = ids.Select(id => new UpdateMetadataJob(0, id)).ToList();
- try
- {
- context.Jobs.AddRange(jobs);
- context.SaveChanges();
- return Created();
- }
- catch (Exception e)
- {
- return StatusCode(500, e.Message);
- }
+ return StatusCode(Status501NotImplemented);
}
private IActionResult AddJobs(Job[] jobs)
@@ -250,6 +243,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -269,8 +263,7 @@ public class JobController(PgsqlContext context) : Controller
{
try
{
- Job? ret = context.Jobs.Find(JobId);
- if(ret is null)
+ if(context.Jobs.Find(JobId) is not { } ret)
return NotFound();
context.Remove(ret);
@@ -279,6 +272,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -322,6 +316,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -354,6 +349,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -367,6 +363,6 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status501NotImplemented)]
public IActionResult StopJob(string JobId)
{
- return StatusCode(501);
+ return StatusCode(Status501NotImplemented);
}
}
\ No newline at end of file
diff --git a/API/Controllers/LibraryConnectorController.cs b/API/Controllers/LibraryConnectorController.cs
index 4a7ee66..0db5748 100644
--- a/API/Controllers/LibraryConnectorController.cs
+++ b/API/Controllers/LibraryConnectorController.cs
@@ -1,6 +1,8 @@
using API.Schema;
+using API.Schema.Contexts;
using API.Schema.LibraryConnectors;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
@@ -9,10 +11,10 @@ namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class LibraryConnectorController(PgsqlContext context) : Controller
+public class LibraryConnectorController(LibraryContext context, ILog Log) : Controller
{
///
- /// Gets all configured Library-Connectors
+ /// Gets all configured ToLibrary-Connectors
///
///
[HttpGet]
@@ -24,9 +26,9 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
///
- /// Returns Library-Connector with requested ID
+ /// Returns ToLibrary-Connector with requested ID
///
- /// Library-Connector-ID
+ /// ToLibrary-Connector-ID
///
/// Connector with ID not found.
[HttpGet("{LibraryControllerId}")]
@@ -43,9 +45,9 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
///
- /// Creates a new Library-Connector
+ /// Creates a new ToLibrary-Connector
///
- /// Library-Connector
+ /// ToLibrary-Connector
///
/// Error during Database Operation
[HttpPut]
@@ -61,14 +63,15 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
///
- /// Deletes the Library-Connector with the requested ID
+ /// Deletes the ToLibrary-Connector with the requested ID
///
- /// Library-Connector-ID
+ /// ToLibrary-Connector-ID
///
/// Connector with ID not found.
/// Error during Database Operation
@@ -90,6 +93,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
diff --git a/API/Controllers/LocalLibrariesController.cs b/API/Controllers/LocalLibrariesController.cs
index 6cb3a1a..9f09118 100644
--- a/API/Controllers/LocalLibrariesController.cs
+++ b/API/Controllers/LocalLibrariesController.cs
@@ -1,6 +1,8 @@
using API.APIEndpointRecords;
using API.Schema;
+using API.Schema.Contexts;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
@@ -9,7 +11,7 @@ namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class LocalLibrariesController(PgsqlContext context) : Controller
+public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controller
{
[HttpGet]
[ProducesResponseType(Status200OK, "application/json")]
@@ -52,6 +54,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -79,6 +82,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -106,6 +110,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -128,6 +133,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -151,6 +157,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
diff --git a/API/Controllers/MangaConnectorController.cs b/API/Controllers/MangaConnectorController.cs
index 9bfa576..095ee59 100644
--- a/API/Controllers/MangaConnectorController.cs
+++ b/API/Controllers/MangaConnectorController.cs
@@ -1,6 +1,7 @@
-using API.Schema;
+using API.Schema.Contexts;
using API.Schema.MangaConnectors;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
@@ -9,7 +10,7 @@ namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class MangaConnectorController(PgsqlContext context) : Controller
+public class MangaConnectorController(PgsqlContext context, ILog Log) : Controller
{
///
/// Get all available Connectors (Scanlation-Sites)
@@ -74,6 +75,7 @@ public class MangaConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs
index 006e58b..eea2020 100644
--- a/API/Controllers/MangaController.cs
+++ b/API/Controllers/MangaController.cs
@@ -1,19 +1,22 @@
using API.Schema;
+using API.Schema.Contexts;
using API.Schema.Jobs;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using static Microsoft.AspNetCore.Http.StatusCodes;
+// ReSharper disable InconsistentNaming
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class MangaController(PgsqlContext context) : Controller
+public class MangaController(PgsqlContext context, ILog Log) : Controller
{
///
/// Returns all cached Manga
@@ -82,6 +85,7 @@ public class MangaController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -105,17 +109,15 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status503ServiceUnavailable, "text/plain")]
public IActionResult GetCover(string MangaId, [FromQuery]int? width, [FromQuery]int? height)
{
- DateTime requestStarted = HttpContext.Features.Get()?.RequestTime ?? DateTime.Now;
- Manga? m = context.Mangas.Find(MangaId);
- if (m is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
if (!System.IO.File.Exists(m.CoverFileNameInCache))
{
List coverDownloadJobs = context.Jobs.Where(j => j.JobType == JobType.DownloadMangaCoverJob).ToList();
- if (coverDownloadJobs.Any(j => j is DownloadMangaCoverJob dmc && dmc.MangaId == MangaId))
+ if (coverDownloadJobs.Any(j => j is DownloadMangaCoverJob dmc && dmc.MangaId == MangaId && dmc.state < JobState.Completed))
{
- Response.Headers.Add("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * coverDownloadJobs.Count() * 2 / 1000:D}");
+ Response.Headers.Append("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * coverDownloadJobs.Count() * 2 / 1000:D}");
return StatusCode(Status503ServiceUnavailable, TrangaSettings.startNewJobTimeoutMs * coverDownloadJobs.Count() * 2 / 1000);
}
else
@@ -151,12 +153,11 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetChapters(string MangaId)
{
- Manga? m = context.Mangas.Find(MangaId);
- if (m is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
- Chapter[] ret = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToArray();
- return Ok(ret);
+ Chapter[] chapters = m.Chapters.ToArray();
+ return Ok(chapters);
}
///
@@ -172,11 +173,10 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetChaptersDownloaded(string MangaId)
{
- Manga? m = context.Mangas.Find(MangaId);
- if (m is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
- List chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId && c.Downloaded == true).ToList();
+ List chapters = m.Chapters.ToList();
if (chapters.Count == 0)
return NoContent();
@@ -196,11 +196,10 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetChaptersNotDownloaded(string MangaId)
{
- Manga? m = context.Mangas.Find(MangaId);
- if (m is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
- List chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId && c.Downloaded == false).ToList();
+ List chapters = m.Chapters.ToList();
if (chapters.Count == 0)
return NoContent();
@@ -224,20 +223,19 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status503ServiceUnavailable, "text/plain")]
public IActionResult GetLatestChapter(string MangaId)
{
- Manga? m = context.Mangas.Find(MangaId);
- if (m is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
- List chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToList();
+ List chapters = m.Chapters.ToList();
if (chapters.Count == 0)
{
List retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).ToList();
- if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId))
+ if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId && rcj.state < JobState.Completed))
{
- Response.Headers.Add("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
+ Response.Headers.Append("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
return StatusCode(Status503ServiceUnavailable, TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2/ 1000);
}else
- return NoContent();
+ return Ok(0);
}
Chapter? max = chapters.Max();
@@ -264,18 +262,16 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status503ServiceUnavailable, "text/plain")]
public IActionResult GetLatestChapterDownloaded(string MangaId)
{
- Manga? m = context.Mangas.Find(MangaId);
- if (m is null)
+ if(context.Mangas.Find(MangaId) is not { } m)
return NotFound();
-
- List chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId && c.Downloaded == true).ToList();
+ List chapters = m.Chapters.ToList();
if (chapters.Count == 0)
{
List retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).ToList();
- if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId))
+ if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId && rcj.state < JobState.Completed))
{
- Response.Headers.Add("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
+ Response.Headers.Append("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
return StatusCode(Status503ServiceUnavailable, TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000);
}else
return NoContent();
@@ -287,11 +283,12 @@ public class MangaController(PgsqlContext context) : Controller
return Ok(max);
}
-
+
///
/// Configure the cut-off for Manga
///
/// Manga-ID
+ /// Threshold (Chapter Number)
///
/// Manga with ID not found.
/// Error during Database Operation
@@ -307,21 +304,22 @@ public class MangaController(PgsqlContext context) : Controller
try
{
- m.IgnoreChapterBefore = chapterThreshold;
+ m.IgnoreChaptersBefore = chapterThreshold;
context.SaveChanges();
return Ok();
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
///
- /// Move Manga to different Library
+ /// Move Manga to different ToLibrary
///
/// Manga-ID
- /// Library-Id
+ /// ToLibrary-Id
/// Folder is going to be moved
/// MangaId or LibraryId not found
/// Error during Database Operation
@@ -331,24 +329,23 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult MoveFolder(string MangaId, string LibraryId)
{
- Manga? manga = context.Mangas.Find(MangaId);
- if (manga is null)
+ if (context.Mangas.Find(MangaId) is not { } manga)
return NotFound();
- LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
- if (library is null)
+ if(context.LocalLibraries.Find(LibraryId) is not { } library)
return NotFound();
-
- MoveMangaLibraryJob dep = new (MangaId, LibraryId);
- UpdateFilesDownloadedJob up = new (0, manga.MangaId, null, [dep.JobId]);
+
+ MoveMangaLibraryJob moveLibrary = new(manga, library);
+ UpdateChaptersDownloadedJob updateDownloadedFiles = new(manga, 0, dependsOnJobs: [moveLibrary]);
try
{
- context.Jobs.AddRange([dep, up]);
+ context.Jobs.AddRange(moveLibrary, updateDownloadedFiles);
context.SaveChanges();
return Accepted();
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
diff --git a/API/Controllers/NotificationConnectorController.cs b/API/Controllers/NotificationConnectorController.cs
index b3a5ed3..c0100e0 100644
--- a/API/Controllers/NotificationConnectorController.cs
+++ b/API/Controllers/NotificationConnectorController.cs
@@ -1,8 +1,9 @@
using System.Text;
using API.APIEndpointRecords;
-using API.Schema;
+using API.Schema.Contexts;
using API.Schema.NotificationConnectors;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
@@ -12,7 +13,7 @@ namespace API.Controllers;
[ApiController]
[Produces("application/json")]
[Route("v{v:apiVersion}/[controller]")]
-public class NotificationConnectorController(PgsqlContext context) : Controller
+public class NotificationConnectorController(NotificationsContext context, ILog Log) : Controller
{
///
/// Gets all configured Notification-Connectors
@@ -69,6 +70,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
@@ -132,32 +134,6 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
return CreateConnector(ntfyConnector);
}
- ///
- /// Creates a new Lunasea-Notification-Connector
- ///
- /// https://docs.lunasea.app/lunasea/notifications/custom-notifications for id. Either device/:device_id or user/:user_id
- /// ID of new connector
- ///
- /// A NotificationConnector with name already exists
- /// Error during Database Operation
- [HttpPut("Lunasea")]
- [ProducesResponseType(Status201Created, "application/json")]
- [ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(Status409Conflict)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult CreateLunaseaConnector([FromBody]LunaseaRecord lunaseaRecord)
- {
- if(!lunaseaRecord.Validate())
- return BadRequest();
-
- NotificationConnector lunaseaConnector = new (TokenGen.CreateToken("Lunasea"),
- $"https://notify.lunasea.app/v1/custom/{lunaseaRecord.id}",
- new Dictionary(),
- "POST",
- "{\"title\": \"%title\", \"body\": \"%text\"}");
- return CreateConnector(lunaseaConnector);
- }
-
///
/// Creates a new Pushover-Notification-Connector
///
@@ -209,6 +185,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e.Message);
}
}
diff --git a/API/Controllers/QueryController.cs b/API/Controllers/QueryController.cs
index 2254e33..f4d603f 100644
--- a/API/Controllers/QueryController.cs
+++ b/API/Controllers/QueryController.cs
@@ -1,14 +1,17 @@
using API.Schema;
+using API.Schema.Contexts;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
+// ReSharper disable InconsistentNaming
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class QueryController(PgsqlContext context) : Controller
+public class QueryController(PgsqlContext context, ILog Log) : Controller
{
///
/// Returns the Author-Information for Author-ID
@@ -32,13 +35,16 @@ public class QueryController(PgsqlContext context) : Controller
///
/// Author-ID
///
+ /// Author not found
[HttpGet("Mangas/WithAuthorId/{AuthorId}")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetMangaWithAuthorIds(string AuthorId)
{
- return Ok(context.Mangas.Where(m => m.AuthorIds.Contains(AuthorId)));
+ if(context.Authors.Find(AuthorId) is not { } a)
+ return NotFound();
+ return Ok(context.Mangas.Where(m => m.Authors.Contains(a)));
}
-
+ /*
///
/// Returns Link-Information for Link-Id
///
@@ -71,18 +77,21 @@ public class QueryController(PgsqlContext context) : Controller
if (ret is null)
return NotFound();
return Ok(ret);
- }
+ }*/
///
/// Returns all Manga with Tag
///
///
///
+ /// Tag not found
[HttpGet("Mangas/WithTag/{Tag}")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetMangasWithTag(string Tag)
{
- return Ok(context.Mangas.Where(m => m.Tags.Contains(Tag)));
+ if(context.Tags.Find(Tag) is not { } t)
+ return NotFound();
+ return Ok(context.Mangas.Where(m => m.MangaTags.Contains(t)));
}
///
diff --git a/API/Controllers/SearchController.cs b/API/Controllers/SearchController.cs
index cc30c6c..fbef959 100644
--- a/API/Controllers/SearchController.cs
+++ b/API/Controllers/SearchController.cs
@@ -1,86 +1,55 @@
using API.Schema;
+using API.Schema.Contexts;
using API.Schema.Jobs;
using API.Schema.MangaConnectors;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using static Microsoft.AspNetCore.Http.StatusCodes;
+// ReSharper disable InconsistentNaming
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class SearchController(PgsqlContext context) : Controller
+public class SearchController(PgsqlContext context, ILog Log) : Controller
{
-
- ///
- /// Initiate a search for a Manga on all Connectors
- ///
- /// Name/Title of the Manga
- ///
- /// Error during Database Operation
- [HttpPost("Name")]
- [ProducesResponseType(Status200OK, "application/json")]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult SearchMangaGlobal([FromBody]string name)
- {
- List<(Manga, List?, List?, List?, List?)> allManga = new();
- foreach (MangaConnector contextMangaConnector in context.MangaConnectors.Where(connector => connector.Enabled))
- allManga.AddRange(contextMangaConnector.GetManga(name));
-
- List retMangas = new();
- foreach ((Manga? manga, List? authors, List? tags, List? links, List? altTitles) in allManga)
- {
- try
- {
- Manga? add = AddMangaToContext(manga, authors, tags, links, altTitles);
- if(add is not null)
- retMangas.Add(add);
- }
- catch (DbUpdateException e)
- {
- return StatusCode(500, e);
- }
- }
- return Ok(retMangas.ToArray());
- }
-
///
/// Initiate a search for a Manga on a specific Connector
///
- /// Manga-Connector-ID
- /// Name/Title of the Manga
+ ///
+ ///
///
/// MangaConnector with ID not found
/// MangaConnector with ID is disabled
/// Error during Database Operation
- [HttpPost("{MangaConnectorName}")]
+ [HttpGet("{MangaConnectorName}/{Query}")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status406NotAcceptable)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult SearchManga(string MangaConnectorName, [FromBody]string name)
+ public IActionResult SearchManga(string MangaConnectorName, string Query)
{
- MangaConnector? connector = context.MangaConnectors.Find(MangaConnectorName);
- if (connector is null)
+ if(context.MangaConnectors.Find(MangaConnectorName) is not { } connector)
return NotFound();
else if (connector.Enabled is false)
- return StatusCode(406);
+ return StatusCode(Status406NotAcceptable);
- (Manga, List?, List?, List?, List?)[] mangas = connector.GetManga(name);
+ Manga[] mangas = connector.SearchManga(Query);
List retMangas = new();
- foreach ((Manga? manga, List? authors, List? tags, List? links, List? altTitles) in mangas)
+ foreach (Manga manga in mangas)
{
try
{
- Manga? add = AddMangaToContext(manga, authors, tags, links, altTitles);
- if(add is not null)
+ if(AddMangaToContext(manga) is { } add)
retMangas.Add(add);
}
catch (DbUpdateException e)
{
- return StatusCode(500, e.Message);
+ Log.Error(e);
+ return StatusCode(Status500InternalServerError, e.Message);
}
}
@@ -98,104 +67,66 @@ public class SearchController(PgsqlContext context) : Controller
/// Error during Database Operation
[HttpPost("Url")]
[ProducesResponseType(Status200OK, "application/json")]
- [ProducesResponseType(Status300MultipleChoices)]
[ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult GetMangaFromUrl([FromBody]string url)
{
- List connectors = context.MangaConnectors.AsEnumerable().Where(c => c.ValidateUrl(url)).ToList();
- if (connectors.Count == 0)
- return NotFound();
- else if (connectors.Count > 1)
- return StatusCode(Status300MultipleChoices);
+ if (context.MangaConnectors.Find("Global") is not { } connector)
+ return StatusCode(Status500InternalServerError, "Could not find Global Connector.");
- (Manga manga, List? authors, List? tags, List? links, List? altTitles)? x = connectors.First().GetMangaFromUrl(url);
- if (x is null)
+ if(connector.GetMangaFromUrl(url) is not { } manga)
return BadRequest();
try
{
- Manga? add = AddMangaToContext(x.Value.manga, x.Value.authors, x.Value.tags, x.Value.links, x.Value.altTitles);
- if (add is not null)
+ if(AddMangaToContext(manga) is { } add)
return Ok(add);
- return StatusCode(500);
+ return StatusCode(Status500InternalServerError);
}
catch (DbUpdateException e)
{
- return StatusCode(500, e.Message);
+ Log.Error(e);
+ return StatusCode(Status500InternalServerError, e.Message);
}
}
- private Manga? AddMangaToContext(Manga? manga, List? authors, List? tags, List? links,
- List? altTitles)
+ private Manga? AddMangaToContext(Manga manga)
{
- if (manga is null)
- return null;
-
- Manga? existing = context.Mangas.Find(manga.MangaId);
+ context.Mangas.Load();
+ context.Authors.Load();
+ context.Tags.Load();
+ context.MangaConnectors.Load();
- if (tags is not null)
+ IEnumerable mergedTags = manga.MangaTags.Select(mt =>
{
- IEnumerable mergedTags = tags.Select(mt =>
- {
- MangaTag? inDb = context.Tags.Find(mt.Tag);
- return inDb ?? mt;
- });
- manga.MangaTags = mergedTags.ToList();
- IEnumerable newTags = manga.MangaTags
- .Where(mt => !context.Tags.Select(t => t.Tag).Contains(mt.Tag));
- context.Tags.AddRange(newTags);
- }
+ MangaTag? inDb = context.Tags.Find(mt.Tag);
+ return inDb ?? mt;
+ });
+ manga.MangaTags = mergedTags.ToList();
- if (authors is not null)
+ IEnumerable mergedAuthors = manga.Authors.Select(ma =>
{
- IEnumerable mergedAuthors = authors.Select(ma =>
- {
- Author? inDb = context.Authors.Find(ma.AuthorId);
- return inDb ?? ma;
- });
- manga.Authors = mergedAuthors.ToList();
- IEnumerable newAuthors = manga.Authors
- .Where(ma => !context.Authors.Select(a => a.AuthorId).Contains(ma.AuthorId));
- context.Authors.AddRange(newAuthors);
- }
+ Author? inDb = context.Authors.Find(ma.AuthorId);
+ return inDb ?? ma;
+ });
+ manga.Authors = mergedAuthors.ToList();
- if (links is not null)
+ try
{
- IEnumerable mergedLinks = links.Select(ml =>
- {
- Link? inDb = context.Links.Find(ml.LinkId);
- return inDb ?? ml;
- });
- manga.Links = mergedLinks.ToList();
- IEnumerable newLinks = manga.Links
- .Where(ml => !context.Links.Select(l => l.LinkId).Contains(ml.LinkId));
- context.Links.AddRange(newLinks);
- }
- if (altTitles is not null)
- {
- IEnumerable mergedAltTitles = altTitles.Select(mat =>
+ if (context.Mangas.Find(manga.MangaId) is { } r)
{
- MangaAltTitle? inDb = context.AltTitles.Find(mat.AltTitleId);
- return inDb ?? mat;
- });
- manga.AltTitles = mergedAltTitles.ToList();
- IEnumerable newAltTitles = manga.AltTitles
- .Where(mat => !context.AltTitles.Select(at => at.AltTitleId).Contains(mat.AltTitleId));
- context.AltTitles.AddRange(newAltTitles);
- }
-
- existing?.UpdateWithInfo(manga);
- if(existing is not null)
- context.Mangas.Update(existing);
- else
+ context.Mangas.Remove(r);
+ context.SaveChanges();
+ }
context.Mangas.Add(manga);
-
- context.Jobs.Add(new DownloadMangaCoverJob(manga.MangaId));
- context.Jobs.Add(new RetrieveChaptersJob(0, manga.MangaId));
-
- context.SaveChanges();
- return existing ?? manga;
+ context.Jobs.Add(new DownloadMangaCoverJob(manga));
+ context.SaveChanges();
+ }
+ catch (DbUpdateException e)
+ {
+ Log.Error(e);
+ return null;
+ }
+ return manga;
}
}
\ No newline at end of file
diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs
index d974eef..f6cbd05 100644
--- a/API/Controllers/SettingsController.cs
+++ b/API/Controllers/SettingsController.cs
@@ -1,7 +1,9 @@
using API.MangaDownloadClients;
using API.Schema;
+using API.Schema.Contexts;
using API.Schema.Jobs;
using Asp.Versioning;
+using log4net;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using static Microsoft.AspNetCore.Http.StatusCodes;
@@ -11,7 +13,7 @@ namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
-public class SettingsController(PgsqlContext context) : Controller
+public class SettingsController(PgsqlContext context, ILog Log) : Controller
{
///
/// Get all Settings
@@ -252,14 +254,16 @@ public class SettingsController(PgsqlContext context) : Controller
{
try
{
+ Dictionary oldPaths = context.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath);
TrangaSettings.UpdateChapterNamingScheme(namingScheme);
- MoveFileOrFolderJob[] newJobs =
- context.Chapters.Where(c => c.Downloaded).Select(c => c.UpdateArchiveFileName()).Where(x => x != null).ToArray()!;
+ MoveFileOrFolderJob[] newJobs = oldPaths
+ .Select(kv => new MoveFileOrFolderJob(kv.Value, kv.Key.FullArchiveFilePath)).ToArray();
context.Jobs.AddRange(newJobs);
return Ok();
}
catch (Exception e)
{
+ Log.Error(e);
return StatusCode(500, e);
}
}
diff --git a/API/MangaDownloadClients/DownloadClient.cs b/API/MangaDownloadClients/DownloadClient.cs
index 7356fde..542d552 100644
--- a/API/MangaDownloadClients/DownloadClient.cs
+++ b/API/MangaDownloadClients/DownloadClient.cs
@@ -15,7 +15,7 @@ internal abstract class DownloadClient
public RequestResult MakeRequest(string url, RequestType requestType, string? referrer = null, string? clickButton = null)
{
- Log.Debug($"Requesting {url}");
+ Log.Debug($"Requesting {requestType} {url}");
if (!TrangaSettings.requestLimits.ContainsKey(requestType))
{
return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null);
@@ -30,7 +30,7 @@ internal abstract class DownloadClient
LastExecutedRateLimit.TryAdd(requestType, now.Subtract(timeBetweenRequests));
TimeSpan rateLimitTimeout = timeBetweenRequests.Subtract(now.Subtract(LastExecutedRateLimit[requestType]));
- Log.Debug($"Request limit {rateLimit}/Minute timeBetweenRequests: {timeBetweenRequests:ss'.'fffff} Timeout: {rateLimitTimeout:ss'.'fffff}");
+ Log.Debug($"Request limit {requestType} {rateLimit}/Minute timeBetweenRequests: {timeBetweenRequests:ss'.'fffff} Timeout: {rateLimitTimeout:ss'.'fffff}");
if (rateLimitTimeout > TimeSpan.Zero)
{
@@ -39,6 +39,7 @@ internal abstract class DownloadClient
RequestResult result = MakeRequestInternal(url, referrer, clickButton);
LastExecutedRateLimit[requestType] = DateTime.UtcNow;
+ Log.Debug($"Result {url}: {result}");
return result;
}
diff --git a/API/MangaDownloadClients/RequestResult.cs b/API/MangaDownloadClients/RequestResult.cs
index b42cfba..ae9cec1 100644
--- a/API/MangaDownloadClients/RequestResult.cs
+++ b/API/MangaDownloadClients/RequestResult.cs
@@ -24,4 +24,10 @@ public struct RequestResult
this.hasBeenRedirected = hasBeenRedirected;
redirectedToUrl = redirectedTo;
}
+
+ public override string ToString()
+ {
+ return
+ $"{(int)statusCode} {statusCode.ToString()} {(hasBeenRedirected ? "Redirected: " : "")} {redirectedToUrl}";
+ }
}
\ No newline at end of file
diff --git a/API/Migrations/20250316143014_dev-160325-Initial.Designer.cs b/API/Migrations/20250316143014_dev-160325-Initial.Designer.cs
deleted file mode 100644
index 24a7d03..0000000
--- a/API/Migrations/20250316143014_dev-160325-Initial.Designer.cs
+++ /dev/null
@@ -1,821 +0,0 @@
-//
-using System;
-using System.Collections.Generic;
-using API.Schema;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace API.Migrations
-{
- [DbContext(typeof(PgsqlContext))]
- [Migration("20250316143014_dev-160325-Initial")]
- partial class dev160325Initial
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "9.0.3")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("API.Schema.Author", b =>
- {
- b.Property("AuthorId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("AuthorName")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.HasKey("AuthorId");
-
- b.ToTable("Authors");
- });
-
- modelBuilder.Entity("API.Schema.Chapter", b =>
- {
- b.Property("ChapterId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("ChapterNumber")
- .IsRequired()
- .HasMaxLength(10)
- .HasColumnType("character varying(10)");
-
- b.Property("Downloaded")
- .HasColumnType("boolean");
-
- b.Property("FileName")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("ParentMangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Title")
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("Url")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.Property("VolumeNumber")
- .HasColumnType("integer");
-
- b.HasKey("ChapterId");
-
- b.HasIndex("ParentMangaId");
-
- b.ToTable("Chapters");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.Job", b =>
- {
- b.Property("JobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.PrimitiveCollection("DependsOnJobsIds")
- .HasMaxLength(64)
- .HasColumnType("text[]");
-
- b.Property("Enabled")
- .HasColumnType("boolean");
-
- b.Property("JobType")
- .HasColumnType("smallint");
-
- b.Property("LastExecution")
- .HasColumnType("timestamp with time zone");
-
- b.Property("ParentJobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("RecurrenceMs")
- .HasColumnType("numeric(20,0)");
-
- b.Property("state")
- .HasColumnType("smallint");
-
- b.HasKey("JobId");
-
- b.HasIndex("ParentJobId");
-
- b.ToTable("Jobs");
-
- b.HasDiscriminator("JobType");
-
- b.UseTphMappingStrategy();
- });
-
- modelBuilder.Entity("API.Schema.LibraryConnectors.LibraryConnector", b =>
- {
- b.Property("LibraryConnectorId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Auth")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("BaseUrl")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("LibraryType")
- .HasColumnType("smallint");
-
- b.HasKey("LibraryConnectorId");
-
- b.ToTable("LibraryConnectors");
-
- b.HasDiscriminator("LibraryType");
-
- b.UseTphMappingStrategy();
- });
-
- modelBuilder.Entity("API.Schema.Link", b =>
- {
- b.Property("LinkId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("LinkProvider")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("LinkUrl")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.HasKey("LinkId");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Links");
- });
-
- modelBuilder.Entity("API.Schema.LocalLibrary", b =>
- {
- b.Property("LocalLibraryId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("BasePath")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("LibraryName")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("character varying(512)");
-
- b.HasKey("LocalLibraryId");
-
- b.ToTable("LocalLibraries");
- });
-
- modelBuilder.Entity("API.Schema.Manga", b =>
- {
- b.Property("MangaId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("CoverFileNameInCache")
- .HasColumnType("text");
-
- b.Property("CoverUrl")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("Description")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("DirectoryName")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("IdOnConnectorSite")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.Property("IgnoreChapterBefore")
- .HasColumnType("real");
-
- b.Property("LibraryLocalLibraryId")
- .HasColumnType("character varying(64)");
-
- b.Property("MangaConnectorId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Name")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("OriginalLanguage")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b.Property("ReleaseStatus")
- .HasColumnType("smallint");
-
- b.Property("WebsiteUrl")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("Year")
- .HasColumnType("bigint");
-
- b.HasKey("MangaId");
-
- b.HasIndex("LibraryLocalLibraryId");
-
- b.HasIndex("MangaConnectorId");
-
- b.ToTable("Mangas");
- });
-
- modelBuilder.Entity("API.Schema.MangaAltTitle", b =>
- {
- b.Property("AltTitleId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Language")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.Property("Title")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.HasKey("AltTitleId");
-
- b.HasIndex("MangaId");
-
- b.ToTable("AltTitles");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaConnector", b =>
- {
- b.Property("Name")
- .HasMaxLength(32)
- .HasColumnType("character varying(32)");
-
- b.PrimitiveCollection("BaseUris")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("text[]");
-
- b.Property("Enabled")
- .HasColumnType("boolean");
-
- b.Property("IconUrl")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.PrimitiveCollection("SupportedLanguages")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("text[]");
-
- b.HasKey("Name");
-
- b.ToTable("MangaConnectors");
-
- b.HasDiscriminator("Name").HasValue("MangaConnector");
-
- b.UseTphMappingStrategy();
- });
-
- modelBuilder.Entity("API.Schema.MangaTag", b =>
- {
- b.Property("Tag")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasKey("Tag");
-
- b.ToTable("Tags");
- });
-
- modelBuilder.Entity("API.Schema.Notification", b =>
- {
- b.Property("NotificationId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Date")
- .HasColumnType("timestamp with time zone");
-
- b.Property("Message")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("character varying(512)");
-
- b.Property("Title")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.Property("Urgency")
- .HasColumnType("smallint");
-
- b.HasKey("NotificationId");
-
- b.ToTable("Notifications");
- });
-
- modelBuilder.Entity("API.Schema.NotificationConnectors.NotificationConnector", b =>
- {
- b.Property("Name")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Body")
- .IsRequired()
- .HasMaxLength(4096)
- .HasColumnType("character varying(4096)");
-
- b.Property>("Headers")
- .IsRequired()
- .HasColumnType("hstore");
-
- b.Property("HttpMethod")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b.Property("Url")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.HasKey("Name");
-
- b.ToTable("NotificationConnectors");
- });
-
- modelBuilder.Entity("AuthorManga", b =>
- {
- b.Property("AuthorsAuthorId")
- .HasColumnType("character varying(64)");
-
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.HasKey("AuthorsAuthorId", "MangaId");
-
- b.HasIndex("MangaId");
-
- b.ToTable("AuthorManga");
- });
-
- modelBuilder.Entity("JobJob", b =>
- {
- b.Property("DependsOnJobsJobId")
- .HasColumnType("character varying(64)");
-
- b.Property("JobId")
- .HasColumnType("character varying(64)");
-
- b.HasKey("DependsOnJobsJobId", "JobId");
-
- b.HasIndex("JobId");
-
- b.ToTable("JobJob");
- });
-
- modelBuilder.Entity("MangaMangaTag", b =>
- {
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.Property("MangaTagsTag")
- .HasColumnType("character varying(64)");
-
- b.HasKey("MangaId", "MangaTagsTag");
-
- b.HasIndex("MangaTagsTag");
-
- b.ToTable("MangaMangaTag");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadAvailableChaptersJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("DownloadAvailableChaptersJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)1);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.HasDiscriminator().HasValue((byte)4);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("ChapterId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("ChapterId");
-
- b.HasDiscriminator().HasValue((byte)0);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.MoveFileOrFolderJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("FromLocation")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("ToLocation")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.HasDiscriminator().HasValue((byte)3);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("RetrieveChaptersJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)5);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateFilesDownloadedJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("UpdateFilesDownloadedJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)6);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("UpdateMetadataJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)2);
- });
-
- modelBuilder.Entity("API.Schema.LibraryConnectors.Kavita", b =>
- {
- b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector");
-
- b.HasDiscriminator().HasValue((byte)1);
- });
-
- modelBuilder.Entity("API.Schema.LibraryConnectors.Komga", b =>
- {
- b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector");
-
- b.HasDiscriminator().HasValue((byte)0);
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.AsuraToon", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("AsuraToon");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Bato", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Bato");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaDex", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("MangaDex");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaHere", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("MangaHere");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaKatana", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("MangaKatana");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Manganato", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Manganato");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Mangaworld", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Mangaworld");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.ManhuaPlus", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("ManhuaPlus");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Weebcentral", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Weebcentral");
- });
-
- modelBuilder.Entity("API.Schema.Chapter", b =>
- {
- b.HasOne("API.Schema.Manga", "ParentManga")
- .WithMany()
- .HasForeignKey("ParentMangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("ParentManga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.Job", b =>
- {
- b.HasOne("API.Schema.Jobs.Job", "ParentJob")
- .WithMany()
- .HasForeignKey("ParentJobId")
- .OnDelete(DeleteBehavior.Cascade);
-
- b.Navigation("ParentJob");
- });
-
- modelBuilder.Entity("API.Schema.Link", b =>
- {
- b.HasOne("API.Schema.Manga", null)
- .WithMany("Links")
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("API.Schema.Manga", b =>
- {
- b.HasOne("API.Schema.LocalLibrary", "Library")
- .WithMany()
- .HasForeignKey("LibraryLocalLibraryId")
- .OnDelete(DeleteBehavior.Cascade);
-
- b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
- .WithMany()
- .HasForeignKey("MangaConnectorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Library");
-
- b.Navigation("MangaConnector");
- });
-
- modelBuilder.Entity("API.Schema.MangaAltTitle", b =>
- {
- b.HasOne("API.Schema.Manga", null)
- .WithMany("AltTitles")
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("AuthorManga", b =>
- {
- b.HasOne("API.Schema.Author", null)
- .WithMany()
- .HasForeignKey("AuthorsAuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("API.Schema.Manga", null)
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("JobJob", b =>
- {
- b.HasOne("API.Schema.Jobs.Job", null)
- .WithMany()
- .HasForeignKey("DependsOnJobsJobId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("API.Schema.Jobs.Job", null)
- .WithMany()
- .HasForeignKey("JobId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("MangaMangaTag", b =>
- {
- b.HasOne("API.Schema.Manga", null)
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("API.Schema.MangaTag", null)
- .WithMany()
- .HasForeignKey("MangaTagsTag")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadAvailableChaptersJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b =>
- {
- b.HasOne("API.Schema.Chapter", "Chapter")
- .WithMany()
- .HasForeignKey("ChapterId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Chapter");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateFilesDownloadedJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Manga", b =>
- {
- b.Navigation("AltTitles");
-
- b.Navigation("Links");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/API/Migrations/20250316150158_dev-160325-2.Designer.cs b/API/Migrations/20250316150158_dev-160325-2.Designer.cs
deleted file mode 100644
index e2bd7a1..0000000
--- a/API/Migrations/20250316150158_dev-160325-2.Designer.cs
+++ /dev/null
@@ -1,821 +0,0 @@
-//
-using System;
-using System.Collections.Generic;
-using API.Schema;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace API.Migrations
-{
- [DbContext(typeof(PgsqlContext))]
- [Migration("20250316150158_dev-160325-2")]
- partial class dev1603252
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "9.0.3")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("API.Schema.Author", b =>
- {
- b.Property("AuthorId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("AuthorName")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.HasKey("AuthorId");
-
- b.ToTable("Authors");
- });
-
- modelBuilder.Entity("API.Schema.Chapter", b =>
- {
- b.Property("ChapterId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("ChapterNumber")
- .IsRequired()
- .HasMaxLength(10)
- .HasColumnType("character varying(10)");
-
- b.Property("Downloaded")
- .HasColumnType("boolean");
-
- b.Property("FileName")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("ParentMangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Title")
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("Url")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.Property("VolumeNumber")
- .HasColumnType("integer");
-
- b.HasKey("ChapterId");
-
- b.HasIndex("ParentMangaId");
-
- b.ToTable("Chapters");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.Job", b =>
- {
- b.Property("JobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.PrimitiveCollection("DependsOnJobsIds")
- .HasMaxLength(64)
- .HasColumnType("text[]");
-
- b.Property("Enabled")
- .HasColumnType("boolean");
-
- b.Property("JobType")
- .HasColumnType("smallint");
-
- b.Property("LastExecution")
- .HasColumnType("timestamp with time zone");
-
- b.Property("ParentJobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("RecurrenceMs")
- .HasColumnType("numeric(20,0)");
-
- b.Property("state")
- .HasColumnType("smallint");
-
- b.HasKey("JobId");
-
- b.HasIndex("ParentJobId");
-
- b.ToTable("Jobs");
-
- b.HasDiscriminator("JobType");
-
- b.UseTphMappingStrategy();
- });
-
- modelBuilder.Entity("API.Schema.LibraryConnectors.LibraryConnector", b =>
- {
- b.Property("LibraryConnectorId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Auth")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("BaseUrl")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("LibraryType")
- .HasColumnType("smallint");
-
- b.HasKey("LibraryConnectorId");
-
- b.ToTable("LibraryConnectors");
-
- b.HasDiscriminator("LibraryType");
-
- b.UseTphMappingStrategy();
- });
-
- modelBuilder.Entity("API.Schema.Link", b =>
- {
- b.Property("LinkId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("LinkProvider")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("LinkUrl")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.HasKey("LinkId");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Links");
- });
-
- modelBuilder.Entity("API.Schema.LocalLibrary", b =>
- {
- b.Property("LocalLibraryId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("BasePath")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("LibraryName")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("character varying(512)");
-
- b.HasKey("LocalLibraryId");
-
- b.ToTable("LocalLibraries");
- });
-
- modelBuilder.Entity("API.Schema.Manga", b =>
- {
- b.Property("MangaId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("CoverFileNameInCache")
- .HasColumnType("text");
-
- b.Property("CoverUrl")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("Description")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("DirectoryName")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("IdOnConnectorSite")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.Property("IgnoreChapterBefore")
- .HasColumnType("real");
-
- b.Property("LibraryLocalLibraryId")
- .HasColumnType("character varying(64)");
-
- b.Property("MangaConnectorId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Name")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("OriginalLanguage")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b.Property("ReleaseStatus")
- .HasColumnType("smallint");
-
- b.Property("WebsiteUrl")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("Year")
- .HasColumnType("bigint");
-
- b.HasKey("MangaId");
-
- b.HasIndex("LibraryLocalLibraryId");
-
- b.HasIndex("MangaConnectorId");
-
- b.ToTable("Mangas");
- });
-
- modelBuilder.Entity("API.Schema.MangaAltTitle", b =>
- {
- b.Property("AltTitleId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Language")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.Property("Title")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.HasKey("AltTitleId");
-
- b.HasIndex("MangaId");
-
- b.ToTable("AltTitles");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaConnector", b =>
- {
- b.Property("Name")
- .HasMaxLength(32)
- .HasColumnType("character varying(32)");
-
- b.PrimitiveCollection("BaseUris")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("text[]");
-
- b.Property("Enabled")
- .HasColumnType("boolean");
-
- b.Property("IconUrl")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.PrimitiveCollection("SupportedLanguages")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("text[]");
-
- b.HasKey("Name");
-
- b.ToTable("MangaConnectors");
-
- b.HasDiscriminator("Name").HasValue("MangaConnector");
-
- b.UseTphMappingStrategy();
- });
-
- modelBuilder.Entity("API.Schema.MangaTag", b =>
- {
- b.Property("Tag")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasKey("Tag");
-
- b.ToTable("Tags");
- });
-
- modelBuilder.Entity("API.Schema.Notification", b =>
- {
- b.Property("NotificationId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Date")
- .HasColumnType("timestamp with time zone");
-
- b.Property("Message")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("character varying(512)");
-
- b.Property("Title")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.Property("Urgency")
- .HasColumnType("smallint");
-
- b.HasKey("NotificationId");
-
- b.ToTable("Notifications");
- });
-
- modelBuilder.Entity("API.Schema.NotificationConnectors.NotificationConnector", b =>
- {
- b.Property("Name")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Body")
- .IsRequired()
- .HasMaxLength(4096)
- .HasColumnType("character varying(4096)");
-
- b.Property>("Headers")
- .IsRequired()
- .HasColumnType("hstore");
-
- b.Property("HttpMethod")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b.Property("Url")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.HasKey("Name");
-
- b.ToTable("NotificationConnectors");
- });
-
- modelBuilder.Entity("AuthorManga", b =>
- {
- b.Property("AuthorsAuthorId")
- .HasColumnType("character varying(64)");
-
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.HasKey("AuthorsAuthorId", "MangaId");
-
- b.HasIndex("MangaId");
-
- b.ToTable("AuthorManga");
- });
-
- modelBuilder.Entity("JobJob", b =>
- {
- b.Property("DependsOnJobsJobId")
- .HasColumnType("character varying(64)");
-
- b.Property("JobId")
- .HasColumnType("character varying(64)");
-
- b.HasKey("DependsOnJobsJobId", "JobId");
-
- b.HasIndex("JobId");
-
- b.ToTable("JobJob");
- });
-
- modelBuilder.Entity("MangaMangaTag", b =>
- {
- b.Property("MangaId")
- .HasColumnType("character varying(64)");
-
- b.Property("MangaTagsTag")
- .HasColumnType("character varying(64)");
-
- b.HasKey("MangaId", "MangaTagsTag");
-
- b.HasIndex("MangaTagsTag");
-
- b.ToTable("MangaMangaTag");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadAvailableChaptersJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("DownloadAvailableChaptersJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)1);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.HasDiscriminator().HasValue((byte)4);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("ChapterId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("ChapterId");
-
- b.HasDiscriminator().HasValue((byte)0);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.MoveFileOrFolderJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("FromLocation")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("ToLocation")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.HasDiscriminator().HasValue((byte)3);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("RetrieveChaptersJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)5);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateFilesDownloadedJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("UpdateFilesDownloadedJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)6);
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
- {
- b.HasBaseType("API.Schema.Jobs.Job");
-
- b.Property("MangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.HasIndex("MangaId");
-
- b.ToTable("Jobs", t =>
- {
- t.Property("MangaId")
- .HasColumnName("UpdateMetadataJob_MangaId");
- });
-
- b.HasDiscriminator().HasValue((byte)2);
- });
-
- modelBuilder.Entity("API.Schema.LibraryConnectors.Kavita", b =>
- {
- b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector");
-
- b.HasDiscriminator().HasValue((byte)1);
- });
-
- modelBuilder.Entity("API.Schema.LibraryConnectors.Komga", b =>
- {
- b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector");
-
- b.HasDiscriminator().HasValue((byte)0);
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.AsuraToon", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("AsuraToon");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Bato", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Bato");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaDex", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("MangaDex");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaHere", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("MangaHere");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.MangaKatana", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("MangaKatana");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Manganato", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Manganato");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Mangaworld", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Mangaworld");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.ManhuaPlus", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("ManhuaPlus");
- });
-
- modelBuilder.Entity("API.Schema.MangaConnectors.Weebcentral", b =>
- {
- b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
-
- b.HasDiscriminator().HasValue("Weebcentral");
- });
-
- modelBuilder.Entity("API.Schema.Chapter", b =>
- {
- b.HasOne("API.Schema.Manga", "ParentManga")
- .WithMany()
- .HasForeignKey("ParentMangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("ParentManga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.Job", b =>
- {
- b.HasOne("API.Schema.Jobs.Job", "ParentJob")
- .WithMany()
- .HasForeignKey("ParentJobId")
- .OnDelete(DeleteBehavior.Cascade);
-
- b.Navigation("ParentJob");
- });
-
- modelBuilder.Entity("API.Schema.Link", b =>
- {
- b.HasOne("API.Schema.Manga", null)
- .WithMany("Links")
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("API.Schema.Manga", b =>
- {
- b.HasOne("API.Schema.LocalLibrary", "Library")
- .WithMany()
- .HasForeignKey("LibraryLocalLibraryId")
- .OnDelete(DeleteBehavior.Restrict);
-
- b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
- .WithMany()
- .HasForeignKey("MangaConnectorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Library");
-
- b.Navigation("MangaConnector");
- });
-
- modelBuilder.Entity("API.Schema.MangaAltTitle", b =>
- {
- b.HasOne("API.Schema.Manga", null)
- .WithMany("AltTitles")
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("AuthorManga", b =>
- {
- b.HasOne("API.Schema.Author", null)
- .WithMany()
- .HasForeignKey("AuthorsAuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("API.Schema.Manga", null)
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("JobJob", b =>
- {
- b.HasOne("API.Schema.Jobs.Job", null)
- .WithMany()
- .HasForeignKey("DependsOnJobsJobId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("API.Schema.Jobs.Job", null)
- .WithMany()
- .HasForeignKey("JobId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("MangaMangaTag", b =>
- {
- b.HasOne("API.Schema.Manga", null)
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("API.Schema.MangaTag", null)
- .WithMany()
- .HasForeignKey("MangaTagsTag")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadAvailableChaptersJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b =>
- {
- b.HasOne("API.Schema.Chapter", "Chapter")
- .WithMany()
- .HasForeignKey("ChapterId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Chapter");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateFilesDownloadedJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
- {
- b.HasOne("API.Schema.Manga", "Manga")
- .WithMany()
- .HasForeignKey("MangaId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Manga");
- });
-
- modelBuilder.Entity("API.Schema.Manga", b =>
- {
- b.Navigation("AltTitles");
-
- b.Navigation("Links");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/API/Migrations/20250316150158_dev-160325-2.cs b/API/Migrations/20250316150158_dev-160325-2.cs
deleted file mode 100644
index 6adb255..0000000
--- a/API/Migrations/20250316150158_dev-160325-2.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace API.Migrations
-{
- ///
- public partial class dev1603252 : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_Mangas_LocalLibraries_LibraryLocalLibraryId",
- table: "Mangas");
-
- migrationBuilder.AddForeignKey(
- name: "FK_Mangas_LocalLibraries_LibraryLocalLibraryId",
- table: "Mangas",
- column: "LibraryLocalLibraryId",
- principalTable: "LocalLibraries",
- principalColumn: "LocalLibraryId",
- onDelete: ReferentialAction.Restrict);
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_Mangas_LocalLibraries_LibraryLocalLibraryId",
- table: "Mangas");
-
- migrationBuilder.AddForeignKey(
- name: "FK_Mangas_LocalLibraries_LibraryLocalLibraryId",
- table: "Mangas",
- column: "LibraryLocalLibraryId",
- principalTable: "LocalLibraries",
- principalColumn: "LocalLibraryId",
- onDelete: ReferentialAction.Cascade);
- }
- }
-}
diff --git a/API/Migrations/20250401001439_dev-010425-1.Designer.cs b/API/Migrations/20250401001439_dev-010425-1.Designer.cs
deleted file mode 100644
index 442a078..0000000
--- a/API/Migrations/20250401001439_dev-010425-1.Designer.cs
+++ /dev/null
@@ -1,827 +0,0 @@
-//
-using System;
-using System.Collections.Generic;
-using API.Schema;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace API.Migrations
-{
- [DbContext(typeof(PgsqlContext))]
- [Migration("20250401001439_dev-010425-1")]
- partial class dev0104251
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "9.0.3")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("API.Schema.Author", b =>
- {
- b.Property("AuthorId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("AuthorName")
- .IsRequired()
- .HasMaxLength(128)
- .HasColumnType("character varying(128)");
-
- b.HasKey("AuthorId");
-
- b.ToTable("Authors");
- });
-
- modelBuilder.Entity("API.Schema.Chapter", b =>
- {
- b.Property("ChapterId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("ChapterNumber")
- .IsRequired()
- .HasMaxLength(10)
- .HasColumnType("character varying(10)");
-
- b.Property("Downloaded")
- .HasColumnType("boolean");
-
- b.Property("FileName")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("ParentMangaId")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("Title")
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b.Property("Url")
- .IsRequired()
- .HasMaxLength(2048)
- .HasColumnType("character varying(2048)");
-
- b.Property("VolumeNumber")
- .HasColumnType("integer");
-
- b.HasKey("ChapterId");
-
- b.HasIndex("ParentMangaId");
-
- b.ToTable("Chapters");
- });
-
- modelBuilder.Entity("API.Schema.Jobs.Job", b =>
- {
- b.Property("JobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.PrimitiveCollection("DependsOnJobsIds")
- .HasMaxLength(64)
- .HasColumnType("text[]");
-
- b.Property("Enabled")
- .HasColumnType("boolean");
-
- b.Property("JobType")
- .HasColumnType("smallint");
-
- b.Property("LastExecution")
- .HasColumnType("timestamp with time zone");
-
- b.Property("ParentJobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b.Property("RecurrenceMs")
- .HasColumnType("numeric(20,0)");
-
- b.Property