diff --git a/API/API.csproj b/API/API.csproj
index 24d1e9c..1cb720c 100644
--- a/API/API.csproj
+++ b/API/API.csproj
@@ -32,8 +32,4 @@
-
-
-
-
diff --git a/API/Controllers/JobController.cs b/API/Controllers/JobController.cs
index 49609f5..f47317a 100644
--- a/API/Controllers/JobController.cs
+++ b/API/Controllers/JobController.cs
@@ -37,7 +37,7 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetJobs([FromBody]string[] ids)
{
- Job[] ret = context.Jobs.Where(job => ids.Contains(job.JobId)).ToArray();
+ Job[] ret = context.Jobs.Where(job => ids.Contains(job.Key)).ToArray();
return Ok(ret);
}
@@ -103,11 +103,11 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
///
/// Create a new DownloadAvailableChaptersJob
///
- /// ID of Manga
+ /// ID of Obj
/// Job-Configuration
/// Job-IDs
- /// Could not find ToLibrary with ID
- /// Could not find Manga with ID
+ /// Could not find ToFileLibrary with ID
+ /// Could not find Obj with ID
/// Error during Database Operation
[HttpPut("DownloadAvailableChaptersJob/{MangaId}")]
[ProducesResponseType(Status201Created, "application/json")]
@@ -122,7 +122,7 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
{
try
{
- LocalLibrary? l = context.LocalLibraries.Find(record.localLibraryId);
+ FileLibrary? l = context.LocalLibraries.Find(record.localLibraryId);
if (l is null)
return BadRequest();
m.Library = l;
@@ -166,9 +166,9 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
///
/// Create a new UpdateChaptersDownloadedJob
///
- /// ID of the Manga
+ /// ID of the Obj
/// Job-IDs
- /// Could not find Manga with ID
+ /// Could not find Obj with ID
/// Error during Database Operation
[HttpPut("UpdateFilesJob/{MangaId}")]
[ProducesResponseType(Status201Created, "application/json")]
@@ -183,7 +183,7 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Create a new UpdateMetadataJob for all Manga
+ /// Create a new UpdateMetadataJob for all Obj
///
/// Job-IDs
/// Error during Database Operation
@@ -209,9 +209,9 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
///
/// Not Implemented: Create a new UpdateMetadataJob
///
- /// ID of the Manga
+ /// ID of the Obj
/// Job-IDs
- /// Could not find Manga with ID
+ /// Could not find Obj with ID
/// Error during Database Operation
[HttpPut("UpdateMetadataJob/{MangaId}")]
[ProducesResponseType(Status201Created, "application/json")]
@@ -223,7 +223,7 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Not Implemented: Create a new UpdateMetadataJob for all Manga
+ /// Not Implemented: Create a new UpdateMetadataJob for all Obj
///
/// Job-IDs
/// Error during Database Operation
@@ -241,7 +241,7 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
{
context.Jobs.AddRange(jobs);
context.SaveChanges();
- return new CreatedResult((string?)null, jobs.Select(j => j.JobId).ToArray());
+ return new CreatedResult((string?)null, jobs.Select(j => j.Key).ToArray());
}
catch (Exception e)
{
@@ -279,15 +279,6 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
}
}
- private IQueryable GetChildJobs(string parentJobId)
- {
- IQueryable children = context.Jobs.Where(j => j.ParentJobId == parentJobId);
- foreach (Job child in children)
- foreach (Job grandChild in GetChildJobs(child.JobId))
- children.Append(grandChild);
- return children;
- }
-
///
/// Modify Job with ID
///
@@ -314,7 +305,7 @@ public class JobController(PgsqlContext context, ILog Log) : Controller
ret.Enabled = modifyJobRecord.Enabled ?? ret.Enabled;
context.SaveChanges();
- return new AcceptedResult(ret.JobId, ret);
+ return new AcceptedResult(ret.Key, ret);
}
catch (Exception e)
{
diff --git a/API/Controllers/LibraryConnectorController.cs b/API/Controllers/LibraryConnectorController.cs
index 0db5748..ccf4419 100644
--- a/API/Controllers/LibraryConnectorController.cs
+++ b/API/Controllers/LibraryConnectorController.cs
@@ -1,5 +1,4 @@
-using API.Schema;
-using API.Schema.Contexts;
+using API.Schema.Contexts;
using API.Schema.LibraryConnectors;
using Asp.Versioning;
using log4net;
@@ -14,7 +13,7 @@ namespace API.Controllers;
public class LibraryConnectorController(LibraryContext context, ILog Log) : Controller
{
///
- /// Gets all configured ToLibrary-Connectors
+ /// Gets all configured ToFileLibrary-Connectors
///
///
[HttpGet]
@@ -26,9 +25,9 @@ public class LibraryConnectorController(LibraryContext context, ILog Log) : Cont
}
///
- /// Returns ToLibrary-Connector with requested ID
+ /// Returns ToFileLibrary-Connector with requested ID
///
- /// ToLibrary-Connector-ID
+ /// ToFileLibrary-Connector-ID
///
/// Connector with ID not found.
[HttpGet("{LibraryControllerId}")]
@@ -45,9 +44,9 @@ public class LibraryConnectorController(LibraryContext context, ILog Log) : Cont
}
///
- /// Creates a new ToLibrary-Connector
+ /// Creates a new ToFileLibrary-Connector
///
- /// ToLibrary-Connector
+ /// ToFileLibrary-Connector
///
/// Error during Database Operation
[HttpPut]
@@ -69,9 +68,9 @@ public class LibraryConnectorController(LibraryContext context, ILog Log) : Cont
}
///
- /// Deletes the ToLibrary-Connector with the requested ID
+ /// Deletes the ToFileLibrary-Connector with the requested ID
///
- /// ToLibrary-Connector-ID
+ /// ToFileLibrary-Connector-ID
///
/// Connector with ID not found.
/// Error during Database Operation
diff --git a/API/Controllers/LocalLibrariesController.cs b/API/Controllers/LocalLibrariesController.cs
index 9f09118..8ca8e6d 100644
--- a/API/Controllers/LocalLibrariesController.cs
+++ b/API/Controllers/LocalLibrariesController.cs
@@ -14,18 +14,18 @@ namespace API.Controllers;
public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controller
{
[HttpGet]
- [ProducesResponseType(Status200OK, "application/json")]
+ [ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetLocalLibraries()
{
return Ok(context.LocalLibraries);
}
[HttpGet("{LibraryId}")]
- [ProducesResponseType(Status200OK, "application/json")]
+ [ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
public IActionResult GetLocalLibrary(string LibraryId)
{
- LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
+ FileLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
return Ok(library);
@@ -38,7 +38,7 @@ public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controll
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult UpdateLocalLibrary(string LibraryId, [FromBody]NewLibraryRecord record)
{
- LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
+ FileLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
if (record.Validate() == false)
@@ -68,7 +68,7 @@ public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controll
{
try
{
- LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
+ FileLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
@@ -96,7 +96,7 @@ public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controll
{
try
{
- LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
+ FileLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
@@ -116,7 +116,7 @@ public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controll
}
[HttpPut]
- [ProducesResponseType(Status200OK, "application/json")]
+ [ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult CreateNewLibrary([FromBody]NewLibraryRecord library)
@@ -125,11 +125,11 @@ public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controll
return BadRequest();
try
{
- LocalLibrary newLibrary = new (library.path, library.name);
- context.LocalLibraries.Add(newLibrary);
+ FileLibrary newFileLibrary = new (library.path, library.name);
+ context.LocalLibraries.Add(newFileLibrary);
context.SaveChanges();
- return Ok(newLibrary);
+ return Ok(newFileLibrary);
}
catch (Exception e)
{
@@ -147,7 +147,7 @@ public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controll
try
{
- LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
+ FileLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
context.Remove(library);
diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs
index c5971b7..5079583 100644
--- a/API/Controllers/MangaController.cs
+++ b/API/Controllers/MangaController.cs
@@ -4,6 +4,7 @@ using API.Schema.Jobs;
using Asp.Versioning;
using log4net;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Net.Http.Headers;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
@@ -20,7 +21,7 @@ namespace API.Controllers;
public class MangaController(PgsqlContext context, ILog Log) : Controller
{
///
- /// Returns all cached Manga
+ /// Returns all cached Obj
///
///
[HttpGet]
@@ -32,24 +33,24 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns all cached Manga with IDs
+ /// Returns all cached Obj with IDs
///
- /// Array of Manga-IDs
+ /// Array of Obj-IDs
///
[HttpPost("WithIDs")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetManga([FromBody]string[] ids)
{
- Manga[] ret = context.Mangas.Where(m => ids.Contains(m.MangaId)).ToArray();
+ Manga[] ret = context.Mangas.Where(m => ids.Contains(m.Key)).ToArray();
return Ok(ret);
}
///
- /// Return Manga with ID
+ /// Return Obj with ID
///
- /// Manga-ID
+ /// Obj-ID
///
- /// Manga with ID not found
+ /// Obj with ID not found
[HttpGet("{MangaId}")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
@@ -62,11 +63,11 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Delete Manga with ID
+ /// Delete Obj with ID
///
- /// Manga-ID
+ /// Obj-ID
///
- /// Manga with ID not found
+ /// Obj with ID not found
/// Error during Database Operation
[HttpDelete("{MangaId}")]
[ProducesResponseType(Status200OK)]
@@ -91,16 +92,45 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
}
+
///
- /// Returns Cover of Manga
+ /// Merge two Manga into one. THIS IS NOT REVERSIBLE!
///
- /// Manga-ID
+ ///
+ /// MangaId not found
+ /// Error during Database Operation
+ [HttpPatch("{MangaIdFrom}/MergeInto/{MangaIdTo}")]
+ [ProducesResponseType(Status200OK,"image/jpeg")]
+ [ProducesResponseType(Status404NotFound)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult MergeIntoManga(string MangaIdFrom, string MangaIdTo)
+ {
+ if(context.Mangas.Find(MangaIdFrom) is not { } from)
+ return NotFound(MangaIdFrom);
+ if(context.Mangas.Find(MangaIdTo) is not { } to)
+ return NotFound(MangaIdTo);
+ try
+ {
+ to.MergeFrom(from, context);
+ return Ok();
+ }
+ catch (DbUpdateException e)
+ {
+ Log.Error(e);
+ return StatusCode(500, e.Message);
+ }
+ }
+
+ ///
+ /// Returns Cover of Obj
+ ///
+ /// Obj-ID
/// If width is provided, height needs to also be provided
/// If height is provided, width needs to also be provided
/// JPEG Image
/// Cover not loaded
/// The formatting-request was invalid
- /// Manga with ID not found
+ /// Obj with ID not found
/// Retry later, downloading cover
[HttpGet("{MangaId}/Cover")]
[ProducesResponseType(Status200OK,"image/jpeg")]
@@ -115,7 +145,7 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
if (!System.IO.File.Exists(m.CoverFileNameInCache))
{
- List coverDownloadJobs = context.Jobs.Where(j => j.JobType == JobType.DownloadMangaCoverJob).ToList();
+ List coverDownloadJobs = context.Jobs.Where(j => j.JobType == JobType.DownloadMangaCoverJob).Include(j => ((DownloadMangaCoverJob)j).Manga).ToList();
if (coverDownloadJobs.Any(j => j is DownloadMangaCoverJob dmc && dmc.MangaId == MangaId && dmc.state < JobState.Completed))
{
Response.Headers.Append("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * coverDownloadJobs.Count() * 2 / 1000:D}");
@@ -146,11 +176,11 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns all Chapters of Manga
+ /// Returns all Chapters of Obj
///
- /// Manga-ID
+ /// Obj-ID
///
- /// Manga with ID not found
+ /// Obj with ID not found
[HttpGet("{MangaId}/Chapters")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
@@ -164,12 +194,12 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns all downloaded Chapters for Manga with ID
+ /// Returns all downloaded Chapters for Obj with ID
///
- /// Manga-ID
+ /// Obj-ID
///
/// No available chapters
- /// Manga with ID not found.
+ /// Obj with ID not found.
[HttpGet("{MangaId}/Chapters/Downloaded")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status204NoContent)]
@@ -187,12 +217,12 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns all Chapters not downloaded for Manga with ID
+ /// Returns all Chapters not downloaded for Obj with ID
///
- /// Manga-ID
+ /// Obj-ID
///
/// No available chapters
- /// Manga with ID not found.
+ /// Obj with ID not found.
[HttpGet("{MangaId}/Chapters/NotDownloaded")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status204NoContent)]
@@ -210,12 +240,12 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns the latest Chapter of requested Manga available on Website
+ /// Returns the latest Chapter of requested Obj available on Website
///
- /// Manga-ID
+ /// Obj-ID
///
/// No available chapters
- /// Manga with ID not found.
+ /// Obj with ID not found.
/// Could not retrieve the maximum chapter-number
/// Retry after timeout, updating value
[HttpGet("{MangaId}/Chapter/LatestAvailable")]
@@ -232,7 +262,7 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
List chapters = m.Chapters.ToList();
if (chapters.Count == 0)
{
- List retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).ToList();
+ List retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).Include(j => ((RetrieveChaptersJob)j).Manga).ToList();
if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId && rcj.state < JobState.Completed))
{
Response.Headers.Append("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
@@ -249,12 +279,12 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns the latest Chapter of requested Manga that is downloaded
+ /// Returns the latest Chapter of requested Obj that is downloaded
///
- /// Manga-ID
+ /// Obj-ID
///
/// No available chapters
- /// Manga with ID not found.
+ /// Obj with ID not found.
/// Could not retrieve the maximum chapter-number
/// Retry after timeout, updating value
[HttpGet("{MangaId}/Chapter/LatestDownloaded")]
@@ -271,7 +301,7 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
List chapters = m.Chapters.ToList();
if (chapters.Count == 0)
{
- List retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).ToList();
+ List retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).Include(j => ((RetrieveChaptersJob)j).Manga).ToList();
if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId && rcj.state < JobState.Completed))
{
Response.Headers.Append("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
@@ -288,12 +318,12 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Configure the cut-off for Manga
+ /// Configure the cut-off for Obj
///
- /// Manga-ID
+ /// Obj-ID
/// Threshold (Chapter Number)
///
- /// Manga with ID not found.
+ /// Obj with ID not found.
/// Error during Database Operation
[HttpPatch("{MangaId}/IgnoreChaptersBefore")]
[ProducesResponseType(Status200OK)]
@@ -319,10 +349,10 @@ public class MangaController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Move Manga to different ToLibrary
+ /// Move Obj to different ToFileLibrary
///
- /// Manga-ID
- /// ToLibrary-Id
+ /// Obj-ID
+ /// ToFileLibrary-Id
/// Folder is going to be moved
/// MangaId or LibraryId not found
/// Error during Database Operation
diff --git a/API/Controllers/NotificationConnectorController.cs b/API/Controllers/NotificationConnectorController.cs
index c0100e0..4ef4655 100644
--- a/API/Controllers/NotificationConnectorController.cs
+++ b/API/Controllers/NotificationConnectorController.cs
@@ -95,7 +95,7 @@ public class NotificationConnectorController(NotificationsContext context, ILog
NotificationConnector gotifyConnector = new NotificationConnector(TokenGen.CreateToken("Gotify"),
gotifyData.endpoint,
- new Dictionary() { { "X-Gotify-Key", gotifyData.appToken } },
+ new Dictionary() { { "X-Gotify-IDOnConnector", gotifyData.appToken } },
"POST",
$"{{\"message\": \"%text\", \"title\": \"%title\", \"priority\": {gotifyData.priority}}}");
return CreateConnector(gotifyConnector);
diff --git a/API/Controllers/QueryController.cs b/API/Controllers/QueryController.cs
index f4d603f..bfa2160 100644
--- a/API/Controllers/QueryController.cs
+++ b/API/Controllers/QueryController.cs
@@ -69,18 +69,18 @@ public class QueryController(PgsqlContext context, ILog Log) : Controller
///
/// AltTitle with ID not found
[HttpGet("AltTitle/{AltTitleId}")]
- [ProducesResponseType(Status200OK, "application/json")]
+ [ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
public IActionResult GetAltTitle(string AltTitleId)
{
- MangaAltTitle? ret = context.AltTitles.Find(AltTitleId);
+ AltTitle? ret = context.AltTitles.Find(AltTitleId);
if (ret is null)
return NotFound();
return Ok(ret);
}*/
///
- /// Returns all Manga with Tag
+ /// Returns all Obj with Tag
///
///
///
diff --git a/API/Controllers/SearchController.cs b/API/Controllers/SearchController.cs
index 5af0ed2..7b10ec3 100644
--- a/API/Controllers/SearchController.cs
+++ b/API/Controllers/SearchController.cs
@@ -1,7 +1,5 @@
using API.Schema;
using API.Schema.Contexts;
-using API.Schema.Jobs;
-using API.Schema.MangaConnectors;
using Asp.Versioning;
using log4net;
using Microsoft.AspNetCore.Mvc;
@@ -18,7 +16,7 @@ namespace API.Controllers;
public class SearchController(PgsqlContext context, ILog Log) : Controller
{
///
- /// Initiate a search for a Manga on a specific Connector
+ /// Initiate a search for a Obj on a specific Connector
///
///
///
@@ -38,9 +36,9 @@ public class SearchController(PgsqlContext context, ILog Log) : Controller
else if (connector.Enabled is false)
return StatusCode(Status406NotAcceptable);
- Manga[] mangas = connector.SearchManga(Query);
+ (Manga, MangaConnectorId)[] mangas = connector.SearchManga(Query);
List retMangas = new();
- foreach (Manga manga in mangas)
+ foreach ((Manga manga, MangaConnectorId mcId) manga in mangas)
{
try
{
@@ -58,7 +56,7 @@ public class SearchController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Search for a known Manga
+ /// Search for a known Obj
///
///
///
@@ -73,12 +71,12 @@ public class SearchController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Returns Manga from MangaConnector associated with URL
+ /// Returns Obj from MangaConnector associated with URL
///
- /// Manga-Page URL
+ /// Obj-Page URL
///
/// Multiple connectors found for URL
- /// Manga not found
+ /// Obj not found
/// Error during Database Operation
[HttpPost("Url")]
[ProducesResponseType(Status200OK, "application/json")]
@@ -104,12 +102,13 @@ public class SearchController(PgsqlContext context, ILog Log) : Controller
}
}
- private Manga? AddMangaToContext(Manga manga)
+ private Manga? AddMangaToContext((Manga, MangaConnectorId) manga) => AddMangaToContext(manga.Item1, manga.Item2, context);
+
+ internal static Manga? AddMangaToContext(Manga addManga, MangaConnectorId addMcId, PgsqlContext context)
{
- context.Mangas.Load();
- context.Authors.Load();
- context.Tags.Load();
- context.MangaConnectors.Load();
+ Manga manga = context.Mangas.Find(addManga.Key) ?? addManga;
+ MangaConnectorId mcId = context.MangaConnectorToManga.Find(addMcId.Key) ?? addMcId;
+ mcId.Obj = manga;
IEnumerable mergedTags = manga.MangaTags.Select(mt =>
{
@@ -120,26 +119,19 @@ public class SearchController(PgsqlContext context, ILog Log) : Controller
IEnumerable mergedAuthors = manga.Authors.Select(ma =>
{
- Author? inDb = context.Authors.Find(ma.AuthorId);
+ Author? inDb = context.Authors.Find(ma.Key);
return inDb ?? ma;
});
manga.Authors = mergedAuthors.ToList();
-
+
try
{
-
- if (context.Mangas.Find(manga.MangaId) is { } r)
- {
- context.Mangas.Remove(r);
- context.SaveChanges();
- }
- context.Mangas.Add(manga);
- context.Jobs.Add(new DownloadMangaCoverJob(manga));
+ if(context.MangaConnectorToManga.Find(addMcId.Key) is null)
+ context.MangaConnectorToManga.Add(mcId);
context.SaveChanges();
}
catch (DbUpdateException e)
{
- Log.Error(e);
return null;
}
return manga;
diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs
index 2c060f3..3fdc2e3 100644
--- a/API/Controllers/SettingsController.cs
+++ b/API/Controllers/SettingsController.cs
@@ -1,12 +1,10 @@
-using System.Net.Http.Headers;
-using API.MangaDownloadClients;
+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;
using Newtonsoft.Json.Linq;
using static Microsoft.AspNetCore.Http.StatusCodes;
@@ -210,14 +208,14 @@ public class SettingsController(PgsqlContext context, ILog Log) : Controller
///
///
/// Placeholders:
- /// %M Manga Name
+ /// %M Obj Name
/// %V Volume
/// %C Chapter
/// %T Title
/// %A Author (first in list)
/// %I Chapter Internal ID
- /// %i Manga Internal ID
- /// %Y Year (Manga)
+ /// %i Obj Internal ID
+ /// %Y Year (Obj)
///
/// ?_(...) replace _ with a value from above:
/// Everything inside the braces will only be added if the value of %_ is not null
@@ -235,14 +233,14 @@ public class SettingsController(PgsqlContext context, ILog Log) : Controller
///
///
/// Placeholders:
- /// %M Manga Name
+ /// %M Obj Name
/// %V Volume
/// %C Chapter
/// %T Title
/// %A Author (first in list)
/// %I Chapter Internal ID
- /// %i Manga Internal ID
- /// %Y Year (Manga)
+ /// %i Obj Internal ID
+ /// %Y Year (Obj)
///
/// ?_(...) replace _ with a value from above:
/// Everything inside the braces will only be added if the value of %_ is not null
@@ -271,7 +269,7 @@ public class SettingsController(PgsqlContext context, ILog Log) : Controller
}
///
- /// Creates a UpdateCoverJob for all Manga
+ /// Creates a UpdateCoverJob for all Obj
///
/// Array of JobIds
/// Error during Database Operation
@@ -285,7 +283,7 @@ public class SettingsController(PgsqlContext context, ILog Log) : Controller
Tranga.RemoveStaleFiles(context);
List newJobs = context.Mangas.ToList().Select(m => new UpdateCoverJob(m, 0)).ToList();
context.Jobs.AddRange(newJobs);
- return Ok(newJobs.Select(j => j.JobId));
+ return Ok(newJobs.Select(j => j.Key));
}
catch (Exception e)
{
diff --git a/API/JobQueueSortable.cs b/API/JobQueueSortable.cs
deleted file mode 100644
index 0064f05..0000000
--- a/API/JobQueueSortable.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using API.Schema.Jobs;
-
-namespace API;
-
-internal static class JobQueueSorter
-{
- public static readonly Dictionary JobTypePriority = new()
- {
-
- { JobType.DownloadSingleChapterJob, 50 },
- { JobType.DownloadAvailableChaptersJob, 51 },
- { JobType.MoveFileOrFolderJob, 102 },
- { JobType.DownloadMangaCoverJob, 10 },
- { JobType.RetrieveChaptersJob, 52 },
- { JobType.UpdateChaptersDownloadedJob, 90 },
- { JobType.MoveMangaLibraryJob, 101 },
- { JobType.UpdateCoverJob, 11 },
- };
-
- public static byte GetPriority(Job job)
- {
- return JobTypePriority[job.JobType];
- }
-
- public static byte GetPriority(JobType jobType)
- {
- return JobTypePriority[jobType];
- }
-
- public static IEnumerable Sort(this IEnumerable jobQueueSortables)
- {
- return jobQueueSortables.Order();
- }
-
- public static IEnumerable GetStartableJobs(this IEnumerable jobQueueSortables)
- {
- Job[] sorted = jobQueueSortables.Order().ToArray();
- // Job has to be due, no missing dependenices
- // Index - 1, Index is first job that does not match requirements
- IEnumerable<(int Index, Job Item)> index = sorted.Index();
- (int i, Job? item) = index.FirstOrDefault(job =>
- job.Item.NextExecution > DateTime.UtcNow || job.Item.GetDependencies().Any(j => !j.IsCompleted));
- if (item is null)
- return sorted;
- index.
- }
-}
\ No newline at end of file
diff --git a/API/MangaDownloadClients/ChromiumDownloadClient.cs b/API/MangaDownloadClients/ChromiumDownloadClient.cs
index 228624e..acde118 100644
--- a/API/MangaDownloadClients/ChromiumDownloadClient.cs
+++ b/API/MangaDownloadClients/ChromiumDownloadClient.cs
@@ -43,7 +43,7 @@ internal class ChromiumDownloadClient : DownloadClient
{
Thread.Sleep(TimeSpan.FromHours(1));
Log.Debug("Removing stale pages");
- foreach ((IPage? key, DateTime value) in _openPages.Where(kv => kv.Value.Subtract(DateTime.Now) > TimeSpan.FromHours(1)))
+ foreach ((IPage key, DateTime _) in _openPages.Where(kv => kv.Value.Subtract(DateTime.Now) > TimeSpan.FromHours(1)))
{
Log.Debug($"Closing {key.Url}");
key.CloseAsync().Wait();
diff --git a/API/MangaDownloadClients/FlareSolverrDownloadClient.cs b/API/MangaDownloadClients/FlareSolverrDownloadClient.cs
index b76e090..03069db 100644
--- a/API/MangaDownloadClients/FlareSolverrDownloadClient.cs
+++ b/API/MangaDownloadClients/FlareSolverrDownloadClient.cs
@@ -172,7 +172,7 @@ public class FlareSolverrDownloadClient : DownloadClient
jsonString = pre.InnerText;
return true;
}
- catch (JsonReaderException)
+ catch (Exception)
{
return false;
}
diff --git a/API/Migrations/pgsql/20250630182650_OofV2.1.Designer.cs b/API/Migrations/pgsql/20250630182650_OofV2.1.Designer.cs
new file mode 100644
index 0000000..372412f
--- /dev/null
+++ b/API/Migrations/pgsql/20250630182650_OofV2.1.Designer.cs
@@ -0,0 +1,795 @@
+//
+using System;
+using API.Schema.Contexts;
+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.pgsql
+{
+ [DbContext(typeof(PgsqlContext))]
+ [Migration("20250630182650_OofV2.1")]
+ partial class OofV21
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("API.Schema.Author", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("AuthorName")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.HasKey("Key");
+
+ b.ToTable("Authors");
+ });
+
+ modelBuilder.Entity("API.Schema.Chapter", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ 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("VolumeNumber")
+ .HasColumnType("integer");
+
+ b.HasKey("Key");
+
+ b.HasIndex("ParentMangaId");
+
+ b.ToTable("Chapters");
+ });
+
+ modelBuilder.Entity("API.Schema.FileLibrary", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("BasePath")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("LibraryName")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.HasKey("Key");
+
+ b.ToTable("LocalLibraries");
+ });
+
+ modelBuilder.Entity("API.Schema.Jobs.Job", b =>
+ {
+ b.Property("Key")
+ .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("Key");
+
+ b.HasIndex("ParentJobId");
+
+ b.ToTable("Jobs");
+
+ b.HasDiscriminator("JobType");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("API.Schema.Manga", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("CoverFileNameInCache")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("CoverUrl")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DirectoryName")
+ .IsRequired()
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("IgnoreChaptersBefore")
+ .HasColumnType("real");
+
+ b.Property("LibraryId")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("OriginalLanguage")
+ .HasMaxLength(8)
+ .HasColumnType("character varying(8)");
+
+ b.Property("ReleaseStatus")
+ .HasColumnType("smallint");
+
+ b.Property("Year")
+ .HasColumnType("bigint");
+
+ b.HasKey("Key");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("Mangas");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("IdOnConnectorSite")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("MangaConnectorName")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ObjId")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("WebsiteUrl")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.HasKey("Key");
+
+ b.HasIndex("MangaConnectorName");
+
+ b.HasIndex("ObjId");
+
+ b.ToTable("MangaConnectorToChapter");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("IdOnConnectorSite")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("MangaConnectorName")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ObjId")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("WebsiteUrl")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.HasKey("Key");
+
+ b.HasIndex("MangaConnectorName");
+
+ b.HasIndex("ObjId");
+
+ b.ToTable("MangaConnectorToManga");
+ });
+
+ 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("AuthorToManga", b =>
+ {
+ b.Property("AuthorIds")
+ .HasColumnType("text");
+
+ b.Property("MangaIds")
+ .HasColumnType("text");
+
+ b.HasKey("AuthorIds", "MangaIds");
+
+ b.HasIndex("MangaIds");
+
+ b.ToTable("AuthorToManga");
+ });
+
+ modelBuilder.Entity("JobJob", b =>
+ {
+ b.Property("DependsOnJobsKey")
+ .HasColumnType("text");
+
+ b.Property("JobKey")
+ .HasColumnType("text");
+
+ b.HasKey("DependsOnJobsKey", "JobKey");
+
+ b.HasIndex("JobKey");
+
+ b.ToTable("JobJob");
+ });
+
+ modelBuilder.Entity("MangaTagToManga", b =>
+ {
+ b.Property("MangaTagIds")
+ .HasColumnType("character varying(64)");
+
+ b.Property("MangaIds")
+ .HasColumnType("text");
+
+ b.HasKey("MangaTagIds", "MangaIds");
+
+ b.HasIndex("MangaIds");
+
+ b.ToTable("MangaTagToManga");
+ });
+
+ 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.MoveMangaLibraryJob", b =>
+ {
+ b.HasBaseType("API.Schema.Jobs.Job");
+
+ b.Property("MangaId")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("ToLibraryId")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.HasIndex("MangaId");
+
+ b.HasIndex("ToLibraryId");
+
+ b.ToTable("Jobs", t =>
+ {
+ t.Property("MangaId")
+ .HasColumnName("MoveMangaLibraryJob_MangaId");
+ });
+
+ b.HasDiscriminator().HasValue((byte)7);
+ });
+
+ modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
+ {
+ b.HasBaseType("API.Schema.Jobs.Job");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasMaxLength(8)
+ .HasColumnType("character varying(8)");
+
+ 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.UpdateChaptersDownloadedJob", 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("UpdateChaptersDownloadedJob_MangaId");
+ });
+
+ b.HasDiscriminator().HasValue((byte)6);
+ });
+
+ modelBuilder.Entity("API.Schema.Jobs.UpdateCoverJob", 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("UpdateCoverJob_MangaId");
+ });
+
+ b.HasDiscriminator().HasValue((byte)9);
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectors.ComickIo", b =>
+ {
+ b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
+
+ b.HasDiscriminator().HasValue("ComickIo");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectors.Global", b =>
+ {
+ b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
+
+ b.HasDiscriminator().HasValue("Global");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectors.MangaDex", b =>
+ {
+ b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
+
+ b.HasDiscriminator().HasValue("MangaDex");
+ });
+
+ modelBuilder.Entity("API.Schema.Chapter", b =>
+ {
+ b.HasOne("API.Schema.Manga", "ParentManga")
+ .WithMany("Chapters")
+ .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.Manga", b =>
+ {
+ b.HasOne("API.Schema.FileLibrary", "Library")
+ .WithMany()
+ .HasForeignKey("LibraryId")
+ .OnDelete(DeleteBehavior.SetNull);
+
+ b.OwnsMany("API.Schema.AltTitle", "AltTitles", b1 =>
+ {
+ b1.Property("Key")
+ .HasColumnType("text");
+
+ b1.Property("Language")
+ .IsRequired()
+ .HasMaxLength(8)
+ .HasColumnType("character varying(8)");
+
+ b1.Property("MangaKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b1.Property("Title")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b1.HasKey("Key");
+
+ b1.HasIndex("MangaKey");
+
+ b1.ToTable("AltTitle");
+
+ b1.WithOwner()
+ .HasForeignKey("MangaKey");
+ });
+
+ b.OwnsMany("API.Schema.Link", "Links", b1 =>
+ {
+ b1.Property("Key")
+ .HasColumnType("text");
+
+ b1.Property("LinkProvider")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b1.Property("LinkUrl")
+ .IsRequired()
+ .HasMaxLength(2048)
+ .HasColumnType("character varying(2048)");
+
+ b1.Property("MangaKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b1.HasKey("Key");
+
+ b1.HasIndex("MangaKey");
+
+ b1.ToTable("Link");
+
+ b1.WithOwner()
+ .HasForeignKey("MangaKey");
+ });
+
+ b.Navigation("AltTitles");
+
+ b.Navigation("Library");
+
+ b.Navigation("Links");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
+ .WithMany()
+ .HasForeignKey("MangaConnectorName")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.Chapter", "Obj")
+ .WithMany("MangaConnectorIds")
+ .HasForeignKey("ObjId")
+ .OnDelete(DeleteBehavior.NoAction)
+ .IsRequired();
+
+ b.Navigation("MangaConnector");
+
+ b.Navigation("Obj");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
+ .WithMany()
+ .HasForeignKey("MangaConnectorName")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.Manga", "Obj")
+ .WithMany("MangaConnectorIds")
+ .HasForeignKey("ObjId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("MangaConnector");
+
+ b.Navigation("Obj");
+ });
+
+ modelBuilder.Entity("AuthorToManga", b =>
+ {
+ b.HasOne("API.Schema.Author", null)
+ .WithMany()
+ .HasForeignKey("AuthorIds")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.Manga", null)
+ .WithMany()
+ .HasForeignKey("MangaIds")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("JobJob", b =>
+ {
+ b.HasOne("API.Schema.Jobs.Job", null)
+ .WithMany()
+ .HasForeignKey("DependsOnJobsKey")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.Jobs.Job", null)
+ .WithMany()
+ .HasForeignKey("JobKey")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("MangaTagToManga", b =>
+ {
+ b.HasOne("API.Schema.Manga", null)
+ .WithMany()
+ .HasForeignKey("MangaIds")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.MangaTag", null)
+ .WithMany()
+ .HasForeignKey("MangaTagIds")
+ .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.MoveMangaLibraryJob", b =>
+ {
+ b.HasOne("API.Schema.Manga", "Manga")
+ .WithMany()
+ .HasForeignKey("MangaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.FileLibrary", "ToFileLibrary")
+ .WithMany()
+ .HasForeignKey("ToLibraryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Manga");
+
+ b.Navigation("ToFileLibrary");
+ });
+
+ 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.UpdateChaptersDownloadedJob", b =>
+ {
+ b.HasOne("API.Schema.Manga", "Manga")
+ .WithMany()
+ .HasForeignKey("MangaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Manga");
+ });
+
+ modelBuilder.Entity("API.Schema.Jobs.UpdateCoverJob", b =>
+ {
+ b.HasOne("API.Schema.Manga", "Manga")
+ .WithMany()
+ .HasForeignKey("MangaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Manga");
+ });
+
+ modelBuilder.Entity("API.Schema.Chapter", b =>
+ {
+ b.Navigation("MangaConnectorIds");
+ });
+
+ modelBuilder.Entity("API.Schema.Manga", b =>
+ {
+ b.Navigation("Chapters");
+
+ b.Navigation("MangaConnectorIds");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/API/Migrations/pgsql/20250630182650_OofV2.1.cs b/API/Migrations/pgsql/20250630182650_OofV2.1.cs
new file mode 100644
index 0000000..a59670f
--- /dev/null
+++ b/API/Migrations/pgsql/20250630182650_OofV2.1.cs
@@ -0,0 +1,1055 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace API.Migrations.pgsql
+{
+ ///
+ public partial class OofV21 : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_AuthorToManga_Authors_AuthorIds",
+ table: "AuthorToManga");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_AuthorToManga_Mangas_MangaIds",
+ table: "AuthorToManga");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Chapters_Mangas_ParentMangaId",
+ table: "Chapters");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_JobJob_Jobs_DependsOnJobsJobId",
+ table: "JobJob");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_JobJob_Jobs_JobId",
+ table: "JobJob");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Chapters_ChapterId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Jobs_ParentJobId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_LocalLibraries_ToLibraryId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_DownloadAvailableChaptersJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_MoveMangaLibraryJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_RetrieveChaptersJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_UpdateChaptersDownloadedJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_UpdateCoverJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Link_Mangas_MangaId",
+ table: "Link");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Mangas_LocalLibraries_LibraryId",
+ table: "Mangas");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Mangas_MangaConnectors_MangaConnectorName",
+ table: "Mangas");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_MangaTagToManga_Mangas_MangaIds",
+ table: "MangaTagToManga");
+
+ migrationBuilder.DropTable(
+ name: "MangaAltTitle");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Mangas",
+ table: "Mangas");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Mangas_MangaConnectorName",
+ table: "Mangas");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_LocalLibraries",
+ table: "LocalLibraries");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Link",
+ table: "Link");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Link_MangaId",
+ table: "Link");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Jobs",
+ table: "Jobs");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_JobJob",
+ table: "JobJob");
+
+ migrationBuilder.DropIndex(
+ name: "IX_JobJob_JobId",
+ table: "JobJob");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Chapters",
+ table: "Chapters");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Authors",
+ table: "Authors");
+
+ migrationBuilder.DropColumn(
+ name: "MangaId",
+ table: "Mangas");
+
+ migrationBuilder.DropColumn(
+ name: "IdOnConnectorSite",
+ table: "Mangas");
+
+ migrationBuilder.DropColumn(
+ name: "MangaConnectorName",
+ table: "Mangas");
+
+ migrationBuilder.DropColumn(
+ name: "WebsiteUrl",
+ table: "Mangas");
+
+ migrationBuilder.DropColumn(
+ name: "LocalLibraryId",
+ table: "LocalLibraries");
+
+ migrationBuilder.DropColumn(
+ name: "LinkId",
+ table: "Link");
+
+ migrationBuilder.DropColumn(
+ name: "MangaId",
+ table: "Link");
+
+ migrationBuilder.DropColumn(
+ name: "JobId",
+ table: "Jobs");
+
+ migrationBuilder.DropColumn(
+ name: "DependsOnJobsJobId",
+ table: "JobJob");
+
+ migrationBuilder.DropColumn(
+ name: "JobId",
+ table: "JobJob");
+
+ migrationBuilder.DropColumn(
+ name: "ChapterId",
+ table: "Chapters");
+
+ migrationBuilder.DropColumn(
+ name: "IdOnConnectorSite",
+ table: "Chapters");
+
+ migrationBuilder.DropColumn(
+ name: "Url",
+ table: "Chapters");
+
+ migrationBuilder.DropColumn(
+ name: "AuthorId",
+ table: "Authors");
+
+ migrationBuilder.AlterColumn(
+ name: "MangaIds",
+ table: "MangaTagToManga",
+ type: "text",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "character varying(64)");
+
+ migrationBuilder.AddColumn(
+ name: "Key",
+ table: "Mangas",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "Key",
+ table: "LocalLibraries",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "Key",
+ table: "Link",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "MangaKey",
+ table: "Link",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "Key",
+ table: "Jobs",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "DependsOnJobsKey",
+ table: "JobJob",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "JobKey",
+ table: "JobJob",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "Key",
+ table: "Chapters",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AlterColumn(
+ name: "MangaIds",
+ table: "AuthorToManga",
+ type: "text",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "character varying(64)");
+
+ migrationBuilder.AlterColumn(
+ name: "AuthorIds",
+ table: "AuthorToManga",
+ type: "text",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "character varying(64)");
+
+ migrationBuilder.AddColumn(
+ name: "Key",
+ table: "Authors",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Mangas",
+ table: "Mangas",
+ column: "Key");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_LocalLibraries",
+ table: "LocalLibraries",
+ column: "Key");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Link",
+ table: "Link",
+ column: "Key");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Jobs",
+ table: "Jobs",
+ column: "Key");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_JobJob",
+ table: "JobJob",
+ columns: new[] { "DependsOnJobsKey", "JobKey" });
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Chapters",
+ table: "Chapters",
+ column: "Key");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Authors",
+ table: "Authors",
+ column: "Key");
+
+ migrationBuilder.CreateTable(
+ name: "AltTitle",
+ columns: table => new
+ {
+ Key = table.Column(type: "text", nullable: false),
+ Language = table.Column(type: "character varying(8)", maxLength: 8, nullable: false),
+ Title = table.Column(type: "character varying(256)", maxLength: 256, nullable: false),
+ MangaKey = table.Column(type: "text", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AltTitle", x => x.Key);
+ table.ForeignKey(
+ name: "FK_AltTitle_Mangas_MangaKey",
+ column: x => x.MangaKey,
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "MangaConnectorToChapter",
+ columns: table => new
+ {
+ Key = table.Column(type: "text", nullable: false),
+ ObjId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ MangaConnectorName = table.Column(type: "character varying(32)", maxLength: 32, nullable: false),
+ IdOnConnectorSite = table.Column(type: "character varying(256)", maxLength: 256, nullable: false),
+ WebsiteUrl = table.Column(type: "character varying(512)", maxLength: 512, nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_MangaConnectorToChapter", x => x.Key);
+ table.ForeignKey(
+ name: "FK_MangaConnectorToChapter_Chapters_ObjId",
+ column: x => x.ObjId,
+ principalTable: "Chapters",
+ principalColumn: "Key");
+ table.ForeignKey(
+ name: "FK_MangaConnectorToChapter_MangaConnectors_MangaConnectorName",
+ column: x => x.MangaConnectorName,
+ principalTable: "MangaConnectors",
+ principalColumn: "Name",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "MangaConnectorToManga",
+ columns: table => new
+ {
+ Key = table.Column(type: "text", nullable: false),
+ ObjId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ MangaConnectorName = table.Column(type: "character varying(32)", maxLength: 32, nullable: false),
+ IdOnConnectorSite = table.Column(type: "character varying(256)", maxLength: 256, nullable: false),
+ WebsiteUrl = table.Column(type: "character varying(512)", maxLength: 512, nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_MangaConnectorToManga", x => x.Key);
+ table.ForeignKey(
+ name: "FK_MangaConnectorToManga_MangaConnectors_MangaConnectorName",
+ column: x => x.MangaConnectorName,
+ principalTable: "MangaConnectors",
+ principalColumn: "Name",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_MangaConnectorToManga_Mangas_ObjId",
+ column: x => x.ObjId,
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Link_MangaKey",
+ table: "Link",
+ column: "MangaKey");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_JobJob_JobKey",
+ table: "JobJob",
+ column: "JobKey");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AltTitle_MangaKey",
+ table: "AltTitle",
+ column: "MangaKey");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MangaConnectorToChapter_MangaConnectorName",
+ table: "MangaConnectorToChapter",
+ column: "MangaConnectorName");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MangaConnectorToChapter_ObjId",
+ table: "MangaConnectorToChapter",
+ column: "ObjId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MangaConnectorToManga_MangaConnectorName",
+ table: "MangaConnectorToManga",
+ column: "MangaConnectorName");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MangaConnectorToManga_ObjId",
+ table: "MangaConnectorToManga",
+ column: "ObjId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_AuthorToManga_Authors_AuthorIds",
+ table: "AuthorToManga",
+ column: "AuthorIds",
+ principalTable: "Authors",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_AuthorToManga_Mangas_MangaIds",
+ table: "AuthorToManga",
+ column: "MangaIds",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Chapters_Mangas_ParentMangaId",
+ table: "Chapters",
+ column: "ParentMangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_JobJob_Jobs_DependsOnJobsKey",
+ table: "JobJob",
+ column: "DependsOnJobsKey",
+ principalTable: "Jobs",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_JobJob_Jobs_JobKey",
+ table: "JobJob",
+ column: "JobKey",
+ principalTable: "Jobs",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Chapters_ChapterId",
+ table: "Jobs",
+ column: "ChapterId",
+ principalTable: "Chapters",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Jobs_ParentJobId",
+ table: "Jobs",
+ column: "ParentJobId",
+ principalTable: "Jobs",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_LocalLibraries_ToLibraryId",
+ table: "Jobs",
+ column: "ToLibraryId",
+ principalTable: "LocalLibraries",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_DownloadAvailableChaptersJob_MangaId",
+ table: "Jobs",
+ column: "DownloadAvailableChaptersJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_MangaId",
+ table: "Jobs",
+ column: "MangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_MoveMangaLibraryJob_MangaId",
+ table: "Jobs",
+ column: "MoveMangaLibraryJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_RetrieveChaptersJob_MangaId",
+ table: "Jobs",
+ column: "RetrieveChaptersJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_UpdateChaptersDownloadedJob_MangaId",
+ table: "Jobs",
+ column: "UpdateChaptersDownloadedJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_UpdateCoverJob_MangaId",
+ table: "Jobs",
+ column: "UpdateCoverJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Link_Mangas_MangaKey",
+ table: "Link",
+ column: "MangaKey",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Mangas_LocalLibraries_LibraryId",
+ table: "Mangas",
+ column: "LibraryId",
+ principalTable: "LocalLibraries",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.SetNull);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_MangaTagToManga_Mangas_MangaIds",
+ table: "MangaTagToManga",
+ column: "MangaIds",
+ principalTable: "Mangas",
+ principalColumn: "Key",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_AuthorToManga_Authors_AuthorIds",
+ table: "AuthorToManga");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_AuthorToManga_Mangas_MangaIds",
+ table: "AuthorToManga");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Chapters_Mangas_ParentMangaId",
+ table: "Chapters");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_JobJob_Jobs_DependsOnJobsKey",
+ table: "JobJob");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_JobJob_Jobs_JobKey",
+ table: "JobJob");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Chapters_ChapterId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Jobs_ParentJobId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_LocalLibraries_ToLibraryId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_DownloadAvailableChaptersJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_MoveMangaLibraryJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_RetrieveChaptersJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_UpdateChaptersDownloadedJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Jobs_Mangas_UpdateCoverJob_MangaId",
+ table: "Jobs");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Link_Mangas_MangaKey",
+ table: "Link");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Mangas_LocalLibraries_LibraryId",
+ table: "Mangas");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_MangaTagToManga_Mangas_MangaIds",
+ table: "MangaTagToManga");
+
+ migrationBuilder.DropTable(
+ name: "AltTitle");
+
+ migrationBuilder.DropTable(
+ name: "MangaConnectorToChapter");
+
+ migrationBuilder.DropTable(
+ name: "MangaConnectorToManga");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Mangas",
+ table: "Mangas");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_LocalLibraries",
+ table: "LocalLibraries");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Link",
+ table: "Link");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Link_MangaKey",
+ table: "Link");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Jobs",
+ table: "Jobs");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_JobJob",
+ table: "JobJob");
+
+ migrationBuilder.DropIndex(
+ name: "IX_JobJob_JobKey",
+ table: "JobJob");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Chapters",
+ table: "Chapters");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_Authors",
+ table: "Authors");
+
+ migrationBuilder.DropColumn(
+ name: "Key",
+ table: "Mangas");
+
+ migrationBuilder.DropColumn(
+ name: "Key",
+ table: "LocalLibraries");
+
+ migrationBuilder.DropColumn(
+ name: "Key",
+ table: "Link");
+
+ migrationBuilder.DropColumn(
+ name: "MangaKey",
+ table: "Link");
+
+ migrationBuilder.DropColumn(
+ name: "Key",
+ table: "Jobs");
+
+ migrationBuilder.DropColumn(
+ name: "DependsOnJobsKey",
+ table: "JobJob");
+
+ migrationBuilder.DropColumn(
+ name: "JobKey",
+ table: "JobJob");
+
+ migrationBuilder.DropColumn(
+ name: "Key",
+ table: "Chapters");
+
+ migrationBuilder.DropColumn(
+ name: "Key",
+ table: "Authors");
+
+ migrationBuilder.AlterColumn(
+ name: "MangaIds",
+ table: "MangaTagToManga",
+ type: "character varying(64)",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "text");
+
+ migrationBuilder.AddColumn(
+ name: "MangaId",
+ table: "Mangas",
+ type: "character varying(64)",
+ maxLength: 64,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "IdOnConnectorSite",
+ table: "Mangas",
+ type: "character varying(256)",
+ maxLength: 256,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "MangaConnectorName",
+ table: "Mangas",
+ type: "character varying(32)",
+ maxLength: 32,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "WebsiteUrl",
+ table: "Mangas",
+ type: "character varying(512)",
+ maxLength: 512,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "LocalLibraryId",
+ table: "LocalLibraries",
+ type: "character varying(64)",
+ maxLength: 64,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "LinkId",
+ table: "Link",
+ type: "character varying(64)",
+ maxLength: 64,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "MangaId",
+ table: "Link",
+ type: "character varying(64)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "JobId",
+ table: "Jobs",
+ type: "character varying(64)",
+ maxLength: 64,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "DependsOnJobsJobId",
+ table: "JobJob",
+ type: "character varying(64)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "JobId",
+ table: "JobJob",
+ type: "character varying(64)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "ChapterId",
+ table: "Chapters",
+ type: "character varying(64)",
+ maxLength: 64,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "IdOnConnectorSite",
+ table: "Chapters",
+ type: "character varying(256)",
+ maxLength: 256,
+ nullable: true);
+
+ migrationBuilder.AddColumn(
+ name: "Url",
+ table: "Chapters",
+ type: "character varying(2048)",
+ maxLength: 2048,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AlterColumn(
+ name: "MangaIds",
+ table: "AuthorToManga",
+ type: "character varying(64)",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "text");
+
+ migrationBuilder.AlterColumn(
+ name: "AuthorIds",
+ table: "AuthorToManga",
+ type: "character varying(64)",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "text");
+
+ migrationBuilder.AddColumn(
+ name: "AuthorId",
+ table: "Authors",
+ type: "character varying(64)",
+ maxLength: 64,
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Mangas",
+ table: "Mangas",
+ column: "MangaId");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_LocalLibraries",
+ table: "LocalLibraries",
+ column: "LocalLibraryId");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Link",
+ table: "Link",
+ column: "LinkId");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Jobs",
+ table: "Jobs",
+ column: "JobId");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_JobJob",
+ table: "JobJob",
+ columns: new[] { "DependsOnJobsJobId", "JobId" });
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Chapters",
+ table: "Chapters",
+ column: "ChapterId");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_Authors",
+ table: "Authors",
+ column: "AuthorId");
+
+ migrationBuilder.CreateTable(
+ name: "MangaAltTitle",
+ columns: table => new
+ {
+ AltTitleId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ Language = table.Column(type: "character varying(8)", maxLength: 8, nullable: false),
+ MangaId = table.Column(type: "character varying(64)", nullable: false),
+ Title = table.Column(type: "character varying(256)", maxLength: 256, nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_MangaAltTitle", x => x.AltTitleId);
+ table.ForeignKey(
+ name: "FK_MangaAltTitle_Mangas_MangaId",
+ column: x => x.MangaId,
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Mangas_MangaConnectorName",
+ table: "Mangas",
+ column: "MangaConnectorName");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Link_MangaId",
+ table: "Link",
+ column: "MangaId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_JobJob_JobId",
+ table: "JobJob",
+ column: "JobId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MangaAltTitle_MangaId",
+ table: "MangaAltTitle",
+ column: "MangaId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_AuthorToManga_Authors_AuthorIds",
+ table: "AuthorToManga",
+ column: "AuthorIds",
+ principalTable: "Authors",
+ principalColumn: "AuthorId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_AuthorToManga_Mangas_MangaIds",
+ table: "AuthorToManga",
+ column: "MangaIds",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Chapters_Mangas_ParentMangaId",
+ table: "Chapters",
+ column: "ParentMangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_JobJob_Jobs_DependsOnJobsJobId",
+ table: "JobJob",
+ column: "DependsOnJobsJobId",
+ principalTable: "Jobs",
+ principalColumn: "JobId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_JobJob_Jobs_JobId",
+ table: "JobJob",
+ column: "JobId",
+ principalTable: "Jobs",
+ principalColumn: "JobId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Chapters_ChapterId",
+ table: "Jobs",
+ column: "ChapterId",
+ principalTable: "Chapters",
+ principalColumn: "ChapterId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Jobs_ParentJobId",
+ table: "Jobs",
+ column: "ParentJobId",
+ principalTable: "Jobs",
+ principalColumn: "JobId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_LocalLibraries_ToLibraryId",
+ table: "Jobs",
+ column: "ToLibraryId",
+ principalTable: "LocalLibraries",
+ principalColumn: "LocalLibraryId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_DownloadAvailableChaptersJob_MangaId",
+ table: "Jobs",
+ column: "DownloadAvailableChaptersJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_MangaId",
+ table: "Jobs",
+ column: "MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_MoveMangaLibraryJob_MangaId",
+ table: "Jobs",
+ column: "MoveMangaLibraryJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_RetrieveChaptersJob_MangaId",
+ table: "Jobs",
+ column: "RetrieveChaptersJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_UpdateChaptersDownloadedJob_MangaId",
+ table: "Jobs",
+ column: "UpdateChaptersDownloadedJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Jobs_Mangas_UpdateCoverJob_MangaId",
+ table: "Jobs",
+ column: "UpdateCoverJob_MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Link_Mangas_MangaId",
+ table: "Link",
+ column: "MangaId",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Mangas_LocalLibraries_LibraryId",
+ table: "Mangas",
+ column: "LibraryId",
+ principalTable: "LocalLibraries",
+ principalColumn: "LocalLibraryId",
+ onDelete: ReferentialAction.SetNull);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Mangas_MangaConnectors_MangaConnectorName",
+ table: "Mangas",
+ column: "MangaConnectorName",
+ principalTable: "MangaConnectors",
+ principalColumn: "Name",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_MangaTagToManga_Mangas_MangaIds",
+ table: "MangaTagToManga",
+ column: "MangaIds",
+ principalTable: "Mangas",
+ principalColumn: "MangaId",
+ onDelete: ReferentialAction.Cascade);
+ }
+ }
+}
diff --git a/API/Migrations/pgsql/PgsqlContextModelSnapshot.cs b/API/Migrations/pgsql/PgsqlContextModelSnapshot.cs
index c6044a7..1272fa2 100644
--- a/API/Migrations/pgsql/PgsqlContextModelSnapshot.cs
+++ b/API/Migrations/pgsql/PgsqlContextModelSnapshot.cs
@@ -24,25 +24,23 @@ namespace API.Migrations.pgsql
modelBuilder.Entity("API.Schema.Author", b =>
{
- b.Property("AuthorId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
+ b.Property("Key")
+ .HasColumnType("text");
b.Property("AuthorName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
- b.HasKey("AuthorId");
+ b.HasKey("Key");
b.ToTable("Authors");
});
modelBuilder.Entity("API.Schema.Chapter", b =>
{
- b.Property("ChapterId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
+ b.Property("Key")
+ .HasColumnType("text");
b.Property("ChapterNumber")
.IsRequired()
@@ -57,38 +55,49 @@ namespace API.Migrations.pgsql
.HasMaxLength(256)
.HasColumnType("character varying(256)");
- b.Property("IdOnConnectorSite")
- .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.HasKey("Key");
b.HasIndex("ParentMangaId");
b.ToTable("Chapters");
});
+ modelBuilder.Entity("API.Schema.FileLibrary", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("BasePath")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("LibraryName")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.HasKey("Key");
+
+ b.ToTable("LocalLibraries");
+ });
+
modelBuilder.Entity("API.Schema.Jobs.Job", b =>
{
- b.Property("JobId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
+ b.Property("Key")
+ .HasColumnType("text");
b.Property("Enabled")
.HasColumnType("boolean");
@@ -109,7 +118,7 @@ namespace API.Migrations.pgsql
b.Property("state")
.HasColumnType("smallint");
- b.HasKey("JobId");
+ b.HasKey("Key");
b.HasIndex("ParentJobId");
@@ -120,32 +129,10 @@ namespace API.Migrations.pgsql
b.UseTphMappingStrategy();
});
- 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("Key")
+ .HasColumnType("text");
b.Property("CoverFileNameInCache")
.HasMaxLength(512)
@@ -165,11 +152,6 @@ namespace API.Migrations.pgsql
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
- b.Property("IdOnConnectorSite")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
b.Property("IgnoreChaptersBefore")
.HasColumnType("real");
@@ -177,11 +159,6 @@ namespace API.Migrations.pgsql
.HasMaxLength(64)
.HasColumnType("character varying(64)");
- b.Property("MangaConnectorName")
- .IsRequired()
- .HasMaxLength(32)
- .HasColumnType("character varying(32)");
-
b.Property("Name")
.IsRequired()
.HasMaxLength(512)
@@ -194,21 +171,80 @@ namespace API.Migrations.pgsql
b.Property("ReleaseStatus")
.HasColumnType("smallint");
- b.Property("WebsiteUrl")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("character varying(512)");
-
b.Property("Year")
.HasColumnType("bigint");
- b.HasKey("MangaId");
+ b.HasKey("Key");
b.HasIndex("LibraryId");
+ b.ToTable("Mangas");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("IdOnConnectorSite")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("MangaConnectorName")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ObjId")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("WebsiteUrl")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.HasKey("Key");
+
b.HasIndex("MangaConnectorName");
- b.ToTable("Mangas");
+ b.HasIndex("ObjId");
+
+ b.ToTable("MangaConnectorToChapter");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("IdOnConnectorSite")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("MangaConnectorName")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ObjId")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("WebsiteUrl")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.HasKey("Key");
+
+ b.HasIndex("MangaConnectorName");
+
+ b.HasIndex("ObjId");
+
+ b.ToTable("MangaConnectorToManga");
});
modelBuilder.Entity("API.Schema.MangaConnectors.MangaConnector", b =>
@@ -258,10 +294,10 @@ namespace API.Migrations.pgsql
modelBuilder.Entity("AuthorToManga", b =>
{
b.Property("AuthorIds")
- .HasColumnType("character varying(64)");
+ .HasColumnType("text");
b.Property("MangaIds")
- .HasColumnType("character varying(64)");
+ .HasColumnType("text");
b.HasKey("AuthorIds", "MangaIds");
@@ -272,15 +308,15 @@ namespace API.Migrations.pgsql
modelBuilder.Entity("JobJob", b =>
{
- b.Property("DependsOnJobsJobId")
- .HasColumnType("character varying(64)");
+ b.Property("DependsOnJobsKey")
+ .HasColumnType("text");
- b.Property("JobId")
- .HasColumnType("character varying(64)");
+ b.Property("JobKey")
+ .HasColumnType("text");
- b.HasKey("DependsOnJobsJobId", "JobId");
+ b.HasKey("DependsOnJobsKey", "JobKey");
- b.HasIndex("JobId");
+ b.HasIndex("JobKey");
b.ToTable("JobJob");
});
@@ -291,7 +327,7 @@ namespace API.Migrations.pgsql
.HasColumnType("character varying(64)");
b.Property("MangaIds")
- .HasColumnType("character varying(64)");
+ .HasColumnType("text");
b.HasKey("MangaTagIds", "MangaIds");
@@ -501,22 +537,44 @@ namespace API.Migrations.pgsql
modelBuilder.Entity("API.Schema.Manga", b =>
{
- b.HasOne("API.Schema.LocalLibrary", "Library")
+ b.HasOne("API.Schema.FileLibrary", "Library")
.WithMany()
.HasForeignKey("LibraryId")
.OnDelete(DeleteBehavior.SetNull);
- b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
- .WithMany()
- .HasForeignKey("MangaConnectorName")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
+ b.OwnsMany("API.Schema.AltTitle", "AltTitles", b1 =>
+ {
+ b1.Property("Key")
+ .HasColumnType("text");
+
+ b1.Property("Language")
+ .IsRequired()
+ .HasMaxLength(8)
+ .HasColumnType("character varying(8)");
+
+ b1.Property("MangaKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b1.Property("Title")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b1.HasKey("Key");
+
+ b1.HasIndex("MangaKey");
+
+ b1.ToTable("AltTitle");
+
+ b1.WithOwner()
+ .HasForeignKey("MangaKey");
+ });
b.OwnsMany("API.Schema.Link", "Links", b1 =>
{
- b1.Property("LinkId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
+ b1.Property("Key")
+ .HasColumnType("text");
b1.Property("LinkProvider")
.IsRequired()
@@ -528,48 +586,18 @@ namespace API.Migrations.pgsql
.HasMaxLength(2048)
.HasColumnType("character varying(2048)");
- b1.Property("MangaId")
+ b1.Property("MangaKey")
.IsRequired()
- .HasColumnType("character varying(64)");
+ .HasColumnType("text");
- b1.HasKey("LinkId");
+ b1.HasKey("Key");
- b1.HasIndex("MangaId");
+ b1.HasIndex("MangaKey");
b1.ToTable("Link");
b1.WithOwner()
- .HasForeignKey("MangaId");
- });
-
- b.OwnsMany("API.Schema.MangaAltTitle", "AltTitles", b1 =>
- {
- b1.Property("AltTitleId")
- .HasMaxLength(64)
- .HasColumnType("character varying(64)");
-
- b1.Property("Language")
- .IsRequired()
- .HasMaxLength(8)
- .HasColumnType("character varying(8)");
-
- b1.Property("MangaId")
- .IsRequired()
- .HasColumnType("character varying(64)");
-
- b1.Property("Title")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("character varying(256)");
-
- b1.HasKey("AltTitleId");
-
- b1.HasIndex("MangaId");
-
- b1.ToTable("MangaAltTitle");
-
- b1.WithOwner()
- .HasForeignKey("MangaId");
+ .HasForeignKey("MangaKey");
});
b.Navigation("AltTitles");
@@ -577,8 +605,44 @@ namespace API.Migrations.pgsql
b.Navigation("Library");
b.Navigation("Links");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
+ .WithMany()
+ .HasForeignKey("MangaConnectorName")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.Chapter", "Obj")
+ .WithMany("MangaConnectorIds")
+ .HasForeignKey("ObjId")
+ .OnDelete(DeleteBehavior.NoAction)
+ .IsRequired();
b.Navigation("MangaConnector");
+
+ b.Navigation("Obj");
+ });
+
+ modelBuilder.Entity("API.Schema.MangaConnectorId", b =>
+ {
+ b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
+ .WithMany()
+ .HasForeignKey("MangaConnectorName")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Schema.Manga", "Obj")
+ .WithMany("MangaConnectorIds")
+ .HasForeignKey("ObjId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("MangaConnector");
+
+ b.Navigation("Obj");
});
modelBuilder.Entity("AuthorToManga", b =>
@@ -600,13 +664,13 @@ namespace API.Migrations.pgsql
{
b.HasOne("API.Schema.Jobs.Job", null)
.WithMany()
- .HasForeignKey("DependsOnJobsJobId")
+ .HasForeignKey("DependsOnJobsKey")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("API.Schema.Jobs.Job", null)
.WithMany()
- .HasForeignKey("JobId")
+ .HasForeignKey("JobKey")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
@@ -667,7 +731,7 @@ namespace API.Migrations.pgsql
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
- b.HasOne("API.Schema.LocalLibrary", "ToLibrary")
+ b.HasOne("API.Schema.FileLibrary", "ToFileLibrary")
.WithMany()
.HasForeignKey("ToLibraryId")
.OnDelete(DeleteBehavior.Cascade)
@@ -675,7 +739,7 @@ namespace API.Migrations.pgsql
b.Navigation("Manga");
- b.Navigation("ToLibrary");
+ b.Navigation("ToFileLibrary");
});
modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
@@ -711,9 +775,16 @@ namespace API.Migrations.pgsql
b.Navigation("Manga");
});
+ modelBuilder.Entity("API.Schema.Chapter", b =>
+ {
+ b.Navigation("MangaConnectorIds");
+ });
+
modelBuilder.Entity("API.Schema.Manga", b =>
{
b.Navigation("Chapters");
+
+ b.Navigation("MangaConnectorIds");
});
#pragma warning restore 612, 618
}
diff --git a/API/Program.cs b/API/Program.cs
index c1d3591..75b5038 100644
--- a/API/Program.cs
+++ b/API/Program.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using API;
+using API.Controllers;
using API.Schema;
using API.Schema.Contexts;
using API.Schema.Jobs;
@@ -108,7 +109,26 @@ app.UseMiddleware();
using (IServiceScope scope = app.Services.CreateScope())
{
PgsqlContext context = scope.ServiceProvider.GetRequiredService();
- context.Database.Migrate();
+ if (context.Database.GetMigrations().Contains("20250630182650_OofV2.1") == false)
+ {
+ IQueryable<(string, string)> mangas = context.Database.SqlQuery<(string, string)>($"SELECT MangaConnectorName, IdOnConnectorSite as ID FROM Mangas");
+ context.Database.Migrate();
+ foreach ((string mangaConnectorName, string idOnConnectorSite) manga in mangas)
+ {
+ if(context.MangaConnectors.Find(manga.mangaConnectorName) is not { } mangaConnector)
+ continue;
+ if(mangaConnector.GetMangaFromId(manga.idOnConnectorSite) is not { } result)
+ continue;
+ if (SearchController.AddMangaToContext(result.Item1, result.Item2, context) is { } added)
+ {
+ RetrieveChaptersJob retrieveChaptersJob = new (added, "en", 0);
+ UpdateChaptersDownloadedJob update = new(added, 0, null, [retrieveChaptersJob]);
+ context.Jobs.AddRange([retrieveChaptersJob, update]);
+ }
+ }
+ } else
+ context.Database.Migrate();
+
MangaConnector[] connectors =
[
@@ -119,7 +139,7 @@ using (IServiceScope scope = app.Services.CreateScope())
MangaConnector[] newConnectors = connectors.Where(c => !context.MangaConnectors.Contains(c)).ToArray();
context.MangaConnectors.AddRange(newConnectors);
if (!context.LocalLibraries.Any())
- context.LocalLibraries.Add(new LocalLibrary(TrangaSettings.downloadLocation, "Default Library"));
+ context.LocalLibraries.Add(new FileLibrary(TrangaSettings.downloadLocation, "Default FileLibrary"));
context.Jobs.AddRange(context.Jobs.Where(j => j.JobType == JobType.DownloadAvailableChaptersJob)
.Include(downloadAvailableChaptersJob => ((DownloadAvailableChaptersJob)downloadAvailableChaptersJob).Manga)
diff --git a/API/Schema/AltTitle.cs b/API/Schema/AltTitle.cs
new file mode 100644
index 0000000..2cc0b0d
--- /dev/null
+++ b/API/Schema/AltTitle.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel.DataAnnotations;
+using Microsoft.EntityFrameworkCore;
+
+namespace API.Schema;
+
+[PrimaryKey("Key")]
+public class AltTitle(string language, string title) : Identifiable(TokenGen.CreateToken("AltTitle"))
+{
+ [StringLength(8)]
+ [Required]
+ public string Language { get; init; } = language;
+ [StringLength(256)]
+ [Required]
+ public string Title { get; init; } = title;
+
+ public override string ToString() => $"{base.ToString()} {Language} {Title}";
+}
\ No newline at end of file
diff --git a/API/Schema/Author.cs b/API/Schema/Author.cs
index b3149af..4517b43 100644
--- a/API/Schema/Author.cs
+++ b/API/Schema/Author.cs
@@ -3,18 +3,12 @@ using Microsoft.EntityFrameworkCore;
namespace API.Schema;
-[PrimaryKey("AuthorId")]
-public class Author(string authorName)
+[PrimaryKey("Key")]
+public class Author(string authorName) : Identifiable(TokenGen.CreateToken(typeof(Author), authorName))
{
- [StringLength(64)]
- [Required]
- public string AuthorId { get; init; } = TokenGen.CreateToken(typeof(Author), authorName);
[StringLength(128)]
[Required]
public string AuthorName { get; init; } = authorName;
- public override string ToString()
- {
- return $"{AuthorId} {AuthorName}";
- }
+ public override string ToString() => $"{base.ToString()} {AuthorName}";
}
\ No newline at end of file
diff --git a/API/Schema/Chapter.cs b/API/Schema/Chapter.cs
index 71ce9fe..47cfd66 100644
--- a/API/Schema/Chapter.cs
+++ b/API/Schema/Chapter.cs
@@ -9,15 +9,11 @@ using Newtonsoft.Json;
namespace API.Schema;
-[PrimaryKey("ChapterId")]
-public class Chapter : IComparable
+[PrimaryKey("Key")]
+public class Chapter : Identifiable, IComparable
{
- [StringLength(64)] [Required] public string ChapterId { get; init; }
-
- [StringLength(256)]public string? IdOnConnectorSite { get; init; }
-
[StringLength(64)] [Required] public string ParentMangaId { get; init; } = null!;
- private Manga? _parentManga = null!;
+ private Manga? _parentManga;
[JsonIgnore]
public Manga ParentManga
@@ -25,41 +21,43 @@ public class Chapter : IComparable
get => _lazyLoader.Load(this, ref _parentManga) ?? throw new InvalidOperationException();
init
{
- ParentMangaId = value.MangaId;
+ ParentMangaId = value.Key;
_parentManga = value;
}
}
- private MangaConnectorMangaEntry? _mangaConnectorMangaEntry = null!;
+ [NotMapped]
+ public Dictionary IdsOnMangaConnectors =>
+ MangaConnectorIds.ToDictionary(id => id.MangaConnectorName, id => id.IdOnConnectorSite);
+
+ private ICollection>? _mangaConnectorIds;
[JsonIgnore]
- public MangaConnectorMangaEntry MangaConnectorMangaEntry
+ public ICollection> MangaConnectorIds
{
- get => _lazyLoader.Load(this, ref _mangaConnectorMangaEntry) ?? throw new InvalidOperationException();
- init => _mangaConnectorMangaEntry = value;
+ get => _lazyLoader.Load(this, ref _mangaConnectorIds) ?? throw new InvalidOperationException();
+ init => _mangaConnectorIds = value;
}
public int? VolumeNumber { get; private set; }
[StringLength(10)] [Required] public string ChapterNumber { get; private set; }
- [StringLength(2048)] [Required] [Url] public string Url { get; internal set; }
-
[StringLength(256)] public string? Title { get; private set; }
[StringLength(256)] [Required] public string FileName { get; private set; }
[Required] public bool Downloaded { get; internal set; }
- [NotMapped] public string FullArchiveFilePath => Path.Join(MangaConnectorMangaEntry.Manga.FullDirectoryPath, FileName);
+ [NotMapped] public string FullArchiveFilePath => Path.Join(ParentManga.FullDirectoryPath, FileName);
private readonly ILazyLoader _lazyLoader = null!;
- public Chapter(MangaConnectorMangaEntry mangaConnectorMangaEntry, string url, string chapterNumber, int? volumeNumber = null, string? idOnConnectorSite = null, string? title = null)
+ public Chapter(Manga parentManga, string chapterNumber,
+ int? volumeNumber, string? title = null)
+ : base(TokenGen.CreateToken(typeof(Chapter), parentManga.Key, chapterNumber))
{
- this.ChapterId = TokenGen.CreateToken(typeof(Chapter), mangaConnectorMangaEntry.MangaId, chapterNumber);
- this.MangaConnectorMangaEntry = mangaConnectorMangaEntry;
- this.IdOnConnectorSite = idOnConnectorSite;
+ this.ParentManga = parentManga;
+ this.MangaConnectorIds = [];
this.VolumeNumber = volumeNumber;
this.ChapterNumber = chapterNumber;
- this.Url = url;
this.Title = title;
this.FileName = GetArchiveFilePath();
this.Downloaded = false;
@@ -68,14 +66,12 @@ public class Chapter : IComparable
///
/// EF ONLY!!!
///
- internal Chapter(ILazyLoader lazyLoader, string chapterId, int? volumeNumber, string chapterNumber, string url, string? idOnConnectorSite, string? title, string fileName, bool downloaded)
+ internal Chapter(ILazyLoader lazyLoader, string key, int? volumeNumber, string chapterNumber, string? title, string fileName, bool downloaded)
+ : base(key)
{
this._lazyLoader = lazyLoader;
- this.ChapterId = chapterId;
- this.IdOnConnectorSite = idOnConnectorSite;
this.VolumeNumber = volumeNumber;
this.ChapterNumber = chapterNumber;
- this.Url = url;
this.Title = title;
this.FileName = fileName;
this.Downloaded = downloaded;
@@ -100,14 +96,14 @@ public class Chapter : IComparable
public bool CheckDownloaded() => File.Exists(FullArchiveFilePath);
/// Placeholders:
- /// %M Manga Name
+ /// %M Obj Name
/// %V Volume
/// %C Chapter
/// %T Title
/// %A Author (first in list)
/// %I Chapter Internal ID
- /// %i Manga Internal ID
- /// %Y Year (Manga)
+ /// %i Obj Internal ID
+ /// %Y Year (Obj)
private static readonly Regex NullableRex = new(@"\?([a-zA-Z])\(([^\)]*)\)|(.+?)");
private static readonly Regex ReplaceRexx = new(@"%([a-zA-Z])|(.+?)");
private string GetArchiveFilePath()
@@ -125,14 +121,12 @@ public class Chapter : IComparable
char placeholder = nullable.Groups[1].Value[0];
bool isNull = placeholder switch
{
- 'M' => MangaConnectorMangaEntry.Manga?.Name is null,
+ 'M' => ParentManga?.Name is null,
'V' => VolumeNumber is null,
'C' => ChapterNumber is null,
'T' => Title is null,
- 'A' => MangaConnectorMangaEntry.Manga?.Authors?.FirstOrDefault()?.AuthorName is null,
- 'I' => ChapterId is null,
- 'i' => MangaConnectorMangaEntry.Manga?.MangaId is null,
- 'Y' => MangaConnectorMangaEntry.Manga?.Year is null,
+ 'A' => ParentManga?.Authors?.FirstOrDefault()?.AuthorName is null,
+ 'Y' => ParentManga?.Year is null,
_ => true
};
if(!isNull)
@@ -153,14 +147,12 @@ public class Chapter : IComparable
char placeholder = replace.Groups[1].Value[0];
string? value = placeholder switch
{
- 'M' => MangaConnectorMangaEntry.Manga?.Name,
+ 'M' => ParentManga?.Name,
'V' => VolumeNumber?.ToString(),
'C' => ChapterNumber,
'T' => Title,
- 'A' => MangaConnectorMangaEntry.Manga?.Authors?.FirstOrDefault()?.AuthorName,
- 'I' => ChapterId,
- 'i' => MangaConnectorMangaEntry.Manga?.MangaId,
- 'Y' => MangaConnectorMangaEntry.Manga?.Year.ToString(),
+ 'A' => ParentManga?.Authors?.FirstOrDefault()?.AuthorName,
+ 'Y' => ParentManga?.Year.ToString(),
_ => null
};
stringBuilder.Append(value);
@@ -201,21 +193,18 @@ public class Chapter : IComparable
);
if(Title is not null)
comicInfo.Add(new XElement("Title", Title));
- if(MangaConnectorMangaEntry.Manga.MangaTags.Count > 0)
- comicInfo.Add(new XElement("Tags", string.Join(',', MangaConnectorMangaEntry.Manga.MangaTags.Select(tag => tag.Tag))));
+ if(ParentManga.MangaTags.Count > 0)
+ comicInfo.Add(new XElement("Tags", string.Join(',', ParentManga.MangaTags.Select(tag => tag.Tag))));
if(VolumeNumber is not null)
comicInfo.Add(new XElement("Volume", VolumeNumber));
- if(MangaConnectorMangaEntry.Manga.Authors.Count > 0)
- comicInfo.Add(new XElement("Writer", string.Join(',', MangaConnectorMangaEntry.Manga.Authors.Select(author => author.AuthorName))));
- if(MangaConnectorMangaEntry.Manga.OriginalLanguage is not null)
- comicInfo.Add(new XElement("LanguageISO", MangaConnectorMangaEntry.Manga.OriginalLanguage));
- if(MangaConnectorMangaEntry.Manga.Description != string.Empty)
- comicInfo.Add(new XElement("Summary", MangaConnectorMangaEntry.Manga.Description));
+ if(ParentManga.Authors.Count > 0)
+ comicInfo.Add(new XElement("Writer", string.Join(',', ParentManga.Authors.Select(author => author.AuthorName))));
+ if(ParentManga.OriginalLanguage is not null)
+ comicInfo.Add(new XElement("LanguageISO", ParentManga.OriginalLanguage));
+ if(ParentManga.Description != string.Empty)
+ comicInfo.Add(new XElement("Summary", ParentManga.Description));
return comicInfo.ToString();
}
- public override string ToString()
- {
- return $"{ChapterId} Vol.{VolumeNumber} Ch.{ChapterNumber} - {Title}";
- }
+ public override string ToString() => $"{base.ToString()} Vol.{VolumeNumber} Ch.{ChapterNumber} - {Title}";
}
\ No newline at end of file
diff --git a/API/Schema/Contexts/PgsqlContext.cs b/API/Schema/Contexts/PgsqlContext.cs
index 6b73821..3c2c927 100644
--- a/API/Schema/Contexts/PgsqlContext.cs
+++ b/API/Schema/Contexts/PgsqlContext.cs
@@ -11,10 +11,12 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op
public DbSet