mirror of
https://github.com/C9Glax/tranga.git
synced 2025-04-14 04:13:18 +02:00
Add NewtonsoftJson to Swagger
Add RetrieveChaptersJob.cs Add UpdateFilesDownloadedJob.cs Remove DownloadNewChaptersJob.cs and instead use DownloadAvailableChaptersJob.cs
This commit is contained in:
parent
ecfc8f349b
commit
ffc0e7555a
@ -27,6 +27,7 @@
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="Soenneker.Utils.String.NeedlemanWunsch" Version="3.0.920" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="7.3.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -83,19 +83,24 @@ public class JobController(PgsqlContext context) : Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new CreateNewDownloadChapterJob
|
||||
/// Create a new DownloadAvailableChaptersJob
|
||||
/// </summary>
|
||||
/// <param name="MangaId">ID of Manga</param>
|
||||
/// <param name="recurrenceTime">How often should we check for new chapters</param>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="404">Could not find Manga with ID</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("NewDownloadChapterJob/{MangaId}")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[HttpPut("DownloadAvailableChaptersJob/{MangaId}")]
|
||||
[ProducesResponseType<string[]>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateNewDownloadChapterJob(string MangaId, [FromBody]ulong recurrenceTime)
|
||||
{
|
||||
Job job = new DownloadNewChaptersJob(recurrenceTime, MangaId);
|
||||
return AddJob(job);
|
||||
if (context.Manga.Find(MangaId) is null)
|
||||
return NotFound();
|
||||
Job dep = new RetrieveChaptersJob(recurrenceTime, MangaId);
|
||||
Job job = new DownloadAvailableChaptersJob(recurrenceTime, MangaId, null, [dep.JobId]);
|
||||
return AddJobs([dep, job]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -103,29 +108,37 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// </summary>
|
||||
/// <param name="ChapterId">ID of the Chapter</param>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="404">Could not find Chapter with ID</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("DownloadSingleChapterJob/{ChapterId}")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string[]>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateNewDownloadChapterJob(string ChapterId)
|
||||
{
|
||||
if(context.Chapters.Find(ChapterId) is null)
|
||||
return NotFound();
|
||||
Job job = new DownloadSingleChapterJob(ChapterId);
|
||||
return AddJob(job);
|
||||
return AddJobs([job]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new UpdateMetadataJob
|
||||
/// Create a new UpdateFilesDownloadedJob
|
||||
/// </summary>
|
||||
/// <param name="MangaId">ID of the Manga</param>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="201">Could not find Manga with ID</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("UpdateMetadataJob/{MangaId}")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[HttpPut("UpdateFilesJob/{MangaId}")]
|
||||
[ProducesResponseType<string[]>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateUpdateMetadataJob(string MangaId)
|
||||
public IActionResult CreateUpdateFilesDownloadedJob(string MangaId)
|
||||
{
|
||||
Job job = new UpdateMetadataJob(0, MangaId);
|
||||
return AddJob(job);
|
||||
if(context.Manga.Find(MangaId) is null)
|
||||
return NotFound();
|
||||
Job job = new UpdateFilesDownloadedJob(0, MangaId);
|
||||
return AddJobs([job]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -133,7 +146,50 @@ public class JobController(PgsqlContext context) : Controller
|
||||
/// </summary>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("UpdateMetadataJob")]
|
||||
[HttpPut("UpdateAllFilesJob")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateUpdateAllFilesDownloadedJob()
|
||||
{
|
||||
List<string> ids = context.Manga.Select(m => m.MangaId).ToList();
|
||||
List<UpdateFilesDownloadedJob> jobs = ids.Select(id => new UpdateFilesDownloadedJob(0, id)).ToList();
|
||||
try
|
||||
{
|
||||
context.Jobs.AddRange(jobs);
|
||||
context.SaveChanges();
|
||||
return Created();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new UpdateMetadataJob
|
||||
/// </summary>
|
||||
/// <param name="MangaId">ID of the Manga</param>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="404">Could not find Manga with ID</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("UpdateMetadataJob/{MangaId}")]
|
||||
[ProducesResponseType<string[]>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateUpdateMetadataJob(string MangaId)
|
||||
{
|
||||
if(context.Manga.Find(MangaId) is null)
|
||||
return NotFound();
|
||||
Job job = new UpdateMetadataJob(0, MangaId);
|
||||
return AddJobs([job]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new UpdateMetadataJob for all Manga
|
||||
/// </summary>
|
||||
/// <response code="201">Created new Job</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("UpdateAllMetadataJob")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateUpdateAllMetadataJob()
|
||||
@ -152,13 +208,13 @@ public class JobController(PgsqlContext context) : Controller
|
||||
}
|
||||
}
|
||||
|
||||
private IActionResult AddJob(Job job)
|
||||
private IActionResult AddJobs(Job[] jobs)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.Jobs.Add(job);
|
||||
context.Jobs.AddRange(jobs);
|
||||
context.SaveChanges();
|
||||
return new CreatedResult(job.JobId, job);
|
||||
return new CreatedResult((string?)null, jobs.Select(j => j.JobId).ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -142,8 +142,8 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
MangaTag? inDb = context.Tags.FirstOrDefault(t => t.Equals(mt));
|
||||
return inDb ?? mt;
|
||||
});
|
||||
manga.Tags = mergedTags.ToList();
|
||||
IEnumerable<MangaTag> newTags = manga.Tags.Where(mt => !context.Tags.Any(t => t.Tag.Equals(mt.Tag)));
|
||||
manga.MangaTags = mergedTags.ToList();
|
||||
IEnumerable<MangaTag> newTags = manga.MangaTags.Where(mt => !context.Tags.Any(t => t.Tag.Equals(mt.Tag)));
|
||||
context.Tags.AddRange(newTags);
|
||||
}
|
||||
|
||||
@ -195,6 +195,7 @@ public class SearchController(PgsqlContext context) : Controller
|
||||
context.Manga.Add(manga);
|
||||
|
||||
context.Jobs.Add(new DownloadMangaCoverJob(manga.MangaId));
|
||||
context.Jobs.Add(new RetrieveChaptersJob(0, manga.MangaId));
|
||||
|
||||
context.SaveChanges();
|
||||
return existing ?? manga;
|
||||
|
@ -367,17 +367,17 @@ namespace API.Migrations
|
||||
b.Property<string>("MangaId")
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("TagsTag")
|
||||
b.Property<string>("MangaTagsTag")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("MangaId", "TagsTag");
|
||||
b.HasKey("MangaId", "MangaTagsTag");
|
||||
|
||||
b.HasIndex("TagsTag");
|
||||
b.HasIndex("MangaTagsTag");
|
||||
|
||||
b.ToTable("MangaMangaTag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadAvailableChaptersJob", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.Jobs.Job");
|
||||
|
||||
@ -391,13 +391,13 @@ namespace API.Migrations
|
||||
b.ToTable("Jobs", t =>
|
||||
{
|
||||
t.Property("MangaId")
|
||||
.HasColumnName("DownloadMangaCoverJob_MangaId");
|
||||
.HasColumnName("DownloadAvailableChaptersJob_MangaId");
|
||||
});
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)4);
|
||||
b.HasDiscriminator().HasValue((byte)1);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadNewChaptersJob", b =>
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.Jobs.Job");
|
||||
|
||||
@ -408,7 +408,7 @@ namespace API.Migrations
|
||||
|
||||
b.HasIndex("MangaId");
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)1);
|
||||
b.HasDiscriminator().HasValue((byte)4);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadSingleChapterJob", b =>
|
||||
@ -440,6 +440,46 @@ namespace API.Migrations
|
||||
b.HasDiscriminator().HasValue((byte)3);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", 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("RetrieveChaptersJob_MangaId");
|
||||
});
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)5);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.UpdateFilesDownloadedJob", 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("UpdateFilesDownloadedJob_MangaId");
|
||||
});
|
||||
|
||||
b.HasDiscriminator().HasValue((byte)6);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
|
||||
{
|
||||
b.HasBaseType("API.Schema.Jobs.Job");
|
||||
@ -604,12 +644,12 @@ namespace API.Migrations
|
||||
|
||||
b.HasOne("API.Schema.MangaTag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsTag")
|
||||
.HasForeignKey("MangaTagsTag")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadAvailableChaptersJob", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "Manga")
|
||||
.WithMany()
|
||||
@ -620,7 +660,7 @@ namespace API.Migrations
|
||||
b.Navigation("Manga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadNewChaptersJob", b =>
|
||||
modelBuilder.Entity("API.Schema.Jobs.DownloadMangaCoverJob", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "Manga")
|
||||
.WithMany()
|
||||
@ -642,6 +682,28 @@ namespace API.Migrations
|
||||
b.Navigation("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.RetrieveChaptersJob", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "Manga")
|
||||
.WithMany()
|
||||
.HasForeignKey("MangaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Manga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.UpdateFilesDownloadedJob", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "Manga")
|
||||
.WithMany()
|
||||
.HasForeignKey("MangaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Manga");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Schema.Jobs.UpdateMetadataJob", b =>
|
||||
{
|
||||
b.HasOne("API.Schema.Manga", "Manga")
|
||||
|
@ -8,6 +8,7 @@ using Asp.Versioning;
|
||||
using Asp.Versioning.Builder;
|
||||
using Asp.Versioning.Conventions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
@ -25,25 +26,27 @@ builder.Services.AddCors(options =>
|
||||
});
|
||||
|
||||
builder.Services.AddApiVersioning(option =>
|
||||
{
|
||||
option.AssumeDefaultVersionWhenUnspecified = true;
|
||||
option.DefaultApiVersion = new ApiVersion(2);
|
||||
option.ReportApiVersions = true;
|
||||
option.ApiVersionReader = ApiVersionReader.Combine(
|
||||
new UrlSegmentApiVersionReader(),
|
||||
new QueryStringApiVersionReader("api-version"),
|
||||
new HeaderApiVersionReader("X-Version"),
|
||||
new MediaTypeApiVersionReader("x-version"));
|
||||
})
|
||||
.AddMvc(options =>
|
||||
{
|
||||
options.Conventions.Add(new VersionByNamespaceConvention());
|
||||
})
|
||||
.AddApiExplorer(options => {
|
||||
options.GroupNameFormat = "'v'V";
|
||||
options.SubstituteApiVersionInUrl = true;
|
||||
});
|
||||
{
|
||||
option.AssumeDefaultVersionWhenUnspecified = true;
|
||||
option.DefaultApiVersion = new ApiVersion(2);
|
||||
option.ReportApiVersions = true;
|
||||
option.ApiVersionReader = ApiVersionReader.Combine(
|
||||
new UrlSegmentApiVersionReader(),
|
||||
new QueryStringApiVersionReader("api-version"),
|
||||
new HeaderApiVersionReader("X-Version"),
|
||||
new MediaTypeApiVersionReader("x-version"));
|
||||
})
|
||||
.AddMvc(options =>
|
||||
{
|
||||
options.Conventions.Add(new VersionByNamespaceConvention());
|
||||
})
|
||||
.AddApiExplorer(options =>
|
||||
{
|
||||
options.GroupNameFormat = "'v'V";
|
||||
options.SubstituteApiVersionInUrl = true;
|
||||
});
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGenNewtonsoftSupport();
|
||||
builder.Services.AddSwaggerGen(opt =>
|
||||
{
|
||||
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||
@ -58,12 +61,13 @@ builder.Services.AddDbContext<PgsqlContext>(options =>
|
||||
$"Password={Environment.GetEnvironmentVariable("POSTGRES_PASSWORD")??"postgres"}"));
|
||||
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
options.AllowEmptyInputInBodyModelBinding = true;
|
||||
})
|
||||
.AddNewtonsoftJson(opts =>
|
||||
{
|
||||
options.AllowEmptyInputInBodyModelBinding = true;
|
||||
});
|
||||
builder.Services.AddControllers().AddNewtonsoftJson(opts =>
|
||||
{
|
||||
opts.SerializerSettings.Converters.Add(new StringEnumConverter());
|
||||
opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
});
|
||||
|
||||
builder.WebHost.UseUrls("http://*:6531");
|
||||
@ -115,9 +119,7 @@ using (var scope = app.Services.CreateScope())
|
||||
MangaConnector[] newConnectors = connectors.Where(c => !context.MangaConnectors.Contains(c)).ToArray();
|
||||
context.MangaConnectors.AddRange(newConnectors);
|
||||
|
||||
IQueryable<string> updateMetadataJobMangaIds = context.Jobs.Where(j => j.JobType == JobType.UpdateMetaDataJob).Select(j => ((UpdateMetadataJob)j).MangaId);
|
||||
Job[] newUpdateMetadataJobs = context.Manga.Where(m => !updateMetadataJobMangaIds.Contains(m.MangaId)).ToList().Select(m => new UpdateMetadataJob(0, m.MangaId)).ToArray();
|
||||
context.Jobs.AddRange(newUpdateMetadataJobs);
|
||||
context.Jobs.AddRange(context.Manga.AsEnumerable().Select(m => new UpdateFilesDownloadedJob(0, m.MangaId)));
|
||||
|
||||
context.Jobs.RemoveRange(context.Jobs.Where(j => j.state == JobState.Completed && j.RecurrenceMs < 1));
|
||||
|
||||
|
@ -39,8 +39,7 @@ public class Chapter : IComparable<Chapter>
|
||||
public bool Downloaded { get; internal set; } = false;
|
||||
|
||||
public string ParentMangaId { get; internal set; }
|
||||
[JsonIgnore]
|
||||
public Manga? ParentManga { get; init; }
|
||||
[JsonIgnore] public Manga? ParentManga { get; init; }
|
||||
|
||||
public int CompareTo(Chapter? other)
|
||||
{
|
||||
@ -130,7 +129,7 @@ public class Chapter : IComparable<Chapter>
|
||||
internal string GetComicInfoXmlString()
|
||||
{
|
||||
XElement comicInfo = new("ComicInfo",
|
||||
new XElement("Tags", string.Join(',', ParentManga.Tags.Select(tag => tag.Tag))),
|
||||
new XElement("Tags", string.Join(',', ParentManga.MangaTags.Select(tag => tag.Tag))),
|
||||
new XElement("LanguageISO", ParentManga.OriginalLanguage),
|
||||
new XElement("Title", Title),
|
||||
new XElement("Writer", string.Join(',', ParentManga.Authors.Select(author => author.AuthorName))),
|
||||
|
21
API/Schema/Jobs/DownloadAvailableChaptersJob.cs
Normal file
21
API/Schema/Jobs/DownloadAvailableChaptersJob.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using API.Schema.MangaConnectors;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class DownloadAvailableChaptersJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadAvailableChaptersJob)), JobType.DownloadAvailableChaptersJob, recurrenceMs, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string MangaId { get; init; } = mangaId;
|
||||
|
||||
[JsonIgnore]
|
||||
public Manga? Manga { get; init; }
|
||||
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
return context.Chapters.Where(c => c.ParentMangaId == MangaId).AsEnumerable()
|
||||
.Select(chapter => new DownloadSingleChapterJob(chapter.ChapterId, this.JobId));
|
||||
}
|
||||
}
|
@ -4,8 +4,10 @@
|
||||
public enum JobType : byte
|
||||
{
|
||||
DownloadSingleChapterJob = 0,
|
||||
DownloadNewChaptersJob = 1,
|
||||
DownloadAvailableChaptersJob = 1,
|
||||
UpdateMetaDataJob = 2,
|
||||
MoveFileOrFolderJob = 3,
|
||||
DownloadMangaCoverJob = 4
|
||||
DownloadMangaCoverJob = 4,
|
||||
RetrieveChaptersJob = 5,
|
||||
UpdateFilesDownloadedJob = 6
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using API.Schema.MangaConnectors;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class DownloadNewChaptersJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(DownloadNewChaptersJob)), JobType.DownloadNewChaptersJob, recurrenceMs, parentJobId, dependsOnJobsIds)
|
||||
public class RetrieveChaptersJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(RetrieveChaptersJob)), JobType.RetrieveChaptersJob, recurrenceMs, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string MangaId { get; init; } = mangaId;
|
||||
@ -30,7 +30,7 @@ public class DownloadNewChaptersJob(ulong recurrenceMs, string mangaId, string?
|
||||
Chapter[] newChapters = allNewChapters.Where(chapter => !chapterIds.Contains(chapter.ChapterId)).ToArray();
|
||||
context.Chapters.AddRangeAsync(newChapters).Wait();
|
||||
context.SaveChangesAsync().Wait();
|
||||
|
||||
return allNewChapters.Select(chapter => new DownloadSingleChapterJob(chapter.ChapterId, this.JobId));
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
24
API/Schema/Jobs/UpdateFilesDownloadedJob.cs
Normal file
24
API/Schema/Jobs/UpdateFilesDownloadedJob.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace API.Schema.Jobs;
|
||||
|
||||
public class UpdateFilesDownloadedJob(ulong recurrenceMs, string mangaId, string? parentJobId = null, ICollection<string>? dependsOnJobsIds = null)
|
||||
: Job(TokenGen.CreateToken(typeof(UpdateFilesDownloadedJob)), JobType.UpdateFilesDownloadedJob, recurrenceMs, parentJobId, dependsOnJobsIds)
|
||||
{
|
||||
[MaxLength(64)]
|
||||
public string MangaId { get; init; } = mangaId;
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual Manga? Manga { get; init; }
|
||||
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
IQueryable<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == MangaId);
|
||||
foreach (Chapter chapter in chapters)
|
||||
chapter.Downloaded = chapter.IsDownloaded();
|
||||
|
||||
context.SaveChanges();
|
||||
return [];
|
||||
}
|
||||
}
|
@ -21,14 +21,6 @@ public class UpdateMetadataJob(ulong recurrenceMs, string mangaId, string? paren
|
||||
/// <param name="context"></param>
|
||||
protected override IEnumerable<Job> RunInternal(PgsqlContext context)
|
||||
{
|
||||
//Manga manga = Manga ?? context.Manga.Find(MangaId)!;
|
||||
IQueryable<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == MangaId);
|
||||
foreach (Chapter chapter in chapters)
|
||||
chapter.Downloaded = chapter.IsDownloaded();
|
||||
|
||||
context.SaveChanges();
|
||||
return [];
|
||||
|
||||
//TODO implement Metadata-Update from MangaConnector
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
@ -6,6 +7,7 @@ using API.MangaDownloadClients;
|
||||
using API.Schema.Jobs;
|
||||
using API.Schema.MangaConnectors;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using static System.IO.UnixFileMode;
|
||||
|
||||
namespace API.Schema;
|
||||
@ -30,26 +32,30 @@ public class Manga
|
||||
public float IgnoreChapterBefore { get; internal set; }
|
||||
|
||||
public string MangaConnectorId { get; private set; }
|
||||
[JsonIgnore] public MangaConnector? MangaConnector { get; private set; }
|
||||
|
||||
public MangaConnector? MangaConnector { get; private set; }
|
||||
[JsonIgnore] public ICollection<Author>? Authors { get; internal set; }
|
||||
[NotMapped] public IEnumerable<string> AuthorIds => Authors?.Select(a => a.AuthorId) ?? [];
|
||||
|
||||
public ICollection<Author>? Authors { get; internal set; }
|
||||
[JsonIgnore] public ICollection<MangaTag>? MangaTags { get; internal set; }
|
||||
[NotMapped] public IEnumerable<string> Tags => MangaTags.Select(t => t.Tag);
|
||||
|
||||
public ICollection<MangaTag>? Tags { get; internal set; }
|
||||
|
||||
public ICollection<Link>? Links { get; internal set; }
|
||||
[JsonIgnore] public ICollection<Link>? Links { get; internal set; }
|
||||
[NotMapped] public IEnumerable<string> LinkIds => Links?.Select(l => l.LinkId) ?? [];
|
||||
|
||||
public ICollection<MangaAltTitle>? AltTitles { get; internal set; }
|
||||
[JsonIgnore] public ICollection<MangaAltTitle>? AltTitles { get; internal set; }
|
||||
[NotMapped] public IEnumerable<string> AltTitleIds => AltTitles?.Select(a => a.AltTitleId) ?? [];
|
||||
|
||||
public Manga(string connectorId, string name, string description, string websiteUrl, string coverUrl,
|
||||
string? coverFileNameInCache, uint year, string? originalLanguage, MangaReleaseStatus releaseStatus,
|
||||
float ignoreChapterBefore, MangaConnector mangaConnector, ICollection<Author> authors,
|
||||
ICollection<MangaTag> tags, ICollection<Link> links, ICollection<MangaAltTitle> altTitles)
|
||||
ICollection<MangaTag> mangaTags, ICollection<Link> links, ICollection<MangaAltTitle> altTitles)
|
||||
: this(connectorId, name, description, websiteUrl, coverUrl, coverFileNameInCache, year, originalLanguage,
|
||||
releaseStatus, ignoreChapterBefore, mangaConnector.Name)
|
||||
{
|
||||
this.Authors = authors;
|
||||
this.Tags = tags;
|
||||
this.MangaTags = mangaTags;
|
||||
this.Links = links;
|
||||
this.AltTitles = altTitles;
|
||||
}
|
||||
@ -89,7 +95,7 @@ public class Manga
|
||||
this.OriginalLanguage = other.OriginalLanguage;
|
||||
this.Authors = other.Authors;
|
||||
this.Links = other.Links;
|
||||
this.Tags = other.Tags;
|
||||
this.MangaTags = other.MangaTags;
|
||||
this.AltTitles = other.AltTitles;
|
||||
this.ReleaseStatus = other.ReleaseStatus;
|
||||
}
|
||||
|
@ -36,21 +36,23 @@ public class PgsqlContext(DbContextOptions<PgsqlContext> options) : DbContext(op
|
||||
.HasDiscriminator<LibraryType>(l => l.LibraryType)
|
||||
.HasValue<Komga>(LibraryType.Komga)
|
||||
.HasValue<Kavita>(LibraryType.Kavita);
|
||||
|
||||
|
||||
modelBuilder.Entity<Job>()
|
||||
.HasDiscriminator<JobType>(j => j.JobType)
|
||||
.HasValue<MoveFileOrFolderJob>(JobType.MoveFileOrFolderJob)
|
||||
.HasValue<DownloadNewChaptersJob>(JobType.DownloadNewChaptersJob)
|
||||
.HasValue<DownloadAvailableChaptersJob>(JobType.DownloadAvailableChaptersJob)
|
||||
.HasValue<DownloadSingleChapterJob>(JobType.DownloadSingleChapterJob)
|
||||
.HasValue<DownloadMangaCoverJob>(JobType.DownloadMangaCoverJob)
|
||||
.HasValue<UpdateMetadataJob>(JobType.UpdateMetaDataJob);
|
||||
.HasValue<UpdateMetadataJob>(JobType.UpdateMetaDataJob)
|
||||
.HasValue<RetrieveChaptersJob>(JobType.RetrieveChaptersJob)
|
||||
.HasValue<UpdateFilesDownloadedJob>(JobType.UpdateFilesDownloadedJob);
|
||||
modelBuilder.Entity<Job>()
|
||||
.HasOne<Job>(j => j.ParentJob)
|
||||
.WithMany()
|
||||
.HasForeignKey(j => j.ParentJobId);
|
||||
modelBuilder.Entity<Job>()
|
||||
.HasMany<Job>(j => j.DependsOnJobs);
|
||||
modelBuilder.Entity<DownloadNewChaptersJob>()
|
||||
modelBuilder.Entity<DownloadAvailableChaptersJob>()
|
||||
.Navigation(dncj => dncj.Manga)
|
||||
.AutoInclude();
|
||||
modelBuilder.Entity<DownloadSingleChapterJob>()
|
||||
@ -74,10 +76,10 @@ public class PgsqlContext(DbContextOptions<PgsqlContext> options) : DbContext(op
|
||||
.Navigation(m => m.Authors)
|
||||
.AutoInclude();
|
||||
modelBuilder.Entity<Manga>()
|
||||
.HasMany<MangaTag>(m => m.Tags)
|
||||
.HasMany<MangaTag>(m => m.MangaTags)
|
||||
.WithMany();
|
||||
modelBuilder.Entity<Manga>()
|
||||
.Navigation(m => m.Tags)
|
||||
.Navigation(m => m.MangaTags)
|
||||
.AutoInclude();
|
||||
modelBuilder.Entity<Manga>()
|
||||
.HasMany<Link>(m => m.Links)
|
||||
|
@ -91,10 +91,10 @@ public static class Tranga
|
||||
// If the job is already running, skip it
|
||||
if (RunningJobs.Values.Any(j => j.JobId == job.JobId)) continue;
|
||||
|
||||
if (job is DownloadNewChaptersJob dncj)
|
||||
if (job is DownloadAvailableChaptersJob dncj)
|
||||
{
|
||||
if (RunningJobs.Values.Any(j =>
|
||||
j is DownloadNewChaptersJob rdncj &&
|
||||
j is DownloadAvailableChaptersJob rdncj &&
|
||||
rdncj.Manga?.MangaConnector == dncj.Manga?.MangaConnector))
|
||||
{
|
||||
continue;
|
||||
@ -143,13 +143,15 @@ public static class Tranga
|
||||
IEnumerable<Job> ret = new List<Job>();
|
||||
if(jobsByType.ContainsKey(JobType.MoveFileOrFolderJob))
|
||||
ret = ret.Concat(jobsByType[JobType.MoveFileOrFolderJob]);
|
||||
if(jobsByType.ContainsKey(JobType.DownloadMangaCoverJob))
|
||||
ret = ret.Concat(jobsByType[JobType.DownloadMangaCoverJob]);
|
||||
if(jobsByType.ContainsKey(JobType.DownloadMangaCoverJob))
|
||||
ret = ret.Concat(jobsByType[JobType.DownloadMangaCoverJob]);
|
||||
if(jobsByType.ContainsKey(JobType.UpdateFilesDownloadedJob))
|
||||
ret = ret.Concat(jobsByType[JobType.UpdateFilesDownloadedJob]);
|
||||
|
||||
Dictionary<MangaConnector, List<Job>> metadataJobsByConnector = new();
|
||||
if (jobsByType.ContainsKey(JobType.DownloadNewChaptersJob))
|
||||
if (jobsByType.ContainsKey(JobType.DownloadAvailableChaptersJob))
|
||||
{
|
||||
foreach (DownloadNewChaptersJob job in jobsByType[JobType.DownloadNewChaptersJob])
|
||||
foreach (DownloadAvailableChaptersJob job in jobsByType[JobType.DownloadAvailableChaptersJob])
|
||||
{
|
||||
Manga manga = job.Manga ?? context.Manga.Find(job.MangaId)!;
|
||||
MangaConnector connector = manga.MangaConnector ?? context.MangaConnectors.Find(manga.MangaConnectorId)!;
|
||||
@ -167,6 +169,16 @@ public static class Tranga
|
||||
metadataJobsByConnector[connector].Add(job);
|
||||
}
|
||||
}
|
||||
if (jobsByType.ContainsKey(JobType.RetrieveChaptersJob))
|
||||
{
|
||||
foreach (RetrieveChaptersJob job in jobsByType[JobType.RetrieveChaptersJob])
|
||||
{
|
||||
Manga manga = job.Manga ?? context.Manga.Find(job.MangaId)!;
|
||||
MangaConnector connector = manga.MangaConnector ?? context.MangaConnectors.Find(manga.MangaConnectorId)!;
|
||||
if(!metadataJobsByConnector.TryAdd(connector, [job]))
|
||||
metadataJobsByConnector[connector].Add(job);
|
||||
}
|
||||
}
|
||||
foreach (List<Job> metadataJobs in metadataJobsByConnector.Values)
|
||||
ret = ret.Append(metadataJobs.MinBy(j => j.NextExecution))!;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user