mirror of
https://github.com/C9Glax/tranga.git
synced 2025-05-09 00:22:08 +02:00
Compare commits
No commits in common. "d314559361e77ce02c2d8113e6b5f926ceffefa8" and "1d2ca4d76a92ee1ef1385c8bb17bc084cde0fa0f" have entirely different histories.
d314559361
...
1d2ca4d76a
@ -12,10 +12,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.74" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.74" />
|
||||||
<PackageReference Include="log4net" Version="3.0.4" />
|
<PackageReference Include="log4net" Version="3.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@ -24,10 +24,10 @@
|
|||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
||||||
<PackageReference Include="PuppeteerSharp" Version="20.1.3" />
|
<PackageReference Include="PuppeteerSharp" Version="20.1.3" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||||
<PackageReference Include="Soenneker.Utils.String.NeedlemanWunsch" Version="3.0.920" />
|
<PackageReference Include="Soenneker.Utils.String.NeedlemanWunsch" Version="3.0.919" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="9.0.2" />
|
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -15,7 +15,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all Jobs
|
/// Returns all Jobs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Jobs</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType<Job[]>(Status200OK)]
|
[ProducesResponseType<Job[]>(Status200OK)]
|
||||||
public IActionResult GetAllJobs()
|
public IActionResult GetAllJobs()
|
||||||
@ -28,7 +28,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// Returns Jobs with requested Job-IDs
|
/// Returns Jobs with requested Job-IDs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ids">Array of Job-IDs</param>
|
/// <param name="ids">Array of Job-IDs</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Jobs</returns>
|
||||||
[HttpPost("WithIDs")]
|
[HttpPost("WithIDs")]
|
||||||
[ProducesResponseType<Job[]>(Status200OK)]
|
[ProducesResponseType<Job[]>(Status200OK)]
|
||||||
public IActionResult GetJobs([FromBody]string[] ids)
|
public IActionResult GetJobs([FromBody]string[] ids)
|
||||||
@ -41,7 +41,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// Get all Jobs in requested State
|
/// Get all Jobs in requested State
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Requested Job-State</param>
|
/// <param name="state">Requested Job-State</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Jobs</returns>
|
||||||
[HttpGet("State/{state}")]
|
[HttpGet("State/{state}")]
|
||||||
[ProducesResponseType<Job[]>(Status200OK)]
|
[ProducesResponseType<Job[]>(Status200OK)]
|
||||||
public IActionResult GetJobsInState(JobState state)
|
public IActionResult GetJobsInState(JobState state)
|
||||||
@ -54,7 +54,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// Returns all Jobs of requested Type
|
/// Returns all Jobs of requested Type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Requested Job-Type</param>
|
/// <param name="type">Requested Job-Type</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Jobs</returns>
|
||||||
[HttpGet("Type/{type}")]
|
[HttpGet("Type/{type}")]
|
||||||
[ProducesResponseType<Job[]>(Status200OK)]
|
[ProducesResponseType<Job[]>(Status200OK)]
|
||||||
public IActionResult GetJobsOfType(JobType type)
|
public IActionResult GetJobsOfType(JobType type)
|
||||||
@ -67,8 +67,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// Return Job with ID
|
/// Return Job with ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Job-ID</param>
|
/// <param name="id">Job-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Job</returns>
|
||||||
/// <response code="404">Job with ID could not be found</response>
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType<Job>(Status200OK)]
|
[ProducesResponseType<Job>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
@ -85,10 +84,8 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new CreateNewDownloadChapterJob
|
/// Create a new CreateNewDownloadChapterJob
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mangaId">ID of Manga</param>
|
/// <param name="request">ID of the Manga, and how often we check again</param>
|
||||||
/// <param name="recurrenceTime">How often should we check for new chapters</param>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="201">Created new Job</response>
|
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPut("NewDownloadChapterJob/{mangaId}")]
|
[HttpPut("NewDownloadChapterJob/{mangaId}")]
|
||||||
[ProducesResponseType(Status201Created)]
|
[ProducesResponseType(Status201Created)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
@ -102,8 +99,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// Create a new DownloadSingleChapterJob
|
/// Create a new DownloadSingleChapterJob
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="chapterId">ID of the Chapter</param>
|
/// <param name="chapterId">ID of the Chapter</param>
|
||||||
/// <response code="201">Created new Job</response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPut("DownloadSingleChapterJob/{chapterId}")]
|
[HttpPut("DownloadSingleChapterJob/{chapterId}")]
|
||||||
[ProducesResponseType(Status201Created)]
|
[ProducesResponseType(Status201Created)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
@ -117,8 +113,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// Create a new UpdateMetadataJob
|
/// Create a new UpdateMetadataJob
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mangaId">ID of the Manga</param>
|
/// <param name="mangaId">ID of the Manga</param>
|
||||||
/// <response code="201">Created new Job</response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPut("UpdateMetadataJob/{mangaId}")]
|
[HttpPut("UpdateMetadataJob/{mangaId}")]
|
||||||
[ProducesResponseType(Status201Created)]
|
[ProducesResponseType(Status201Created)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
@ -131,8 +126,7 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new UpdateMetadataJob for all Manga
|
/// Create a new UpdateMetadataJob for all Manga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="201">Created new Job</response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPut("UpdateMetadataJob")]
|
[HttpPut("UpdateMetadataJob")]
|
||||||
[ProducesResponseType(Status201Created)]
|
[ProducesResponseType(Status201Created)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
@ -167,14 +161,12 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete Job with ID and all children
|
/// Delete Job with ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Job-ID</param>
|
/// <param name="id">Job-ID</param>
|
||||||
/// <response code="200">Job(s) deleted</response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="404">Job could not be found</response>
|
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType<string[]>(Status200OK)]
|
[ProducesResponseType(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
[ProducesResponseType(Status500InternalServerError)]
|
[ProducesResponseType(Status500InternalServerError)]
|
||||||
public IActionResult DeleteJob(string id)
|
public IActionResult DeleteJob(string id)
|
||||||
@ -182,57 +174,14 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Job? ret = context.Jobs.Find(id);
|
Job? ret = context.Jobs.Find(id);
|
||||||
if(ret is null)
|
switch (ret is not null)
|
||||||
return NotFound();
|
{
|
||||||
IQueryable<Job> children = GetChildJobs(id);
|
case true:
|
||||||
|
context.Remove(ret);
|
||||||
context.RemoveRange(children);
|
context.SaveChanges();
|
||||||
context.Remove(ret);
|
return Ok();
|
||||||
context.SaveChanges();
|
case false: return NotFound();
|
||||||
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -247,12 +196,12 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
/// <response code="202">Job started</response>
|
/// <response code="202">Job started</response>
|
||||||
/// <response code="404">Job with ID not found</response>
|
/// <response code="404">Job with ID not found</response>
|
||||||
/// <response code="409">Job was already running</response>
|
/// <response code="409">Job was already running</response>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
/// <response code="500">Internal Error</response>
|
||||||
[HttpPost("{id}/Start")]
|
[HttpPost("{id}/Start")]
|
||||||
[ProducesResponseType(Status202Accepted)]
|
[ProducesResponseType<AcceptedResult>(Status202Accepted)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType<NotFoundResult>(Status404NotFound)]
|
||||||
[ProducesResponseType(Status409Conflict)]
|
[ProducesResponseType<ConflictResult>(Status409Conflict)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<ObjectResult>(Status500InternalServerError)]
|
||||||
public IActionResult StartJob(string id)
|
public IActionResult StartJob(string id)
|
||||||
{
|
{
|
||||||
Job? ret = context.Jobs.Find(id);
|
Job? ret = context.Jobs.Find(id);
|
||||||
@ -273,13 +222,21 @@ public class JobController(PgsqlContext context) : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stops the Job with the requested ID
|
/// NOT IMPLEMENTED. Stops the Job with the requested ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Job-ID</param>
|
/// <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>
|
/// <remarks>NOT IMPLEMENTED</remarks>
|
||||||
|
[ProducesResponseType<AcceptedResult>(Status202Accepted)]
|
||||||
|
[ProducesResponseType<NotFoundResult>(Status404NotFound)]
|
||||||
|
[ProducesResponseType<ConflictResult>(Status409Conflict)]
|
||||||
|
[ProducesResponseType<ObjectResult>(Status500InternalServerError)]
|
||||||
[HttpPost("{id}/Stop")]
|
[HttpPost("{id}/Stop")]
|
||||||
public IActionResult StopJob(string id)
|
public IActionResult StopJob(string id)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return NotFound(new ProblemResponse("Not implemented")); //TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all configured Library-Connectors
|
/// Gets all configured Library-Connectors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of configured Library-Connectors</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType<LibraryConnector[]>(Status200OK)]
|
[ProducesResponseType<LibraryConnector[]>(Status200OK)]
|
||||||
public IActionResult GetAllConnectors()
|
public IActionResult GetAllConnectors()
|
||||||
@ -28,8 +28,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
|||||||
/// Returns Library-Connector with requested ID
|
/// Returns Library-Connector with requested ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Library-Connector-ID</param>
|
/// <param name="id">Library-Connector-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Library-Connector</returns>
|
||||||
/// <response code="404">Connector with ID not found.</response>
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType<LibraryConnector>(Status200OK)]
|
[ProducesResponseType<LibraryConnector>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
@ -47,8 +46,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
|||||||
/// Creates a new Library-Connector
|
/// Creates a new Library-Connector
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryConnector">Library-Connector</param>
|
/// <param name="libraryConnector">Library-Connector</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType(Status200OK)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
@ -70,9 +68,7 @@ public class LibraryConnectorController(PgsqlContext context) : Controller
|
|||||||
/// Deletes the Library-Connector with the requested ID
|
/// Deletes the Library-Connector with the requested ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Library-Connector-ID</param>
|
/// <param name="id">Library-Connector-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="404">Connector with ID not found.</response>
|
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
|
@ -1,79 +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 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>
|
/// <summary>
|
||||||
/// Returns all cached Manga
|
/// Returns all cached Manga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Manga</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||||
public IActionResult GetAllManga()
|
public IActionResult GetAllManga()
|
||||||
@ -27,7 +27,7 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// Returns all cached Manga with IDs
|
/// Returns all cached Manga with IDs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ids">Array of Manga-IDs</param>
|
/// <param name="ids">Array of Manga-IDs</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Manga</returns>
|
||||||
[HttpPost("WithIDs")]
|
[HttpPost("WithIDs")]
|
||||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||||
public IActionResult GetManga([FromBody]string[] ids)
|
public IActionResult GetManga([FromBody]string[] ids)
|
||||||
@ -40,41 +40,42 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// Return Manga with ID
|
/// Return Manga with ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Manga</returns>
|
||||||
/// <response code="404">Manga with ID not found</response>
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType<Manga>(Status200OK)]
|
[ProducesResponseType<Manga>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
public IActionResult GetManga(string id)
|
public IActionResult GetManga(string id)
|
||||||
{
|
{
|
||||||
Manga? ret = context.Manga.Find(id);
|
Manga? ret = context.Manga.Find(id);
|
||||||
if (ret is null)
|
return (ret is not null) switch
|
||||||
return NotFound();
|
{
|
||||||
return Ok(ret);
|
true => Ok(ret),
|
||||||
|
false => NotFound()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete Manga with ID
|
/// Delete Manga with ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="404">Manga with ID not found</response>
|
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType(Status500InternalServerError)]
|
||||||
public IActionResult DeleteManga(string id)
|
public IActionResult DeleteManga(string id)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Manga? ret = context.Manga.Find(id);
|
Manga? ret = context.Manga.Find(id);
|
||||||
if (ret is null)
|
switch (ret is not null)
|
||||||
return NotFound();
|
{
|
||||||
|
case true:
|
||||||
context.Remove(ret);
|
context.Remove(ret);
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
return Ok();
|
return Ok();
|
||||||
|
case false: return NotFound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -86,29 +87,28 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// Returns URL of Cover of Manga
|
/// Returns URL of Cover of Manga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
/// <returns>URL of Cover</returns>
|
||||||
[HttpGet("{id}/Cover")]
|
[HttpGet("{id}/Cover")]
|
||||||
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetCover(string id)
|
public IActionResult GetCover(string id)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all Chapters of Manga
|
/// Returns all Chapters of Manga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Chapters</returns>
|
||||||
/// <response code="404">Manga with ID not found</response>
|
|
||||||
[HttpGet("{id}/Chapters")]
|
[HttpGet("{id}/Chapters")]
|
||||||
[ProducesResponseType<Chapter[]>(Status200OK)]
|
[ProducesResponseType<Chapter[]>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType<string>(Status404NotFound)]
|
||||||
public IActionResult GetChapters(string id)
|
public IActionResult GetChapters(string id)
|
||||||
{
|
{
|
||||||
Manga? m = context.Manga.Find(id);
|
Manga? m = context.Manga.Find(id);
|
||||||
if (m is null)
|
if (m is null)
|
||||||
return NotFound();
|
return NotFound("Manga could not be found");
|
||||||
|
Chapter[] ret = context.Chapters.Where(c => c.ParentManga.MangaId == m.MangaId).ToArray();
|
||||||
Chapter[] ret = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToArray();
|
|
||||||
return Ok(ret);
|
return Ok(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,46 +116,35 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// Returns the latest Chapter of requested Manga
|
/// Returns the latest Chapter of requested Manga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Latest Chapter</returns>
|
||||||
/// <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")]
|
[HttpGet("{id}/Chapter/Latest")]
|
||||||
[ProducesResponseType<Chapter>(Status200OK)]
|
[ProducesResponseType<Chapter>(Status200OK)]
|
||||||
[ProducesResponseType(Status204NoContent)]
|
[ProducesResponseType<string>(Status404NotFound)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
|
||||||
public IActionResult GetLatestChapter(string id)
|
public IActionResult GetLatestChapter(string id)
|
||||||
{
|
{
|
||||||
Manga? m = context.Manga.Find(id);
|
Manga? m = context.Manga.Find(id);
|
||||||
if (m is null)
|
if (m is null)
|
||||||
return NotFound();
|
return NotFound("Manga could not be found");
|
||||||
|
List<Chapter> chapters = context.Chapters.Where(c => c.ParentManga.MangaId == m.MangaId).ToList();
|
||||||
List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToList();
|
|
||||||
if (chapters.Count == 0)
|
|
||||||
return NoContent();
|
|
||||||
|
|
||||||
Chapter? max = chapters.Max();
|
Chapter? max = chapters.Max();
|
||||||
if (max is null)
|
if (max is null)
|
||||||
return StatusCode(500, "Max chapter could not be found");
|
return NotFound("Chapter could not be found");
|
||||||
|
|
||||||
return Ok(max);
|
return Ok(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configure the cut-off for Manga
|
/// Configure the cut-off for Manga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>This is important for the DownloadNewChapters-Job</remarks>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="404">Manga with ID not found.</response>
|
|
||||||
[HttpPatch("{id}/IgnoreChaptersBefore")]
|
[HttpPatch("{id}/IgnoreChaptersBefore")]
|
||||||
[ProducesResponseType<float>(Status200OK)]
|
[ProducesResponseType<float>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult IgnoreChaptersBefore(string id)
|
public IActionResult IgnoreChaptersBefore(string id)
|
||||||
{
|
{
|
||||||
Manga? m = context.Manga.Find(id);
|
Manga? m = context.Manga.Find(id);
|
||||||
if (m is null)
|
if (m is null)
|
||||||
return NotFound();
|
return NotFound("Manga could not be found");
|
||||||
return Ok(m.IgnoreChapterBefore);
|
return Ok(m.IgnoreChapterBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,10 +153,11 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-ID</param>
|
/// <param name="id">Manga-ID</param>
|
||||||
/// <param name="folder">New Directory-Path</param>
|
/// <param name="folder">New Directory-Path</param>
|
||||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
/// <returns>Nothing</returns>
|
||||||
[HttpPost("{id}/MoveFolder")]
|
[HttpPost("{id}/MoveFolder")]
|
||||||
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult MoveFolder(string id, [FromBody]string folder)
|
public IActionResult MoveFolder(string id, [FromBody]string folder)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
26
API/Controllers/MiscController.cs
Normal file
26
API/Controllers/MiscController.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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>
|
/// <summary>
|
||||||
/// Gets all configured Notification-Connectors
|
/// Gets all configured Notification-Connectors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of configured Notification-Connectors</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType<NotificationConnector[]>(Status200OK)]
|
[ProducesResponseType<NotificationConnector[]>(Status200OK)]
|
||||||
public IActionResult GetAllConnectors()
|
public IActionResult GetAllConnectors()
|
||||||
@ -28,8 +28,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
|||||||
/// Returns Notification-Connector with requested ID
|
/// Returns Notification-Connector with requested ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Notification-Connector-ID</param>
|
/// <param name="id">Notification-Connector-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Notification-Connector</returns>
|
||||||
/// <response code="404">NotificationConnector with ID not found</response>
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType<NotificationConnector>(Status200OK)]
|
[ProducesResponseType<NotificationConnector>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
@ -47,8 +46,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
|||||||
/// Creates a new Notification-Connector
|
/// Creates a new Notification-Connector
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="notificationConnector">Notification-Connector</param>
|
/// <param name="notificationConnector">Notification-Connector</param>
|
||||||
/// <response code="201"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[ProducesResponseType<NotificationConnector[]>(Status200OK)]
|
[ProducesResponseType<NotificationConnector[]>(Status200OK)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
@ -70,9 +68,7 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
|||||||
/// Deletes the Notification-Connector with the requested ID
|
/// Deletes the Notification-Connector with the requested ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Notification-Connector-ID</param>
|
/// <param name="id">Notification-Connector-ID</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="404">NotificationConnector with ID not found</response>
|
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
@ -82,12 +78,14 @@ public class NotificationConnectorController(PgsqlContext context) : Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
NotificationConnector? ret = context.NotificationConnectors.Find(id);
|
NotificationConnector? ret = context.NotificationConnectors.Find(id);
|
||||||
if(ret is null)
|
switch (ret is not null)
|
||||||
return NotFound();
|
{
|
||||||
|
case true:
|
||||||
context.Remove(ret);
|
context.Remove(ret);
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
return Ok();
|
return Ok();
|
||||||
|
case false: return NotFound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -18,17 +18,14 @@ public class SearchController(PgsqlContext context) : Controller
|
|||||||
/// Initiate a search for a Manga on all Connectors
|
/// Initiate a search for a Manga on all Connectors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">Name/Title of the Manga</param>
|
/// <param name="name">Name/Title of the Manga</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Array of Manga</returns>
|
||||||
/// <response code="500">Error during Database Operation</response>
|
|
||||||
[HttpPost("{name}")]
|
[HttpPost("{name}")]
|
||||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
[ProducesResponseType<Manga[]>(Status500InternalServerError)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
|
||||||
public IActionResult SearchMangaGlobal(string name)
|
public IActionResult SearchMangaGlobal(string name)
|
||||||
{
|
{
|
||||||
List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> allManga = new();
|
List<(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)> allManga = new();
|
||||||
foreach (MangaConnector contextMangaConnector in context.MangaConnectors)
|
foreach (MangaConnector contextMangaConnector in context.MangaConnectors)
|
||||||
allManga.AddRange(contextMangaConnector.GetManga(name));
|
allManga.AddRange(contextMangaConnector.GetManga(name));
|
||||||
|
|
||||||
List<Manga> retMangas = new();
|
List<Manga> retMangas = new();
|
||||||
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in allManga)
|
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in allManga)
|
||||||
{
|
{
|
||||||
@ -38,9 +35,9 @@ public class SearchController(PgsqlContext context) : Controller
|
|||||||
if(add is not null)
|
if(add is not null)
|
||||||
retMangas.Add(add);
|
retMangas.Add(add);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (DbUpdateException)
|
||||||
{
|
{
|
||||||
return StatusCode(500, e);
|
return StatusCode(500, new ProblemResponse("An error occurred while processing your request."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(retMangas.ToArray());
|
return Ok(retMangas.ToArray());
|
||||||
@ -51,23 +48,16 @@ public class SearchController(PgsqlContext context) : Controller
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">Manga-Connector-ID</param>
|
/// <param name="id">Manga-Connector-ID</param>
|
||||||
/// <param name="name">Name/Title of the Manga</param>
|
/// <param name="name">Name/Title of the Manga</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Manga</returns>
|
||||||
/// <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}")]
|
[HttpPost("{id}/{name}")]
|
||||||
[ProducesResponseType<Manga[]>(Status200OK)]
|
[ProducesResponseType<Manga[]>(Status200OK)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType<ProblemResponse>(Status404NotFound)]
|
||||||
[ProducesResponseType(Status406NotAcceptable)]
|
[ProducesResponseType<ProblemResponse>(Status500InternalServerError)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
|
||||||
public IActionResult SearchManga(string id, string name)
|
public IActionResult SearchManga(string id, string name)
|
||||||
{
|
{
|
||||||
MangaConnector? connector = context.MangaConnectors.Find(id);
|
MangaConnector? connector = context.MangaConnectors.Find(id);
|
||||||
if (connector is null)
|
if (connector is null)
|
||||||
return NotFound();
|
return NotFound(new ProblemResponse("Connector not found."));
|
||||||
else if (connector.Enabled is false)
|
|
||||||
return StatusCode(406);
|
|
||||||
|
|
||||||
(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] mangas = connector.GetManga(name);
|
(Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)[] mangas = connector.GetManga(name);
|
||||||
List<Manga> retMangas = new();
|
List<Manga> retMangas = new();
|
||||||
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in mangas)
|
foreach ((Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links, List<MangaAltTitle>? altTitles) in mangas)
|
||||||
@ -78,15 +68,15 @@ public class SearchController(PgsqlContext context) : Controller
|
|||||||
if(add is not null)
|
if(add is not null)
|
||||||
retMangas.Add(add);
|
retMangas.Add(add);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (DbUpdateException e)
|
||||||
{
|
{
|
||||||
return StatusCode(500, e.Message);
|
return StatusCode(500, new ProblemResponse("An error occurred while processing your request.", e.Message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(retMangas.ToArray());
|
return Ok(retMangas.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Manga? AddMangaToContext(Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links,
|
private Manga? AddMangaToContext(Manga? manga, List<Author>? authors, List<MangaTag>? tags, List<Link>? links,
|
||||||
List<MangaAltTitle>? altTitles)
|
List<MangaAltTitle>? altTitles)
|
||||||
{
|
{
|
||||||
@ -154,7 +144,7 @@ public class SearchController(PgsqlContext context) : Controller
|
|||||||
context.Manga.Update(existing);
|
context.Manga.Update(existing);
|
||||||
else
|
else
|
||||||
context.Manga.Add(manga);
|
context.Manga.Add(manga);
|
||||||
|
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
return existing ?? manga;
|
return existing ?? manga;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using System.Text.Json.Nodes;
|
using API.Schema;
|
||||||
using API.MangaDownloadClients;
|
|
||||||
using API.Schema;
|
|
||||||
using Asp.Versioning;
|
using Asp.Versioning;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||||
@ -16,144 +14,136 @@ public class SettingsController(PgsqlContext context) : Controller
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get all Settings
|
/// Get all Settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType<JsonObject>(StatusCodes.Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetSettings()
|
public IActionResult GetSettings()
|
||||||
{
|
{
|
||||||
return Ok(TrangaSettings.Serialize());
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current UserAgent used by Tranga
|
/// Get the current UserAgent used by Tranga
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>UserAgent as string</returns>
|
||||||
[HttpGet("UserAgent")]
|
[HttpGet("UserAgent")]
|
||||||
[ProducesResponseType<string>(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetUserAgent()
|
public IActionResult GetUserAgent()
|
||||||
{
|
{
|
||||||
return Ok(TrangaSettings.userAgent);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set a new UserAgent
|
/// Set a new UserAgent
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
[HttpPatch("UserAgent")]
|
[HttpPatch("UserAgent")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult SetUserAgent([FromBody]string userAgent)
|
public IActionResult SetUserAgent()
|
||||||
{
|
{
|
||||||
TrangaSettings.UpdateUserAgent(userAgent);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reset the UserAgent to default
|
/// Reset the UserAgent to default
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
[HttpDelete("UserAgent")]
|
[HttpDelete("UserAgent")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult ResetUserAgent()
|
public IActionResult ResetUserAgent()
|
||||||
{
|
{
|
||||||
TrangaSettings.UpdateUserAgent(TrangaSettings.DefaultUserAgent);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get all Request-Limits
|
/// Get all Request-Limits
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns></returns>
|
||||||
[HttpGet("RequestLimits")]
|
[HttpGet("RequestLimits")]
|
||||||
[ProducesResponseType<Dictionary<RequestType,int>>(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetRequestLimits()
|
public IActionResult GetRequestLimits()
|
||||||
{
|
{
|
||||||
return Ok(TrangaSettings.requestLimits);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update all Request-Limits to new values
|
/// Update all Request-Limits to new values
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>NOT IMPLEMENTED</remarks>
|
/// <returns>Nothing</returns>
|
||||||
[HttpPatch("RequestLimits")]
|
[HttpPatch("RequestLimits")]
|
||||||
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult SetRequestLimits()
|
public IActionResult SetRequestLimits()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reset all Request-Limits
|
/// Reset all Request-Limits
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
[HttpDelete("RequestLimits")]
|
[HttpDelete("RequestLimits")]
|
||||||
[ProducesResponseType<string>(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult ResetRequestLimits()
|
public IActionResult ResetRequestLimits()
|
||||||
{
|
{
|
||||||
TrangaSettings.ResetRequestLimits();
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns Level of Image-Compression for Images
|
/// Returns Level of Image-Compression for Images
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200">JPEG compression-level as Integer</response>
|
/// <returns></returns>
|
||||||
[HttpGet("ImageCompression")]
|
[HttpGet("ImageCompression")]
|
||||||
[ProducesResponseType<int>(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetImageCompression()
|
public IActionResult GetImageCompression()
|
||||||
{
|
{
|
||||||
return Ok(TrangaSettings.compression);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the Image-Compression-Level for Images
|
/// Set the Image-Compression-Level for Images
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="level">100 to disable, 0-99 for JPEG compression-Level</param>
|
/// <param name="percentage">100 to disable, 0-99 for JPEG compression-Level</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
/// <response code="400">Level outside permitted range</response>
|
|
||||||
[HttpPatch("ImageCompression")]
|
[HttpPatch("ImageCompression")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
[ProducesResponseType(Status400BadRequest)]
|
public IActionResult SetImageCompression(int percentage)
|
||||||
public IActionResult SetImageCompression(int level)
|
|
||||||
{
|
{
|
||||||
if (level < 0 || level > 100)
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
return BadRequest();
|
|
||||||
TrangaSettings.UpdateCompressImages(level);
|
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get state of Black/White-Image setting
|
/// Get state of Black/White-Image setting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200">True if enabled</response>
|
/// <returns>True if enabled</returns>
|
||||||
[HttpGet("BWImages")]
|
[HttpGet("BWImages")]
|
||||||
[ProducesResponseType<bool>(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetBwImagesToggle()
|
public IActionResult GetBwImagesToggle()
|
||||||
{
|
{
|
||||||
return Ok(TrangaSettings.bwImages);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable/Disable conversion of Images to Black and White
|
/// Enable/Disable conversion of Images to Black and White
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="enabled">true to enable</param>
|
/// <param name="enabled">true to enable</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
[HttpPatch("BWImages")]
|
[HttpPatch("BWImages")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult SetBwImagesToggle(bool enabled)
|
public IActionResult SetBwImagesToggle(bool enabled)
|
||||||
{
|
{
|
||||||
TrangaSettings.UpdateBwImages(enabled);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get state of April Fools Mode
|
/// Get state of April Fools Mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
|
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
|
||||||
/// <response code="200">True if enabled</response>
|
/// <returns>True if enabled</returns>
|
||||||
[HttpGet("AprilFoolsMode")]
|
[HttpGet("AprilFoolsMode")]
|
||||||
[ProducesResponseType<bool>(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult GetAprilFoolsMode()
|
public IActionResult GetAprilFoolsMode()
|
||||||
{
|
{
|
||||||
return Ok(TrangaSettings.aprilFoolsMode);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -161,12 +151,11 @@ public class SettingsController(PgsqlContext context) : Controller
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
|
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
|
||||||
/// <param name="enabled">true to enable</param>
|
/// <param name="enabled">true to enable</param>
|
||||||
/// <response code="200"></response>
|
/// <returns>Nothing</returns>
|
||||||
[HttpPatch("AprilFoolsMode")]
|
[HttpPatch("AprilFoolsMode")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType<string>(Status500InternalServerError)]
|
||||||
public IActionResult SetAprilFoolsMode(bool enabled)
|
public IActionResult SetAprilFoolsMode(bool enabled)
|
||||||
{
|
{
|
||||||
TrangaSettings.UpdateAprilFoolsMode(enabled);
|
return StatusCode(500, "Not implemented"); //TODO
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
678
API/Migrations/20250307104111_dev-070325-1.Designer.cs
generated
678
API/Migrations/20250307104111_dev-070325-1.Designer.cs
generated
@ -1,678 +0,0 @@
|
|||||||
// <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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
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
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "9.0.2")
|
.HasAnnotation("ProductVersion", "9.0.0")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@ -86,9 +86,6 @@ namespace API.Migrations
|
|||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
.HasColumnType("text[]");
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
b.Property<bool>("Enabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("JobId1")
|
b.Property<string>("JobId1")
|
||||||
.HasColumnType("character varying(64)");
|
.HasColumnType("character varying(64)");
|
||||||
|
|
||||||
@ -263,9 +260,6 @@ namespace API.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text[]");
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
b.Property<bool>("Enabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("SupportedLanguages")
|
b.PrimitiveCollection<string[]>("SupportedLanguages")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text[]");
|
.HasColumnType("text[]");
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
namespace API;
|
|
||||||
|
|
||||||
public record ModifyJobRecord(ulong? RecurrenceMs, bool? Enabled);
|
|
3
API/ProblemResponse.cs
Normal file
3
API/ProblemResponse.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace API;
|
||||||
|
|
||||||
|
public record ProblemResponse(string title, string? message = null);
|
@ -84,7 +84,7 @@ public class Chapter : IComparable<Chapter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates full file path of chapter-archive
|
/// Creates full file path of chapter-archive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Filepath</returns>
|
/// <returns>Filepath</returns>
|
||||||
internal string GetArchiveFilePath()
|
internal string GetArchiveFilePath()
|
||||||
@ -92,10 +92,6 @@ public class Chapter : IComparable<Chapter>
|
|||||||
return Path.Join(TrangaSettings.downloadLocation, ParentManga.FolderName, ArchiveFileName);
|
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()
|
public bool IsDownloaded()
|
||||||
{
|
{
|
||||||
string path = GetArchiveFilePath();
|
string path = GetArchiveFilePath();
|
||||||
|
@ -20,9 +20,15 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
|
|
||||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||||
{
|
{
|
||||||
Chapter chapter = Chapter ?? context.Chapters.Find(ChapterId)!;
|
Chapter c = Chapter ?? context.Chapters.Find(ChapterId)!;
|
||||||
Manga manga = chapter.ParentManga ?? context.Manga.Find(chapter.ParentMangaId)!;
|
Manga m = c.ParentManga ?? context.Manga.Find(c.ParentMangaId)!;
|
||||||
MangaConnector connector = manga.MangaConnector ?? context.MangaConnectors.Find(manga.MangaConnectorId)!;
|
MangaConnector connector = m.MangaConnector ?? context.MangaConnectors.Find(m.MangaConnectorId)!;
|
||||||
|
DownloadChapterImages(c, connector, m);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DownloadChapterImages(Chapter chapter, MangaConnector connector, Manga manga)
|
||||||
|
{
|
||||||
string[] imageUrls = connector.GetChapterImageUrls(chapter);
|
string[] imageUrls = connector.GetChapterImageUrls(chapter);
|
||||||
string saveArchiveFilePath = chapter.GetArchiveFilePath();
|
string saveArchiveFilePath = chapter.GetArchiveFilePath();
|
||||||
|
|
||||||
@ -46,7 +52,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
if (imageUrls.Length == 0)
|
if (imageUrls.Length == 0)
|
||||||
{
|
{
|
||||||
Directory.Delete(tempFolder, true);
|
Directory.Delete(tempFolder, true);
|
||||||
return [];
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string imageUrl in imageUrls)
|
foreach (string imageUrl in imageUrls)
|
||||||
@ -55,10 +61,10 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
string imagePath = Path.Join(tempFolder, $"{chapterNum++}.{extension}");
|
string imagePath = Path.Join(tempFolder, $"{chapterNum++}.{extension}");
|
||||||
bool status = DownloadImage(imageUrl, imagePath);
|
bool status = DownloadImage(imageUrl, imagePath);
|
||||||
if (status is false)
|
if (status is false)
|
||||||
return [];
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyCoverFromCacheToDownloadLocation(context, manga);
|
CopyCoverFromCacheToDownloadLocation(manga);
|
||||||
|
|
||||||
File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString());
|
File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString());
|
||||||
|
|
||||||
@ -68,10 +74,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
File.SetUnixFileMode(saveArchiveFilePath, UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute | OtherRead | OtherExecute);
|
File.SetUnixFileMode(saveArchiveFilePath, UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute | OtherRead | OtherExecute);
|
||||||
Directory.Delete(tempFolder, true); //Cleanup
|
Directory.Delete(tempFolder, true); //Cleanup
|
||||||
|
|
||||||
chapter.Downloaded = true;
|
return true;
|
||||||
context.SaveChanges();
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessImage(string imagePath)
|
private void ProcessImage(string imagePath)
|
||||||
@ -89,7 +92,7 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyCoverFromCacheToDownloadLocation(PgsqlContext context, Manga manga, int? retries = 1)
|
private void CopyCoverFromCacheToDownloadLocation(Manga manga, int? retries = 1)
|
||||||
{
|
{
|
||||||
//Check if Publication already has a Folder and cover
|
//Check if Publication already has a Folder and cover
|
||||||
string publicationFolder = manga.CreatePublicationFolder();
|
string publicationFolder = manga.CreatePublicationFolder();
|
||||||
@ -104,9 +107,8 @@ public class DownloadSingleChapterJob(string chapterId, string? parentJobId = nu
|
|||||||
{
|
{
|
||||||
if (retries > 0)
|
if (retries > 0)
|
||||||
{
|
{
|
||||||
manga.CoverFileNameInCache = manga.SaveCoverImageToCache();
|
manga.SaveCoverImageToCache();
|
||||||
context.SaveChanges();
|
CopyCoverFromCacheToDownloadLocation(manga, --retries);
|
||||||
CopyCoverFromCacheToDownloadLocation(context, manga, --retries);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -26,7 +26,6 @@ public abstract class Job
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime NextExecution => LastExecution.AddMilliseconds(RecurrenceMs);
|
public DateTime NextExecution => LastExecution.AddMilliseconds(RecurrenceMs);
|
||||||
public JobState state { get; internal set; } = JobState.Waiting;
|
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)
|
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())
|
: this(jobId, jobType, recurrenceMs, parentJob?.JobId, dependsOnJobs?.Select(j => j.JobId).ToList())
|
||||||
|
@ -8,24 +8,10 @@ public class UpdateMetadataJob(ulong recurrenceMs, string mangaId, string? paren
|
|||||||
{
|
{
|
||||||
[MaxLength(64)]
|
[MaxLength(64)]
|
||||||
public string MangaId { get; init; } = mangaId;
|
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)
|
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||||
{
|
{
|
||||||
//Manga manga = Manga ?? context.Manga.Find(MangaId)!;
|
throw new NotImplementedException();
|
||||||
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,8 +14,6 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s
|
|||||||
public string[] SupportedLanguages { get; init; } = supportedLanguages;
|
public string[] SupportedLanguages { get; init; } = supportedLanguages;
|
||||||
public string[] BaseUris { get; init; } = baseUris;
|
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>?)[] GetManga(string publicationTitle = "");
|
||||||
|
|
||||||
public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url);
|
public abstract (Manga, List<Author>?, List<MangaTag>?, List<Link>?, List<MangaAltTitle>?)? GetMangaFromUrl(string url);
|
||||||
|
@ -69,21 +69,18 @@ public static class Tranga
|
|||||||
Log.Info(TRANGA);
|
Log.Info(TRANGA);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
List<Job> completedJobs = context.Jobs.Where(j => j.state >= JobState.Completed).ToList();
|
List<Job> completedJobs = context.Jobs.Where(j => j.state >= JobState.Completed && j.state < JobState.Failed).ToList();
|
||||||
foreach (Job job in completedJobs)
|
foreach (Job job in completedJobs)
|
||||||
if (job.RecurrenceMs <= 0)
|
if (job.RecurrenceMs <= 0)
|
||||||
context.Jobs.Remove(job);
|
context.Jobs.Remove(job);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (job.state >= JobState.Failed)
|
|
||||||
job.Enabled = false;
|
|
||||||
else
|
|
||||||
job.state = JobState.Waiting;
|
|
||||||
job.LastExecution = DateTime.UtcNow;
|
job.LastExecution = DateTime.UtcNow;
|
||||||
|
job.state = JobState.Waiting;
|
||||||
context.Jobs.Update(job);
|
context.Jobs.Update(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Job> runJobs = context.Jobs.Where(j => j.state <= JobState.Running && j.Enabled == true).ToList()
|
List<Job> runJobs = context.Jobs.Where(j => j.state <= JobState.Running).ToList()
|
||||||
.Where(j => j.NextExecution < DateTime.UtcNow).ToList();
|
.Where(j => j.NextExecution < DateTime.UtcNow).ToList();
|
||||||
foreach (Job job in runJobs)
|
foreach (Job job in runJobs)
|
||||||
{
|
{
|
||||||
|
@ -118,7 +118,7 @@ public static class TrangaSettings
|
|||||||
ExportSettings();
|
ExportSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ResetRequestLimits()
|
public static void ResetRateLimits()
|
||||||
{
|
{
|
||||||
requestLimits = DefaultRequestLimits;
|
requestLimits = DefaultRequestLimits;
|
||||||
ExportSettings();
|
ExportSettings();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user