mirror of
https://github.com/C9Glax/tranga.git
synced 2025-05-07 15:42:10 +02:00
Compare commits
12 Commits
1d2ca4d76a
...
d314559361
Author | SHA1 | Date | |
---|---|---|---|
d314559361 | |||
5ab0bd0b78 | |||
5c0ace291b | |||
0d931bc835 | |||
e1e5a45960 | |||
3ecbc1a805 | |||
3305519307 | |||
9fca2d81ab | |||
949c0cc16d | |||
5921e524a9 | |||
bf332717a5 | |||
bb31a94eea |
@ -12,10 +12,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.74" />
|
||||
<PackageReference Include="log4net" Version="3.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||
<PackageReference Include="log4net" Version="3.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@ -24,10 +24,10 @@
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
||||
<PackageReference Include="PuppeteerSharp" Version="20.1.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||
<PackageReference Include="Soenneker.Utils.String.NeedlemanWunsch" Version="3.0.919" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="Soenneker.Utils.String.NeedlemanWunsch" Version="3.0.920" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -15,7 +15,7 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Returns all Jobs
|
||||
/// </summary>
|
||||
/// <returns>Array of Jobs</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<Job[]>(Status200OK)]
|
||||
public IActionResult GetAllJobs()
|
||||
@ -28,7 +28,7 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// Returns Jobs with requested Job-IDs
|
||||
/// </summary>
|
||||
/// <param name="ids">Array of Job-IDs</param>
|
||||
/// <returns>Array of Jobs</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpPost("WithIDs")]
|
||||
[ProducesResponseType<Job[]>(Status200OK)]
|
||||
public IActionResult GetJobs([FromBody]string[] ids)
|
||||
@ -41,7 +41,7 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// Get all Jobs in requested State
|
||||
/// </summary>
|
||||
/// <param name="state">Requested Job-State</param>
|
||||
/// <returns>Array of Jobs</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("State/{state}")]
|
||||
[ProducesResponseType<Job[]>(Status200OK)]
|
||||
public IActionResult GetJobsInState(JobState state)
|
||||
@ -54,7 +54,7 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// Returns all Jobs of requested Type
|
||||
/// </summary>
|
||||
/// <param name="type">Requested Job-Type</param>
|
||||
/// <returns>Array of Jobs</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("Type/{type}")]
|
||||
[ProducesResponseType<Job[]>(Status200OK)]
|
||||
public IActionResult GetJobsOfType(JobType type)
|
||||
@ -67,7 +67,8 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// Return Job with ID
|
||||
/// </summary>
|
||||
/// <param name="id">Job-ID</param>
|
||||
/// <returns>Job</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Job with ID could not be found</response>
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType<Job>(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
@ -84,8 +85,10 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Create a new CreateNewDownloadChapterJob
|
||||
/// </summary>
|
||||
/// <param name="request">ID of the Manga, and how often we check again</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <param name="mangaId">ID of Manga</param>
|
||||
/// <param name="recurrenceTime">How often should we check for new chapters</param>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("NewDownloadChapterJob/{mangaId}")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
@ -99,7 +102,8 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// Create a new DownloadSingleChapterJob
|
||||
/// </summary>
|
||||
/// <param name="chapterId">ID of the Chapter</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("DownloadSingleChapterJob/{chapterId}")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
@ -113,7 +117,8 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// Create a new UpdateMetadataJob
|
||||
/// </summary>
|
||||
/// <param name="mangaId">ID of the Manga</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("UpdateMetadataJob/{mangaId}")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
@ -126,7 +131,8 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Create a new UpdateMetadataJob for all Manga
|
||||
/// </summary>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("UpdateMetadataJob")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
@ -161,12 +167,14 @@ public class JobController(PgsqlContext context) : Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Job with ID
|
||||
/// Delete Job with ID and all children
|
||||
/// </summary>
|
||||
/// <param name="id">Job-ID</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200">Job(s) deleted</response>
|
||||
/// <response code="404">Job could not be found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType<string[]>(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status500InternalServerError)]
|
||||
public IActionResult DeleteJob(string id)
|
||||
@ -174,14 +182,57 @@ public class JobController(PgsqlContext context) : Controller
|
||||
try
|
||||
{
|
||||
Job? ret = context.Jobs.Find(id);
|
||||
switch (ret is not null)
|
||||
{
|
||||
case true:
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
case false: return NotFound();
|
||||
}
|
||||
if(ret is null)
|
||||
return NotFound();
|
||||
IQueryable<Job> children = GetChildJobs(id);
|
||||
|
||||
context.RemoveRange(children);
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return new OkObjectResult(children.Select(x => x.JobId).Append(ret.JobId).ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private IQueryable<Job> GetChildJobs(string parentJobId)
|
||||
{
|
||||
IQueryable<Job> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify Job with ID
|
||||
/// </summary>
|
||||
/// <param name="id">Job-ID</param>
|
||||
/// <param name="modifyJobRecord">Fields to modify, set to null to keep previous value</param>
|
||||
/// <response code="202">Job modified</response>
|
||||
/// <response code="400">Malformed request</response>
|
||||
/// <response code="404">Job with ID not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPatch("{id}/")]
|
||||
[ProducesResponseType<Job>(Status202Accepted)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult ModifyJob(string id, [FromBody]ModifyJobRecord modifyJobRecord)
|
||||
{
|
||||
try
|
||||
{
|
||||
Job? ret = context.Jobs.Find(id);
|
||||
if(ret is null)
|
||||
return NotFound();
|
||||
|
||||
ret.RecurrenceMs = modifyJobRecord.RecurrenceMs ?? ret.RecurrenceMs;
|
||||
ret.Enabled = modifyJobRecord.Enabled ?? ret.Enabled;
|
||||
|
||||
context.SaveChanges();
|
||||
return new AcceptedResult(ret.JobId, ret);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -196,12 +247,12 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// <response code="202">Job started</response>
|
||||
/// <response code="404">Job with ID not found</response>
|
||||
/// <response code="409">Job was already running</response>
|
||||
/// <response code="500">Internal Error</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("{id}/Start")]
|
||||
[ProducesResponseType<AcceptedResult>(Status202Accepted)]
|
||||
[ProducesResponseType<NotFoundResult>(Status404NotFound)]
|
||||
[ProducesResponseType<ConflictResult>(Status409Conflict)]
|
||||
[ProducesResponseType<ObjectResult>(Status500InternalServerError)]
|
||||
[ProducesResponseType(Status202Accepted)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status409Conflict)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult StartJob(string id)
|
||||
{
|
||||
Job? ret = context.Jobs.Find(id);
|
||||
@ -222,21 +273,13 @@ public class JobController(PgsqlContext context) : Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NOT IMPLEMENTED. Stops the Job with the requested ID
|
||||
/// Stops the Job with the requested ID
|
||||
/// </summary>
|
||||
/// <param name="id">Job-ID</param>
|
||||
/// <response code="202">Job started</response>
|
||||
/// <response code="404">Job with ID not found</response>
|
||||
/// <response code="409">Job was not running</response>
|
||||
/// <response code="500">Internal Error</response>
|
||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
||||
[ProducesResponseType<AcceptedResult>(Status202Accepted)]
|
||||
[ProducesResponseType<NotFoundResult>(Status404NotFound)]
|
||||
[ProducesResponseType<ConflictResult>(Status409Conflict)]
|
||||
[ProducesResponseType<ObjectResult>(Status500InternalServerError)]
|
||||
[HttpPost("{id}/Stop")]
|
||||
public IActionResult StopJob(string id)
|
||||
{
|
||||
return NotFound(new ProblemResponse("Not implemented")); //TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Gets all configured Library-Connectors
|
||||
/// </summary>
|
||||
/// <returns>Array of configured Library-Connectors</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<LibraryConnector[]>(Status200OK)]
|
||||
public IActionResult GetAllConnectors()
|
||||
@ -28,7 +28,8 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
||||
/// Returns Library-Connector with requested ID
|
||||
/// </summary>
|
||||
/// <param name="id">Library-Connector-ID</param>
|
||||
/// <returns>Library-Connector</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType<LibraryConnector>(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
@ -46,7 +47,8 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
||||
/// Creates a new Library-Connector
|
||||
/// </summary>
|
||||
/// <param name="libraryConnector">Library-Connector</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
@ -68,7 +70,9 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
||||
/// Deletes the Library-Connector with the requested ID
|
||||
/// </summary>
|
||||
/// <param name="id">Library-Connector-ID</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
|
79
API/Controllers/MangaConnectorController.cs
Normal file
79
API/Controllers/MangaConnectorController.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using API.Schema;
|
||||
using API.Schema.MangaConnectors;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("v{v:apiVersion}")]
|
||||
public class MangaConnectorController(PgsqlContext context) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all available Connectors (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK)]
|
||||
public IActionResult GetConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.ToArray();
|
||||
return Ok(connectors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all enabled Connectors (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("enabled")]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK)]
|
||||
public IActionResult GetEnabledConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.Where(c => c.Enabled == true).ToArray();
|
||||
return Ok(connectors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all disabled Connectors (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("disabled")]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK)]
|
||||
public IActionResult GetDisabledConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.Where(c => c.Enabled == false).ToArray();
|
||||
return Ok(connectors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enabled or disables a Connector
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the connector</param>
|
||||
/// <param name="enabled">Set true to enable</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
[HttpPatch("{id}/SetEnabled/{enabled}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult SetEnabled(string id, bool enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
MangaConnector? connector = context.MangaConnectors.Find(id);
|
||||
if (connector is null)
|
||||
return NotFound();
|
||||
|
||||
connector.Enabled = enabled;
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ public class MangaController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Returns all cached Manga
|
||||
/// </summary>
|
||||
/// <returns>Array of Manga</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||
public IActionResult GetAllManga()
|
||||
@ -27,7 +27,7 @@ public class MangaController(PgsqlContext context) : Controller
|
||||
/// Returns all cached Manga with IDs
|
||||
/// </summary>
|
||||
/// <param name="ids">Array of Manga-IDs</param>
|
||||
/// <returns>Array of Manga</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpPost("WithIDs")]
|
||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||
public IActionResult GetManga([FromBody]string[] ids)
|
||||
@ -40,42 +40,41 @@ public class MangaController(PgsqlContext context) : Controller
|
||||
/// Return Manga with ID
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <returns>Manga</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Manga with ID not found</response>
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType<Manga>(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetManga(string id)
|
||||
{
|
||||
Manga? ret = context.Manga.Find(id);
|
||||
return (ret is not null) switch
|
||||
{
|
||||
true => Ok(ret),
|
||||
false => NotFound()
|
||||
};
|
||||
if (ret is null)
|
||||
return NotFound();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Manga with ID
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Manga with ID not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status500InternalServerError)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult DeleteManga(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
Manga? ret = context.Manga.Find(id);
|
||||
switch (ret is not null)
|
||||
{
|
||||
case true:
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
case false: return NotFound();
|
||||
}
|
||||
if (ret is null)
|
||||
return NotFound();
|
||||
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -87,28 +86,29 @@ public class MangaController(PgsqlContext context) : Controller
|
||||
/// Returns URL of Cover of Manga
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <returns>URL of Cover</returns>
|
||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
||||
[HttpGet("{id}/Cover")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult GetCover(string id)
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Chapters of Manga
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <returns>Array of Chapters</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Manga with ID not found</response>
|
||||
[HttpGet("{id}/Chapters")]
|
||||
[ProducesResponseType<Chapter[]>(Status200OK)]
|
||||
[ProducesResponseType<string>(Status404NotFound)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetChapters(string id)
|
||||
{
|
||||
Manga? m = context.Manga.Find(id);
|
||||
if (m is null)
|
||||
return NotFound("Manga could not be found");
|
||||
Chapter[] ret = context.Chapters.Where(c => c.ParentManga.MangaId == m.MangaId).ToArray();
|
||||
return NotFound();
|
||||
|
||||
Chapter[] ret = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToArray();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
@ -116,35 +116,46 @@ public class MangaController(PgsqlContext context) : Controller
|
||||
/// Returns the latest Chapter of requested Manga
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <returns>Latest Chapter</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="204">No available chapters</response>
|
||||
/// <response code="404">Manga with ID not found.</response>
|
||||
/// <response code="500">Could not retrieve the maximum chapter-number</response>
|
||||
[HttpGet("{id}/Chapter/Latest")]
|
||||
[ProducesResponseType<Chapter>(Status200OK)]
|
||||
[ProducesResponseType<string>(Status404NotFound)]
|
||||
[ProducesResponseType(Status204NoContent)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult GetLatestChapter(string id)
|
||||
{
|
||||
Manga? m = context.Manga.Find(id);
|
||||
if (m is null)
|
||||
return NotFound("Manga could not be found");
|
||||
List<Chapter> chapters = context.Chapters.Where(c => c.ParentManga.MangaId == m.MangaId).ToList();
|
||||
return NotFound();
|
||||
|
||||
List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToList();
|
||||
if (chapters.Count == 0)
|
||||
return NoContent();
|
||||
|
||||
Chapter? max = chapters.Max();
|
||||
if (max is null)
|
||||
return NotFound("Chapter could not be found");
|
||||
return StatusCode(500, "Max chapter could not be found");
|
||||
|
||||
return Ok(max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the cut-off for Manga
|
||||
/// </summary>
|
||||
/// <remarks>This is important for the DownloadNewChapters-Job</remarks>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Manga with ID not found.</response>
|
||||
[HttpPatch("{id}/IgnoreChaptersBefore")]
|
||||
[ProducesResponseType<float>(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult IgnoreChaptersBefore(string id)
|
||||
{
|
||||
Manga? m = context.Manga.Find(id);
|
||||
if (m is null)
|
||||
return NotFound("Manga could not be found");
|
||||
return NotFound();
|
||||
return Ok(m.IgnoreChapterBefore);
|
||||
}
|
||||
|
||||
@ -153,11 +164,10 @@ public class MangaController(PgsqlContext context) : Controller
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-ID</param>
|
||||
/// <param name="folder">New Directory-Path</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
||||
[HttpPost("{id}/MoveFolder")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult MoveFolder(string id, [FromBody]string folder)
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using API.Schema;
|
||||
using API.Schema.MangaConnectors;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("v{v:apiVersion}")]
|
||||
public class MiscController(PgsqlContext context) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all available Connectors (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <returns>Array of MangaConnector</returns>
|
||||
[HttpGet("GetConnectors")]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK)]
|
||||
public IActionResult GetConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.ToArray();
|
||||
return Ok(connectors);
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Gets all configured Notification-Connectors
|
||||
/// </summary>
|
||||
/// <returns>Array of configured Notification-Connectors</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<NotificationConnector[]>(Status200OK)]
|
||||
public IActionResult GetAllConnectors()
|
||||
@ -28,7 +28,8 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
||||
/// Returns Notification-Connector with requested ID
|
||||
/// </summary>
|
||||
/// <param name="id">Notification-Connector-ID</param>
|
||||
/// <returns>Notification-Connector</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">NotificationConnector with ID not found</response>
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType<NotificationConnector>(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
@ -46,7 +47,8 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
||||
/// Creates a new Notification-Connector
|
||||
/// </summary>
|
||||
/// <param name="notificationConnector">Notification-Connector</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="201"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut]
|
||||
[ProducesResponseType<NotificationConnector[]>(Status200OK)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
@ -68,7 +70,9 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
||||
/// Deletes the Notification-Connector with the requested ID
|
||||
/// </summary>
|
||||
/// <param name="id">Notification-Connector-ID</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">NotificationConnector with ID not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{id}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
@ -78,14 +82,12 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
||||
try
|
||||
{
|
||||
NotificationConnector? ret = context.NotificationConnectors.Find(id);
|
||||
switch (ret is not null)
|
||||
{
|
||||
case true:
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
case false: return NotFound();
|
||||
}
|
||||
if(ret is null)
|
||||
return NotFound();
|
||||
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -18,14 +18,17 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
/// Initiate a search for a Manga on all Connectors
|
||||
/// </summary>
|
||||
/// <param name="name">Name/Title of the Manga</param>
|
||||
/// <returns>Array of Manga</returns>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("{name}")]
|
||||
[ProducesResponseType<Manga[]>(Status500InternalServerError)]
|
||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult SearchMangaGlobal(string name)
|
||||
{
|
||||
List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> allManga = new();
|
||||
foreach (MangaConnector contextMangaConnector in context.MangaConnectors)
|
||||
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)
|
||||
{
|
||||
@ -35,9 +38,9 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
if(add is not null)
|
||||
retMangas.Add(add);
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
catch (Exception e)
|
||||
{
|
||||
return StatusCode(500, new ProblemResponse("An error occurred while processing your request."));
|
||||
return StatusCode(500, e);
|
||||
}
|
||||
}
|
||||
return Ok(retMangas.ToArray());
|
||||
@ -48,16 +51,23 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
/// </summary>
|
||||
/// <param name="id">Manga-Connector-ID</param>
|
||||
/// <param name="name">Name/Title of the Manga</param>
|
||||
/// <returns>Manga</returns>
|
||||
/// <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("{id}/{name}")]
|
||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||
[ProducesResponseType<ProblemResponse>(Status404NotFound)]
|
||||
[ProducesResponseType<ProblemResponse>(Status500InternalServerError)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status406NotAcceptable)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult SearchManga(string id, string name)
|
||||
{
|
||||
MangaConnector? connector = context.MangaConnectors.Find(id);
|
||||
if (connector is null)
|
||||
return NotFound(new ProblemResponse("Connector not found."));
|
||||
return NotFound();
|
||||
else if (connector.Enabled is false)
|
||||
return StatusCode(406);
|
||||
|
||||
(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] mangas = connector.GetManga(name);
|
||||
List<Manga> retMangas = new();
|
||||
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in mangas)
|
||||
@ -68,15 +78,15 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
if(add is not null)
|
||||
retMangas.Add(add);
|
||||
}
|
||||
catch (DbUpdateException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
return StatusCode(500, new ProblemResponse("An error occurred while processing your request.", e.Message));
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(retMangas.ToArray());
|
||||
}
|
||||
|
||||
|
||||
private Manga? AddMangaToContext(Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links,
|
||||
List<MangaAltTitle>? altTitles)
|
||||
{
|
||||
@ -144,7 +154,7 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
context.Manga.Update(existing);
|
||||
else
|
||||
context.Manga.Add(manga);
|
||||
|
||||
|
||||
context.SaveChanges();
|
||||
return existing ?? manga;
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
using API.Schema;
|
||||
using System.Text.Json.Nodes;
|
||||
using API.MangaDownloadClients;
|
||||
using API.Schema;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
@ -14,136 +16,144 @@ public class SettingsController(PgsqlContext context) : Controller
|
||||
/// <summary>
|
||||
/// Get all Settings
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<JsonObject>(StatusCodes.Status200OK)]
|
||||
public IActionResult GetSettings()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
return Ok(TrangaSettings.Serialize());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current UserAgent used by Tranga
|
||||
/// </summary>
|
||||
/// <returns>UserAgent as string</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("UserAgent")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<string>(Status200OK)]
|
||||
public IActionResult GetUserAgent()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
return Ok(TrangaSettings.userAgent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a new UserAgent
|
||||
/// </summary>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpPatch("UserAgent")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult SetUserAgent()
|
||||
[ProducesResponseType(Status200OK)]
|
||||
public IActionResult SetUserAgent([FromBody]string userAgent)
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
TrangaSettings.UpdateUserAgent(userAgent);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the UserAgent to default
|
||||
/// </summary>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpDelete("UserAgent")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
public IActionResult ResetUserAgent()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
TrangaSettings.UpdateUserAgent(TrangaSettings.DefaultUserAgent);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all Request-Limits
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("RequestLimits")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<Dictionary<RequestType,int>>(Status200OK)]
|
||||
public IActionResult GetRequestLimits()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
return Ok(TrangaSettings.requestLimits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update all Request-Limits to new values
|
||||
/// </summary>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
||||
[HttpPatch("RequestLimits")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult SetRequestLimits()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all Request-Limits
|
||||
/// </summary>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpDelete("RequestLimits")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<string>(Status200OK)]
|
||||
public IActionResult ResetRequestLimits()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
TrangaSettings.ResetRequestLimits();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Level of Image-Compression for Images
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <response code="200">JPEG compression-level as Integer</response>
|
||||
[HttpGet("ImageCompression")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<int>(Status200OK)]
|
||||
public IActionResult GetImageCompression()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
return Ok(TrangaSettings.compression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Image-Compression-Level for Images
|
||||
/// </summary>
|
||||
/// <param name="percentage">100 to disable, 0-99 for JPEG compression-Level</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <param name="level">100 to disable, 0-99 for JPEG compression-Level</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="400">Level outside permitted range</response>
|
||||
[HttpPatch("ImageCompression")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
public IActionResult SetImageCompression(int percentage)
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
public IActionResult SetImageCompression(int level)
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
if (level < 0 || level > 100)
|
||||
return BadRequest();
|
||||
TrangaSettings.UpdateCompressImages(level);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get state of Black/White-Image setting
|
||||
/// </summary>
|
||||
/// <returns>True if enabled</returns>
|
||||
/// <response code="200">True if enabled</response>
|
||||
[HttpGet("BWImages")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<bool>(Status200OK)]
|
||||
public IActionResult GetBwImagesToggle()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
return Ok(TrangaSettings.bwImages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable/Disable conversion of Images to Black and White
|
||||
/// </summary>
|
||||
/// <param name="enabled">true to enable</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpPatch("BWImages")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
public IActionResult SetBwImagesToggle(bool enabled)
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
TrangaSettings.UpdateBwImages(enabled);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get state of April Fools Mode
|
||||
/// </summary>
|
||||
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
|
||||
/// <returns>True if enabled</returns>
|
||||
/// <response code="200">True if enabled</response>
|
||||
[HttpGet("AprilFoolsMode")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType<bool>(Status200OK)]
|
||||
public IActionResult GetAprilFoolsMode()
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
return Ok(TrangaSettings.aprilFoolsMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -151,11 +161,12 @@ public class SettingsController(PgsqlContext context) : Controller
|
||||
/// </summary>
|
||||
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
|
||||
/// <param name="enabled">true to enable</param>
|
||||
/// <returns>Nothing</returns>
|
||||
/// <response code="200"></response>
|
||||
[HttpPatch("AprilFoolsMode")]
|
||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
public IActionResult SetAprilFoolsMode(bool enabled)
|
||||
{
|
||||
return StatusCode(500, "Not implemented"); //TODO
|
||||
TrangaSettings.UpdateAprilFoolsMode(enabled);
|
||||
return Ok();
|
||||
}
|
||||
}
|
678
API/Migrations/20250307104111_dev-070325-1.Designer.cs
generated
Normal file
678
API/Migrations/20250307104111_dev-070325-1.Designer.cs
generated
Normal file
@ -0,0 +1,678 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using API.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
[DbContext(typeof(PgsqlContext))]
|
||||
[Migration("20250307104111_dev-070325-1")]
|
||||
partial class dev0703251
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("API.Schema.Author", b =>
|
||||
{
|
||||
b.Property<string>("AuthorId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("AuthorName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("AuthorId");
|
||||
|
||||
b.ToTable("Authors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Chapter", b =>
|
||||
{
|
||||
b.Property<string>("ChapterId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("ArchiveFileName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ChapterNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)");
|
||||
|
||||
b.Property<bool>("Downloaded")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("ParentMangaId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("VolumeNumber")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("ChapterId");
|
||||
|
||||
b.HasIndex("ParentMangaId");
|
||||
|
||||
b.ToTable("Chapters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.Job", b =>
|
||||
{
|
||||
b.Property<string>("JobId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.PrimitiveCollection<string[]>("DependsOnJobsIds")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("JobId1")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<byte>("JobType")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<DateTime>("LastExecution")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ParentJobId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<decimal>("RecurrenceMs")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<byte>("state")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("JobId");
|
||||
|
||||
b.HasIndex("JobId1");
|
||||
|
||||
b.HasIndex("ParentJobId");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
|
||||
b.HasDiscriminator<byte>("JobType");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.LibraryConnectors.LibraryConnector", b =>
|
||||
{
|
||||
b.Property<string>("LibraryConnectorId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Auth")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("BaseUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte>("LibraryType")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("LibraryConnectorId");
|
||||
|
||||
b.ToTable("LibraryConnectors");
|
||||
|
||||
b.HasDiscriminator<byte>("LibraryType");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Link", b =>
|
||||
{
|
||||
b.Property<string>("LinkId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("LinkProvider")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LinkUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("MangaId")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.HasKey("LinkId");
|
||||
|
||||
b.HasIndex("MangaId");
|
||||
|
||||
b.ToTable("Link");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Manga", b =>
|
||||
{
|
||||
b.Property<string>("MangaId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("ConnectorId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("CoverFileNameInCache")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("CoverUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("FolderName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<float>("IgnoreChapterBefore")
|
||||
.HasColumnType("real");
|
||||
|
||||
b.Property<string>("MangaConnectorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte>("ReleaseStatus")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<string>("WebsiteUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("Year")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("MangaId");
|
||||
|
||||
b.HasIndex("MangaConnectorId");
|
||||
|
||||
b.ToTable("Manga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaAltTitle", b =>
|
||||
{
|
||||
b.Property<string>("AltTitleId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("character varying(8)");
|
||||
|
||||
b.Property<string>("MangaId")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("AltTitleId");
|
||||
|
||||
b.HasIndex("MangaId");
|
||||
|
||||
b.ToTable("AltTitles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.MangaConnector", b =>
|
||||
{
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.PrimitiveCollection<string[]>("BaseUris")
|
||||
.IsRequired()
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.PrimitiveCollection<string[]>("SupportedLanguages")
|
||||
.IsRequired()
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.HasKey("Name");
|
||||
|
||||
b.ToTable("MangaConnectors");
|
||||
|
||||
b.HasDiscriminator<string>("Name").HasValue("MangaConnector");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaTag", b =>
|
||||
{
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Tag");
|
||||
|
||||
b.ToTable("Tags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Notification", b =>
|
||||
{
|
||||
b.Property<string>("NotificationId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte>("Urgency")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("NotificationId");
|
||||
|
||||
b.ToTable("Notifications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.NotificationConnectors.NotificationConnector", b =>
|
||||
{
|
||||
b.Property<string>("NotificationConnectorId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<byte>("NotificationConnectorType")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("NotificationConnectorId");
|
||||
|
||||
b.ToTable("NotificationConnectors");
|
||||
|
||||
b.HasDiscriminator<byte>("NotificationConnectorType");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AuthorManga", b =>
|
||||
{
|
||||
b.Property<string>("AuthorsAuthorId")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("MangaId")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.HasKey("AuthorsAuthorId", "MangaId");
|
||||
|
||||
b.HasIndex("MangaId");
|
||||
|
||||
b.ToTable("AuthorManga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MangaMangaTag", b =>
|
||||
{
|
||||
b.Property<string>("MangaId")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("TagsTag")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("MangaId", "TagsTag");
|
||||
|
||||
b.HasIndex("TagsTag");
|
||||
|
||||
b.ToTable("MangaMangaTag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadNewChaptersJob", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.Jobs.Job");
|
||||
|
||||
b.Property<string>("MangaId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.HasIndex("MangaId");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)1);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.Jobs.Job");
|
||||
|
||||
b.Property<string>("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<string>("FromLocation")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ToLocation")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)3);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.Jobs.Job");
|
||||
|
||||
b.Property<string>("MangaId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.HasIndex("MangaId");
|
||||
|
||||
b.ToTable("Jobs", t =>
|
||||
{
|
||||
t.Property("MangaId")
|
||||
.HasColumnName("UpdateMetadataJob_MangaId");
|
||||
});
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)2);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.LibraryConnectors.Kavita", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)1);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.LibraryConnectors.Komga", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.LibraryConnectors.LibraryConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)0);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.AsuraToon", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("AsuraToon");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.Bato", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("Bato");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.MangaDex", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("MangaDex");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.MangaHere", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("MangaHere");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.MangaKatana", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("MangaKatana");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.Mangaworld", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("Mangaworld");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.ManhuaPlus", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("ManhuaPlus");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaConnectors.Weebcentral", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.MangaConnectors.MangaConnector");
|
||||
|
||||
b.HasDiscriminator().HasValue("Weebcentral");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.NotificationConnectors.Gotify", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.NotificationConnectors.NotificationConnector");
|
||||
|
||||
b.Property<string>("AppToken")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Endpoint")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)0);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.NotificationConnectors.Lunasea", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.NotificationConnectors.NotificationConnector");
|
||||
|
||||
b.Property<string>("Id")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)1);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.NotificationConnectors.Ntfy", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.NotificationConnectors.NotificationConnector");
|
||||
|
||||
b.Property<string>("Auth")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Endpoint")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Topic")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.ToTable("NotificationConnectors", t =>
|
||||
{
|
||||
t.Property("Endpoint")
|
||||
.HasColumnName("Ntfy_Endpoint");
|
||||
});
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)2);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Chapter", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "ParentManga")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentMangaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ParentManga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.Job", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Jobs.Job", null)
|
||||
.WithMany("DependsOnJobs")
|
||||
.HasForeignKey("JobId1");
|
||||
|
||||
b.HasOne("API.Schema.Jobs.Job", "ParentJob")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentJobId");
|
||||
|
||||
b.Navigation("ParentJob");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Link", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", null)
|
||||
.WithMany("Links")
|
||||
.HasForeignKey("MangaId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Manga", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.MangaConnectors.MangaConnector", "MangaConnector")
|
||||
.WithMany()
|
||||
.HasForeignKey("MangaConnectorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MangaConnector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.MangaAltTitle", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", null)
|
||||
.WithMany("AltTitles")
|
||||
.HasForeignKey("MangaId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AuthorManga", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Author", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorsAuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Schema.Manga", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("MangaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MangaMangaTag", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("MangaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Schema.MangaTag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsTag")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadNewChaptersJob", 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.UpdateMetadataJob", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "Manga")
|
||||
.WithMany()
|
||||
.HasForeignKey("MangaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Manga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.Job", b =>
|
||||
{
|
||||
b.Navigation("DependsOnJobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Manga", b =>
|
||||
{
|
||||
b.Navigation("AltTitles");
|
||||
|
||||
b.Navigation("Links");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
40
API/Migrations/20250307104111_dev-070325-1.cs
Normal file
40
API/Migrations/20250307104111_dev-070325-1.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class dev0703251 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Enabled",
|
||||
table: "MangaConnectors",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Enabled",
|
||||
table: "Jobs",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Enabled",
|
||||
table: "MangaConnectors");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Enabled",
|
||||
table: "Jobs");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace API.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.0")
|
||||
.HasAnnotation("ProductVersion", "9.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@ -86,6 +86,9 @@ namespace API.Migrations
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("JobId1")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
@ -260,6 +263,9 @@ namespace API.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.PrimitiveCollection<string[]>("SupportedLanguages")
|
||||
.IsRequired()
|
||||
.HasColumnType("text[]");
|
||||
|
3
API/ModifyJobRecord.cs
Normal file
3
API/ModifyJobRecord.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace API;
|
||||
|
||||
public record ModifyJobRecord(ulong? RecurrenceMs, bool? Enabled);
|
@ -1,3 +0,0 @@
|
||||
namespace API;
|
||||
|
||||
public record ProblemResponse(string title, string? message = null);
|
@ -84,7 +84,7 @@ public class Chapter : IComparable<Chapter>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates full file path of chapter-archive
|
||||
/// Creates full file path of chapter-archive
|
||||
/// </summary>
|
||||
/// <returns>Filepath</returns>
|
||||
internal string GetArchiveFilePath()
|
||||
@ -92,6 +92,10 @@ public class Chapter : IComparable<Chapter>
|
||||
return Path.Join(TrangaSettings.downloadLocation, ParentManga.FolderName, ArchiveFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the filesystem if an archive at the ArchiveFilePath exists
|
||||
/// </summary>
|
||||
/// <returns>True if archive exists on disk</returns>
|
||||
public bool IsDownloaded()
|
||||
{
|
||||
string path = GetArchiveFilePath();
|
||||
|
@ -20,15 +20,9 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
Chapter c = Chapter ?? context.Chapters.Find(ChapterId)!;
|
||||
Manga m = c.ParentManga ?? context.Manga.Find(c.ParentMangaId)!;
|
||||
MangaConnector connector = m.MangaConnector ?? context.MangaConnectors.Find(m.MangaConnectorId)!;
|
||||
DownloadChapterImages(c, connector, m);
|
||||
return [];
|
||||
}
|
||||
|
||||
private bool DownloadChapterImages(Chapter chapter, MangaConnector connector, Manga manga)
|
||||
{
|
||||
Chapter chapter = Chapter ?? context.Chapters.Find(ChapterId)!;
|
||||
Manga manga = chapter.ParentManga ?? context.Manga.Find(chapter.ParentMangaId)!;
|
||||
MangaConnector connector = manga.MangaConnector ?? context.MangaConnectors.Find(manga.MangaConnectorId)!;
|
||||
string[] imageUrls = connector.GetChapterImageUrls(chapter);
|
||||
string saveArchiveFilePath = chapter.GetArchiveFilePath();
|
||||
|
||||
@ -52,7 +46,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
if (imageUrls.Length == 0)
|
||||
{
|
||||
Directory.Delete(tempFolder, true);
|
||||
return false;
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach (string imageUrl in imageUrls)
|
||||
@ -61,10 +55,10 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
string imagePath = Path.Join(tempFolder, $"{chapterNum++}.{extension}");
|
||||
bool status = DownloadImage(imageUrl, imagePath);
|
||||
if (status is false)
|
||||
return false;
|
||||
return [];
|
||||
}
|
||||
|
||||
CopyCoverFromCacheToDownloadLocation(manga);
|
||||
CopyCoverFromCacheToDownloadLocation(context, manga);
|
||||
|
||||
File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString());
|
||||
|
||||
@ -74,7 +68,10 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
File.SetUnixFileMode(saveArchiveFilePath, UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute | OtherRead | OtherExecute);
|
||||
Directory.Delete(tempFolder, true); //Cleanup
|
||||
|
||||
return true;
|
||||
chapter.Downloaded = true;
|
||||
context.SaveChanges();
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private void ProcessImage(string imagePath)
|
||||
@ -92,7 +89,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
});
|
||||
}
|
||||
|
||||
private void CopyCoverFromCacheToDownloadLocation(Manga manga, int? retries = 1)
|
||||
private void CopyCoverFromCacheToDownloadLocation(PgsqlContext context, Manga manga, int? retries = 1)
|
||||
{
|
||||
//Check if Publication already has a Folder and cover
|
||||
string publicationFolder = manga.CreatePublicationFolder();
|
||||
@ -107,8 +104,9 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
||||
{
|
||||
if (retries > 0)
|
||||
{
|
||||
manga.SaveCoverImageToCache();
|
||||
CopyCoverFromCacheToDownloadLocation(manga, --retries);
|
||||
manga.CoverFileNameInCache = manga.SaveCoverImageToCache();
|
||||
context.SaveChanges();
|
||||
CopyCoverFromCacheToDownloadLocation(context, manga, --retries);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -26,6 +26,7 @@ public abstract class Job
|
||||
[NotMapped]
|
||||
public DateTime NextExecution => LastExecution.AddMilliseconds(RecurrenceMs);
|
||||
public JobState state { get; internal set; } = JobState.Waiting;
|
||||
public bool Enabled { get; internal set; } = true;
|
||||
|
||||
public Job(string jobId, JobType jobType, ulong recurrenceMs, Job? parentJob = null, ICollection<Job>? dependsOnJobs = null)
|
||||
: this(jobId, jobType, recurrenceMs, parentJob?.JobId, dependsOnJobs?.Select(j => j.JobId).ToList())
|
||||
|
@ -8,10 +8,24 @@ public class UpdateMetadataJob(ulong recurrenceMs, string mangaId, string? paren
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string MangaId { get; init; } = mangaId;
|
||||
public virtual Manga Manga { get; init; }
|
||||
public virtual Manga? Manga { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates all data related to Manga.
|
||||
/// Retrieves data from Mangaconnector
|
||||
/// Updates Chapter-info
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//Manga manga = Manga ?? context.Manga.Find(MangaId)!;
|
||||
IQueryable<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == MangaId);
|
||||
foreach (Chapter chapter in chapters)
|
||||
chapter.Downloaded = chapter.IsDownloaded();
|
||||
|
||||
context.SaveChanges();
|
||||
return [];
|
||||
|
||||
//TODO implement Metadata-Update from MangaConnector
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s
|
||||
public string[] SupportedLanguages { get; init; } = supportedLanguages;
|
||||
public string[] BaseUris { get; init; } = baseUris;
|
||||
|
||||
public bool Enabled { get; internal set; } = true;
|
||||
|
||||
public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] GetManga(string publicationTitle = "");
|
||||
|
||||
public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url);
|
||||
|
@ -69,18 +69,21 @@ public static class Tranga
|
||||
Log.Info(TRANGA);
|
||||
while (true)
|
||||
{
|
||||
List<Job> completedJobs = context.Jobs.Where(j => j.state >= JobState.Completed && j.state < JobState.Failed).ToList();
|
||||
List<Job> completedJobs = context.Jobs.Where(j => j.state >= JobState.Completed).ToList();
|
||||
foreach (Job job in completedJobs)
|
||||
if (job.RecurrenceMs <= 0)
|
||||
context.Jobs.Remove(job);
|
||||
else
|
||||
{
|
||||
if (job.state >= JobState.Failed)
|
||||
job.Enabled = false;
|
||||
else
|
||||
job.state = JobState.Waiting;
|
||||
job.LastExecution = DateTime.UtcNow;
|
||||
job.state = JobState.Waiting;
|
||||
context.Jobs.Update(job);
|
||||
}
|
||||
|
||||
List<Job> runJobs = context.Jobs.Where(j => j.state <= JobState.Running).ToList()
|
||||
List<Job> runJobs = context.Jobs.Where(j => j.state <= JobState.Running && j.Enabled == true).ToList()
|
||||
.Where(j => j.NextExecution < DateTime.UtcNow).ToList();
|
||||
foreach (Job job in runJobs)
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ public static class TrangaSettings
|
||||
ExportSettings();
|
||||
}
|
||||
|
||||
public static void ResetRateLimits()
|
||||
public static void ResetRequestLimits()
|
||||
{
|
||||
requestLimits = DefaultRequestLimits;
|
||||
ExportSettings();
|
||||
|
Loading…
x
Reference in New Issue
Block a user