mirror of
https://github.com/C9Glax/tranga.git
synced 2025-05-07 15:42:10 +02:00
Compare commits
5 Commits
31c039d71e
...
cef3b24efd
Author | SHA1 | Date | |
---|---|---|---|
cef3b24efd | |||
f10c478cab | |||
01ba927491 | |||
90ce1395b8 | |||
892ef6c9d6 |
@ -96,26 +96,29 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// <response code="204">Cover not loaded</response>
|
/// <response code="204">Cover not loaded</response>
|
||||||
/// <response code="400">The formatting-request was invalid</response>
|
/// <response code="400">The formatting-request was invalid</response>
|
||||||
/// <response code="404">Manga with ID not found</response>
|
/// <response code="404">Manga with ID not found</response>
|
||||||
|
/// <response code="503">Retry later, downloading cover</response>
|
||||||
[HttpGet("{MangaId}/Cover")]
|
[HttpGet("{MangaId}/Cover")]
|
||||||
[ProducesResponseType<byte[]>(Status200OK,"image/jpeg")]
|
[ProducesResponseType<byte[]>(Status200OK,"image/jpeg")]
|
||||||
[ProducesResponseType(Status204NoContent)]
|
[ProducesResponseType(Status204NoContent)]
|
||||||
[ProducesResponseType(Status400BadRequest)]
|
[ProducesResponseType(Status400BadRequest)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
|
[ProducesResponseType<int>(Status503ServiceUnavailable, "text/plain")]
|
||||||
public IActionResult GetCover(string MangaId, [FromQuery]int? width, [FromQuery]int? height)
|
public IActionResult GetCover(string MangaId, [FromQuery]int? width, [FromQuery]int? height)
|
||||||
{
|
{
|
||||||
|
DateTime requestStarted = HttpContext.Features.Get<IHttpRequestTimeFeature>()?.RequestTime ?? DateTime.Now;
|
||||||
Manga? m = context.Mangas.Find(MangaId);
|
Manga? m = context.Mangas.Find(MangaId);
|
||||||
if (m is null)
|
if (m is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
if (!System.IO.File.Exists(m.CoverFileNameInCache))
|
if (!System.IO.File.Exists(m.CoverFileNameInCache))
|
||||||
{
|
{
|
||||||
bool coverIsBeingDownloaded = false;
|
List<Job> coverDownloadJobs = context.Jobs.Where(j => j.JobType == JobType.DownloadMangaCoverJob).ToList();
|
||||||
do
|
if (coverDownloadJobs.Any(j => j is DownloadMangaCoverJob dmc && dmc.MangaId == MangaId))
|
||||||
{
|
{
|
||||||
coverIsBeingDownloaded = context.Jobs.Where(j => j.JobType == JobType.DownloadMangaCoverJob).AsEnumerable()
|
Response.Headers.Add("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * coverDownloadJobs.Count() * 2 / 1000:D}");
|
||||||
.Any(j => j is DownloadMangaCoverJob dmcj && dmcj.MangaId == MangaId);
|
return StatusCode(Status503ServiceUnavailable, TrangaSettings.startNewJobTimeoutMs * coverDownloadJobs.Count() * 2 / 1000);
|
||||||
Thread.Sleep(100);
|
}
|
||||||
} while (coverIsBeingDownloaded);
|
else
|
||||||
if (!System.IO.File.Exists(m.CoverFileNameInCache))
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,11 +215,13 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// <response code="204">No available chapters</response>
|
/// <response code="204">No available chapters</response>
|
||||||
/// <response code="404">Manga with ID not found.</response>
|
/// <response code="404">Manga with ID not found.</response>
|
||||||
/// <response code="500">Could not retrieve the maximum chapter-number</response>
|
/// <response code="500">Could not retrieve the maximum chapter-number</response>
|
||||||
|
/// <response code="503">Retry after timeout, updating value</response>
|
||||||
[HttpGet("{MangaId}/Chapter/LatestAvailable")]
|
[HttpGet("{MangaId}/Chapter/LatestAvailable")]
|
||||||
[ProducesResponseType<Chapter>(Status200OK, "application/json")]
|
[ProducesResponseType<Chapter>(Status200OK, "application/json")]
|
||||||
[ProducesResponseType(Status204NoContent)]
|
[ProducesResponseType(Status204NoContent)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType<string>(Status404NotFound, "text/plain")]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||||
|
[ProducesResponseType<int>(Status503ServiceUnavailable, "text/plain")]
|
||||||
public IActionResult GetLatestChapter(string MangaId)
|
public IActionResult GetLatestChapter(string MangaId)
|
||||||
{
|
{
|
||||||
Manga? m = context.Mangas.Find(MangaId);
|
Manga? m = context.Mangas.Find(MangaId);
|
||||||
@ -225,7 +230,15 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
|
|
||||||
List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToList();
|
List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToList();
|
||||||
if (chapters.Count == 0)
|
if (chapters.Count == 0)
|
||||||
return NoContent();
|
{
|
||||||
|
List<Job> retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).ToList();
|
||||||
|
if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId))
|
||||||
|
{
|
||||||
|
Response.Headers.Add("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
|
||||||
|
return StatusCode(Status503ServiceUnavailable, TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2/ 1000);
|
||||||
|
}else
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
Chapter? max = chapters.Max();
|
Chapter? max = chapters.Max();
|
||||||
if (max is null)
|
if (max is null)
|
||||||
@ -242,20 +255,31 @@ public class MangaController(PgsqlContext context) : Controller
|
|||||||
/// <response code="204">No available chapters</response>
|
/// <response code="204">No available chapters</response>
|
||||||
/// <response code="404">Manga with ID not found.</response>
|
/// <response code="404">Manga with ID not found.</response>
|
||||||
/// <response code="500">Could not retrieve the maximum chapter-number</response>
|
/// <response code="500">Could not retrieve the maximum chapter-number</response>
|
||||||
|
/// <response code="503">Retry after timeout, updating value</response>
|
||||||
[HttpGet("{MangaId}/Chapter/LatestDownloaded")]
|
[HttpGet("{MangaId}/Chapter/LatestDownloaded")]
|
||||||
[ProducesResponseType<Chapter>(Status200OK, "application/json")]
|
[ProducesResponseType<Chapter>(Status200OK, "application/json")]
|
||||||
[ProducesResponseType(Status204NoContent)]
|
[ProducesResponseType(Status204NoContent)]
|
||||||
[ProducesResponseType(Status404NotFound)]
|
[ProducesResponseType(Status404NotFound)]
|
||||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||||
|
[ProducesResponseType<int>(Status503ServiceUnavailable, "text/plain")]
|
||||||
public IActionResult GetLatestChapterDownloaded(string MangaId)
|
public IActionResult GetLatestChapterDownloaded(string MangaId)
|
||||||
{
|
{
|
||||||
Manga? m = context.Mangas.Find(MangaId);
|
Manga? m = context.Mangas.Find(MangaId);
|
||||||
if (m is null)
|
if (m is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
|
|
||||||
List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId && c.Downloaded == true).ToList();
|
List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId && c.Downloaded == true).ToList();
|
||||||
if (chapters.Count == 0)
|
if (chapters.Count == 0)
|
||||||
return NoContent();
|
{
|
||||||
|
List<Job> retrieveChapterJobs = context.Jobs.Where(j => j.JobType == JobType.RetrieveChaptersJob).ToList();
|
||||||
|
if (retrieveChapterJobs.Any(j => j is RetrieveChaptersJob rcj && rcj.MangaId == MangaId))
|
||||||
|
{
|
||||||
|
Response.Headers.Add("Retry-After", $"{TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000:D}");
|
||||||
|
return StatusCode(Status503ServiceUnavailable, TrangaSettings.startNewJobTimeoutMs * retrieveChapterJobs.Count() * 2 / 1000);
|
||||||
|
}else
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
Chapter? max = chapters.Max();
|
Chapter? max = chapters.Max();
|
||||||
if (max is null)
|
if (max is null)
|
||||||
|
35
API/HttpRequestTimeFeature.cs
Normal file
35
API/HttpRequestTimeFeature.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
namespace API;
|
||||||
|
|
||||||
|
public interface IHttpRequestTimeFeature
|
||||||
|
{
|
||||||
|
DateTime RequestTime { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HttpRequestTimeFeature : IHttpRequestTimeFeature
|
||||||
|
{
|
||||||
|
public DateTime RequestTime { get; }
|
||||||
|
|
||||||
|
public HttpRequestTimeFeature()
|
||||||
|
{
|
||||||
|
RequestTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RequestTimeMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public RequestTimeMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var httpRequestTimeFeature = new HttpRequestTimeFeature();
|
||||||
|
context.Features.Set<IHttpRequestTimeFeature>(httpRequestTimeFeature);
|
||||||
|
|
||||||
|
// Call the next delegate/middleware in the pipeline
|
||||||
|
return this._next(context);
|
||||||
|
}
|
||||||
|
}
|
@ -95,6 +95,8 @@ app.UseSwaggerUI(options =>
|
|||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
//app.UseMiddleware<RequestTimeMiddleware>();
|
||||||
|
|
||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var db = scope.ServiceProvider.GetRequiredService<PgsqlContext>();
|
var db = scope.ServiceProvider.GetRequiredService<PgsqlContext>();
|
||||||
|
@ -14,6 +14,7 @@ public class Chapter : IComparable<Chapter>
|
|||||||
: this(parentManga.MangaId, url, chapterNumber, volumeNumber, title)
|
: this(parentManga.MangaId, url, chapterNumber, volumeNumber, title)
|
||||||
{
|
{
|
||||||
ParentManga = parentManga;
|
ParentManga = parentManga;
|
||||||
|
FileName = GetArchiveFilePath(parentManga.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Chapter(string parentMangaId, string url, string chapterNumber,
|
public Chapter(string parentMangaId, string url, string chapterNumber,
|
||||||
@ -25,7 +26,6 @@ public class Chapter : IComparable<Chapter>
|
|||||||
ChapterNumber = chapterNumber;
|
ChapterNumber = chapterNumber;
|
||||||
VolumeNumber = volumeNumber;
|
VolumeNumber = volumeNumber;
|
||||||
Title = title;
|
Title = title;
|
||||||
FileName = GetArchiveFilePath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StringLength(64)]
|
[StringLength(64)]
|
||||||
@ -106,9 +106,9 @@ public class Chapter : IComparable<Chapter>
|
|||||||
return File.Exists(path);
|
return File.Exists(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetArchiveFilePath()
|
private string GetArchiveFilePath(string? parentMangaName = null)
|
||||||
{
|
{
|
||||||
return $"{ParentManga!.Name} - Vol.{VolumeNumber ?? 0} Ch.{ChapterNumber}{(Title is null ? "" : $" - {Title}")}.cbz";
|
return $"{parentMangaName ?? ParentManga?.Name ?? ""} - Vol.{VolumeNumber ?? 0} Ch.{ChapterNumber}{(Title is null ? "" : $" - {Title}")}.cbz";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CompareChapterNumbers(string ch1, string ch2)
|
private static int CompareChapterNumbers(string ch1, string ch2)
|
||||||
|
@ -141,6 +141,8 @@ public static class Tranga
|
|||||||
jobsByType[job.JobType].Add(job);
|
jobsByType[job.JobType].Add(job);
|
||||||
|
|
||||||
IEnumerable<Job> ret = new List<Job>();
|
IEnumerable<Job> ret = new List<Job>();
|
||||||
|
if(jobsByType.ContainsKey(JobType.MoveMangaLibraryJob))
|
||||||
|
ret = ret.Concat(jobsByType[JobType.MoveMangaLibraryJob]);
|
||||||
if(jobsByType.ContainsKey(JobType.MoveFileOrFolderJob))
|
if(jobsByType.ContainsKey(JobType.MoveFileOrFolderJob))
|
||||||
ret = ret.Concat(jobsByType[JobType.MoveFileOrFolderJob]);
|
ret = ret.Concat(jobsByType[JobType.MoveFileOrFolderJob]);
|
||||||
if(jobsByType.ContainsKey(JobType.DownloadMangaCoverJob))
|
if(jobsByType.ContainsKey(JobType.DownloadMangaCoverJob))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user