MangaConnectors do not have to return an Object with 6 Parameters.

Job-Start Logic readable and optimized
More robust Database design
This commit is contained in:
2025-05-09 06:28:44 +02:00
parent 7477f4d04d
commit 7d4a6be569
56 changed files with 2924 additions and 5855 deletions

View File

@ -2,15 +2,17 @@
using API.Schema;
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
{
/// <summary>
/// Returns all Jobs
@ -102,7 +104,7 @@ public class JobController(PgsqlContext context) : Controller
/// <param name="MangaId">ID of Manga</param>
/// <param name="record">Job-Configuration</param>
/// <response code="201">Job-IDs</response>
/// <response code="400">Could not find Library with ID</response>
/// <response code="400">Could not find ToLibrary with ID</response>
/// <response code="404">Could not find Manga with ID</response>
/// <response code="500">Error during Database Operation</response>
[HttpPut("DownloadAvailableChaptersJob/{MangaId}")]
@ -110,7 +112,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType<string>(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 +128,13 @@ 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 downloadChapters = new DownloadAvailableChaptersJob(m, record.recurrenceTimeMs, dependsOnJobs: [retrieveChapters]);
return AddJobs([retrieveChapters, downloadChapters]);
}
/// <summary>
@ -148,9 +150,9 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(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]);
}
@ -167,9 +169,9 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(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 UpdateFilesDownloadedJob(m, 0);
return AddJobs([job]);
}
@ -183,8 +185,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateAllFilesDownloadedJob()
{
List<string> ids = context.Mangas.Select(m => m.MangaId).ToList();
List<UpdateFilesDownloadedJob> jobs = ids.Select(id => new UpdateFilesDownloadedJob(0, id)).ToList();
List<UpdateFilesDownloadedJob> jobs = context.Mangas.Select(m => new UpdateFilesDownloadedJob(m, 0, null, null)).ToList();
try
{
context.Jobs.AddRange(jobs);
@ -193,12 +194,13 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
/// <summary>
/// Create a new UpdateMetadataJob
/// Not Implemented: Create a new UpdateMetadataJob
/// </summary>
/// <param name="MangaId">ID of the Manga</param>
/// <response code="201">Job-IDs</response>
@ -210,14 +212,11 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(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);
}
/// <summary>
/// Create a new UpdateMetadataJob for all Manga
/// Not Implemented: Create a new UpdateMetadataJob for all Manga
/// </summary>
/// <response code="201">Job-IDs</response>
/// <response code="500">Error during Database Operation</response>
@ -226,18 +225,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateAllMetadataJob()
{
List<string> ids = context.Mangas.Select(m => m.MangaId).ToList();
List<UpdateMetadataJob> 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 +238,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -269,8 +258,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 +267,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -322,6 +311,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -354,6 +344,7 @@ public class JobController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -367,6 +358,6 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType(Status501NotImplemented)]
public IActionResult StopJob(string JobId)
{
return StatusCode(501);
return StatusCode(Status501NotImplemented);
}
}

View File

@ -1,6 +1,7 @@
using API.Schema;
using API.Schema.LibraryConnectors;
using Asp.Versioning;
using log4net;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
@ -9,10 +10,10 @@ namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
public class LibraryConnectorController(PgsqlContext context) : Controller
public class LibraryConnectorController(PgsqlContext context, ILog Log) : Controller
{
/// <summary>
/// Gets all configured Library-Connectors
/// Gets all configured ToLibrary-Connectors
/// </summary>
/// <response code="200"></response>
[HttpGet]
@ -24,9 +25,9 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
/// <summary>
/// Returns Library-Connector with requested ID
/// Returns ToLibrary-Connector with requested ID
/// </summary>
/// <param name="LibraryControllerId">Library-Connector-ID</param>
/// <param name="LibraryControllerId">ToLibrary-Connector-ID</param>
/// <response code="200"></response>
/// <response code="404">Connector with ID not found.</response>
[HttpGet("{LibraryControllerId}")]
@ -43,9 +44,9 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
/// <summary>
/// Creates a new Library-Connector
/// Creates a new ToLibrary-Connector
/// </summary>
/// <param name="libraryConnector">Library-Connector</param>
/// <param name="libraryConnector">ToLibrary-Connector</param>
/// <response code="201"></response>
/// <response code="500">Error during Database Operation</response>
[HttpPut]
@ -61,14 +62,15 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
/// <summary>
/// Deletes the Library-Connector with the requested ID
/// Deletes the ToLibrary-Connector with the requested ID
/// </summary>
/// <param name="LibraryControllerId">Library-Connector-ID</param>
/// <param name="LibraryControllerId">ToLibrary-Connector-ID</param>
/// <response code="200"></response>
/// <response code="404">Connector with ID not found.</response>
/// <response code="500">Error during Database Operation</response>
@ -90,6 +92,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}

View File

@ -1,6 +1,7 @@
using API.APIEndpointRecords;
using API.Schema;
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 LocalLibrariesController(PgsqlContext context) : Controller
public class LocalLibrariesController(PgsqlContext context, ILog Log) : Controller
{
[HttpGet]
[ProducesResponseType<LocalLibrary[]>(Status200OK, "application/json")]
@ -52,6 +53,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -79,6 +81,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -106,6 +109,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -128,6 +132,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -151,6 +156,7 @@ public class LocalLibrariesController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}

View File

@ -1,6 +1,7 @@
using API.Schema;
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
{
/// <summary>
/// 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);
}
}

View File

@ -1,19 +1,21 @@
using API.Schema;
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
{
/// <summary>
/// Returns all cached Manga
@ -82,6 +84,7 @@ public class MangaController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
@ -287,11 +290,12 @@ public class MangaController(PgsqlContext context) : Controller
return Ok(max);
}
/// <summary>
/// Configure the cut-off for Manga
/// </summary>
/// <param name="MangaId">Manga-ID</param>
/// <param name="chapterThreshold">Threshold (Chapter Number)</param>
/// <response code="200"></response>
/// <response code="404">Manga with ID not found.</response>
/// <response code="500">Error during Database Operation</response>
@ -307,21 +311,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);
}
}
/// <summary>
/// Move Manga to different Library
/// Move Manga to different ToLibrary
/// </summary>
/// <param name="MangaId">Manga-ID</param>
/// <param name="LibraryId">Library-Id</param>
/// <param name="LibraryId">ToLibrary-Id</param>
/// <response code="202">Folder is going to be moved</response>
/// <response code="404">MangaId or LibraryId not found</response>
/// <response code="500">Error during Database Operation</response>
@ -331,24 +336,23 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<string>(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);
UpdateFilesDownloadedJob 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);
}
}

View File

@ -3,6 +3,7 @@ using API.APIEndpointRecords;
using API.Schema;
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(PgsqlContext context, ILog Log) : Controller
{
/// <summary>
/// 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);
}
}
@ -209,6 +211,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}

View File

@ -1,14 +1,16 @@
using API.Schema;
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
{
/// <summary>
/// Returns the Author-Information for Author-ID
@ -32,13 +34,16 @@ public class QueryController(PgsqlContext context) : Controller
/// </summary>
/// <param name="AuthorId">Author-ID</param>
/// <response code="200"></response>
/// <response code="404">Author not found</response>
[HttpGet("Mangas/WithAuthorId/{AuthorId}")]
[ProducesResponseType<Manga[]>(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)));
}
/*
/// <summary>
/// Returns Link-Information for Link-Id
/// </summary>
@ -71,18 +76,21 @@ public class QueryController(PgsqlContext context) : Controller
if (ret is null)
return NotFound();
return Ok(ret);
}
}*/
/// <summary>
/// Returns all Manga with Tag
/// </summary>
/// <param name="Tag"></param>
/// <response code="200"></response>
/// <response code="404">Tag not found</response>
[HttpGet("Mangas/WithTag/{Tag}")]
[ProducesResponseType<Manga[]>(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)));
}
/// <summary>

View File

@ -2,85 +2,53 @@ using API.Schema;
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
{
/// <summary>
/// Initiate a search for a Manga on all Connectors
/// </summary>
/// <param name="name">Name/Title of the Manga</param>
/// <response code="200"></response>
/// <response code="500">Error during Database Operation</response>
[HttpPost("Name")]
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult SearchMangaGlobal([FromBody]string name)
{
List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> allManga = new();
foreach (MangaConnector contextMangaConnector in context.MangaConnectors.Where(connector => connector.Enabled))
allManga.AddRange(contextMangaConnector.GetManga(name));
List<Manga> retMangas = new();
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? 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());
}
/// <summary>
/// Initiate a search for a Manga on a specific Connector
/// </summary>
/// <param name="MangaConnectorName">Manga-Connector-ID</param>
/// <param name="name">Name/Title of the Manga</param>
/// <param name="MangaConnectorName"></param>
/// <param name="Query"></param>
/// <response code="200"></response>
/// <response code="404">MangaConnector with ID not found</response>
/// <response code="406">MangaConnector with ID is disabled</response>
/// <response code="500">Error during Database Operation</response>
[HttpPost("{MangaConnectorName}")]
[HttpPost("{MangaConnectorName}/{Query}")]
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status406NotAcceptable)]
[ProducesResponseType<string>(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<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] mangas = connector.GetManga(name);
Manga[] mangas = connector.SearchManga(Query);
List<Manga> retMangas = new();
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? 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);
}
}
@ -104,98 +72,77 @@ public class SearchController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult GetMangaFromUrl([FromBody]string url)
{
List<MangaConnector> connectors = context.MangaConnectors.AsEnumerable().Where(c => c.ValidateUrl(url)).ToList();
List<MangaConnector> connectors = context.MangaConnectors.AsEnumerable().Where(c => c.UrlMatchesConnector(url)).ToList();
if (connectors.Count == 0)
return NotFound();
else if (connectors.Count > 1)
return StatusCode(Status300MultipleChoices);
(Manga manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles)? x = connectors.First().GetMangaFromUrl(url);
if (x is null)
if(connectors.First().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);
}
catch (DbUpdateException e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
}
private Manga? AddMangaToContext(Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links,
List<MangaAltTitle>? altTitles)
private Manga? AddMangaToContext(Manga manga)
{
if (manga is null)
return null;
Manga? existing = context.Mangas.Find(manga.MangaId);
if (tags is not null)
IEnumerable<MangaTag> mergedTags = manga.MangaTags.Select(mt =>
{
IEnumerable<MangaTag> mergedTags = tags.Select(mt =>
{
MangaTag? inDb = context.Tags.Find(mt.Tag);
return inDb ?? mt;
});
manga.MangaTags = mergedTags.ToList();
IEnumerable<MangaTag> 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<Author> mergedAuthors = manga.Authors.Select(ma =>
{
IEnumerable<Author> mergedAuthors = authors.Select(ma =>
{
Author? inDb = context.Authors.Find(ma.AuthorId);
return inDb ?? ma;
});
manga.Authors = mergedAuthors.ToList();
IEnumerable<Author> 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)
/*
IEnumerable<Link> mergedLinks = manga.Links.Select(ml =>
{
IEnumerable<Link> mergedLinks = links.Select(ml =>
{
Link? inDb = context.Links.Find(ml.LinkId);
return inDb ?? ml;
});
manga.Links = mergedLinks.ToList();
IEnumerable<Link> newLinks = manga.Links
.Where(ml => !context.Links.Select(l => l.LinkId).Contains(ml.LinkId));
context.Links.AddRange(newLinks);
}
Link? inDb = context.Links.Find(ml.LinkId);
return inDb ?? ml;
});
manga.Links = mergedLinks.ToList();
if (altTitles is not null)
IEnumerable<MangaAltTitle> mergedAltTitles = manga.AltTitles.Select(mat =>
{
IEnumerable<MangaAltTitle> mergedAltTitles = altTitles.Select(mat =>
MangaAltTitle? inDb = context.AltTitles.Find(mat.AltTitleId);
return inDb ?? mat;
});
manga.AltTitles = mergedAltTitles.ToList();
*/
try
{
if (context.Mangas.Find(manga.MangaId) is { } r)
{
MangaAltTitle? inDb = context.AltTitles.Find(mat.AltTitleId);
return inDb ?? mat;
});
manga.AltTitles = mergedAltTitles.ToList();
IEnumerable<MangaAltTitle> 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;
}
}

View File

@ -2,6 +2,7 @@
using API.Schema;
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 +12,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
{
/// <summary>
/// Get all Settings
@ -252,14 +253,16 @@ public class SettingsController(PgsqlContext context) : Controller
{
try
{
Dictionary<Chapter, string> 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);
}
}