#168 Multiple Base-Paths (Libraries) Support
Some checks failed
Docker Image CI / build (push) Has been cancelled

This commit is contained in:
2025-03-16 16:48:19 +01:00
parent 5012bbb2eb
commit 5b03befbf1
64 changed files with 668 additions and 16084 deletions

View File

@ -100,20 +100,37 @@ public class JobController(PgsqlContext context) : Controller
/// Create a new DownloadAvailableChaptersJob
/// </summary>
/// <param name="MangaId">ID of Manga</param>
/// <param name="recurrenceTime">How often should we check for new chapters</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="404">Could not find Manga with ID</response>
/// <response code="500">Error during Database Operation</response>
[HttpPut("DownloadAvailableChaptersJob/{MangaId}")]
[ProducesResponseType<string[]>(Status201Created, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateNewDownloadChapterJob(string MangaId, [FromBody]ulong recurrenceTime)
public IActionResult CreateDownloadAvailableChaptersJob(string MangaId, [FromBody]DownloadAvailableJobsRecord record)
{
if (context.Manga.Find(MangaId) is null)
if (context.Mangas.Find(MangaId) is not { } m)
return NotFound();
Job dep = new RetrieveChaptersJob(recurrenceTime, MangaId);
Job job = new DownloadAvailableChaptersJob(recurrenceTime, MangaId, null, [dep.JobId]);
else
{
try
{
LocalLibrary? l = context.LocalLibraries.Find(record.localLibraryId);
if (l is null)
return BadRequest();
m.Library = l;
context.SaveChanges();
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
Job dep = new RetrieveChaptersJob(record.recurrenceTimeMs, MangaId);
Job job = new DownloadAvailableChaptersJob(record.recurrenceTimeMs, MangaId, null, [dep.JobId]);
return AddJobs([dep, job]);
}
@ -149,7 +166,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateFilesDownloadedJob(string MangaId)
{
if(context.Manga.Find(MangaId) is null)
if(context.Mangas.Find(MangaId) is null)
return NotFound();
Job job = new UpdateFilesDownloadedJob(0, MangaId);
return AddJobs([job]);
@ -165,7 +182,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateAllFilesDownloadedJob()
{
List<string> ids = context.Manga.Select(m => m.MangaId).ToList();
List<string> ids = context.Mangas.Select(m => m.MangaId).ToList();
List<UpdateFilesDownloadedJob> jobs = ids.Select(id => new UpdateFilesDownloadedJob(0, id)).ToList();
try
{
@ -192,7 +209,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateMetadataJob(string MangaId)
{
if(context.Manga.Find(MangaId) is null)
if(context.Mangas.Find(MangaId) is null)
return NotFound();
Job job = new UpdateMetadataJob(0, MangaId);
return AddJobs([job]);
@ -208,7 +225,7 @@ public class JobController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateUpdateAllMetadataJob()
{
List<string> ids = context.Manga.Select(m => m.MangaId).ToList();
List<string> ids = context.Mangas.Select(m => m.MangaId).ToList();
List<UpdateMetadataJob> jobs = ids.Select(id => new UpdateMetadataJob(0, id)).ToList();
try
{

View File

@ -0,0 +1,127 @@
using API.APIEndpointRecords;
using API.Schema;
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
public class LocalLibrariesController(PgsqlContext context) : Controller
{
[HttpGet]
[ProducesResponseType<LocalLibrary[]>(Status200OK, "application/json")]
public IActionResult GetLocalLibraries()
{
return Ok(context.LocalLibraries);
}
[HttpGet("{LibraryId}")]
[ProducesResponseType<LocalLibrary>(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
public IActionResult GetLocalLibrary(string LibraryId)
{
LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
return Ok(library);
}
[HttpPatch("{LibraryId}/ChangeBasePath")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult ChangeLibraryBasePath(string LibraryId, [FromBody] string newBasePath)
{
try
{
LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
if (false) //TODO implement path check
return BadRequest();
library.BasePath = newBasePath;
context.SaveChanges();
return Ok();
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
[HttpPatch("{LibraryId}/ChangeName")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult ChangeLibraryName(string LibraryId, [FromBody] string newName)
{
try
{
LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
if(newName.Length < 1)
return BadRequest();
library.LibraryName = newName;
context.SaveChanges();
return Ok();
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
[HttpPut]
[ProducesResponseType<LocalLibrary>(Status200OK, "application/json")]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult CreateNewLibrary([FromBody] NewLibraryRecord library)
{
try
{
LocalLibrary newLibrary = new (library.path, library.name);
context.LocalLibraries.Add(newLibrary);
context.SaveChanges();
return Ok(newLibrary);
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
[HttpDelete("{LibraryId}")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult DeleteLocalLibrary(string LibraryId)
{
try
{
LocalLibrary? library = context.LocalLibraries.Find(LibraryId);
if (library is null)
return NotFound();
context.Remove(library);
context.SaveChanges();
return Ok();
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
}

View File

@ -23,7 +23,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
public IActionResult GetAllManga()
{
Manga[] ret = context.Manga.ToArray();
Manga[] ret = context.Mangas.ToArray();
return Ok(ret);
}
@ -36,7 +36,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
public IActionResult GetManga([FromBody]string[] ids)
{
Manga[] ret = context.Manga.Where(m => ids.Contains(m.MangaId)).ToArray();
Manga[] ret = context.Mangas.Where(m => ids.Contains(m.MangaId)).ToArray();
return Ok(ret);
}
@ -51,7 +51,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetManga(string MangaId)
{
Manga? ret = context.Manga.Find(MangaId);
Manga? ret = context.Mangas.Find(MangaId);
if (ret is null)
return NotFound();
return Ok(ret);
@ -72,7 +72,7 @@ public class MangaController(PgsqlContext context) : Controller
{
try
{
Manga? ret = context.Manga.Find(MangaId);
Manga? ret = context.Mangas.Find(MangaId);
if (ret is null)
return NotFound();
@ -103,7 +103,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetCover(string MangaId, [FromQuery]int? width, [FromQuery]int? height)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
if (!System.IO.File.Exists(m.CoverFileNameInCache))
@ -148,7 +148,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetChapters(string MangaId)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
@ -169,7 +169,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetChaptersDownloaded(string MangaId)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
@ -193,7 +193,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetChaptersNotDownloaded(string MangaId)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
@ -219,7 +219,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult GetLatestChapter(string MangaId)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
@ -249,7 +249,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult GetLatestChapterDownloaded(string MangaId)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
@ -277,7 +277,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult IgnoreChaptersBefore(string MangaId, [FromBody]float chapterThreshold)
{
Manga? m = context.Manga.Find(MangaId);
Manga? m = context.Mangas.Find(MangaId);
if (m is null)
return NotFound();
@ -305,7 +305,7 @@ public class MangaController(PgsqlContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public IActionResult MoveFolder(string MangaId, [FromBody]string folder)
{
Manga? manga = context.Manga.Find(MangaId);
Manga? manga = context.Mangas.Find(MangaId);
if (manga is null)
return NotFound();
MoveFileOrFolderJob dep = manga.UpdateFolderName(TrangaSettings.downloadLocation, folder);

View File

@ -36,7 +36,7 @@ public class QueryController(PgsqlContext context) : Controller
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
public IActionResult GetMangaWithAuthorIds(string AuthorId)
{
return Ok(context.Manga.Where(m => m.AuthorIds.Contains(AuthorId)));
return Ok(context.Mangas.Where(m => m.AuthorIds.Contains(AuthorId)));
}
/// <summary>
@ -50,7 +50,7 @@ public class QueryController(PgsqlContext context) : Controller
[ProducesResponseType(Status404NotFound)]
public IActionResult GetLink(string LinkId)
{
Link? ret = context.Link.Find(LinkId);
Link? ret = context.Links.Find(LinkId);
if (ret is null)
return NotFound();
return Ok(ret);
@ -82,7 +82,7 @@ public class QueryController(PgsqlContext context) : Controller
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
public IActionResult GetMangasWithTag(string Tag)
{
return Ok(context.Manga.Where(m => m.Tags.Contains(Tag)));
return Ok(context.Mangas.Where(m => m.Tags.Contains(Tag)));
}
/// <summary>

View File

@ -131,7 +131,7 @@ public class SearchController(PgsqlContext context) : Controller
if (manga is null)
return null;
Manga? existing = context.Manga.Find(manga.MangaId);
Manga? existing = context.Mangas.Find(manga.MangaId);
if (tags is not null)
{
@ -163,13 +163,13 @@ public class SearchController(PgsqlContext context) : Controller
{
IEnumerable<Link> mergedLinks = links.Select(ml =>
{
Link? inDb = context.Link.Find(ml.LinkId);
Link? inDb = context.Links.Find(ml.LinkId);
return inDb ?? ml;
});
manga.Links = mergedLinks.ToList();
IEnumerable<Link> newLinks = manga.Links
.Where(ml => !context.Link.Select(l => l.LinkId).Contains(ml.LinkId));
context.Link.AddRange(newLinks);
.Where(ml => !context.Links.Select(l => l.LinkId).Contains(ml.LinkId));
context.Links.AddRange(newLinks);
}
if (altTitles is not null)
@ -187,9 +187,9 @@ public class SearchController(PgsqlContext context) : Controller
existing?.UpdateWithInfo(manga);
if(existing is not null)
context.Manga.Update(existing);
context.Mangas.Update(existing);
else
context.Manga.Add(manga);
context.Mangas.Add(manga);
context.Jobs.Add(new DownloadMangaCoverJob(manga.MangaId));
context.Jobs.Add(new RetrieveChaptersJob(0, manga.MangaId));