Basic wiring with Kavita+ for CBR implemented. Metadata writing needs to be hooked up.
This commit is contained in:
parent
75419fb62b
commit
eba5e6875f
9 changed files with 52 additions and 33 deletions
|
|
@ -648,13 +648,13 @@ public class SeriesController : BaseApiController
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This will perform the fix match
|
/// This will perform the fix match
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="aniListId"></param>
|
/// <param name="match"></param>
|
||||||
/// <param name="seriesId"></param>
|
/// <param name="seriesId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("update-match")]
|
[HttpPost("update-match")]
|
||||||
public ActionResult UpdateSeriesMatch([FromQuery] int seriesId, [FromQuery] int aniListId, [FromQuery] long? malId)
|
public ActionResult UpdateSeriesMatch([FromQuery] int seriesId, [FromQuery] int? aniListId, [FromQuery] long? malId, [FromQuery] int? cbrId)
|
||||||
{
|
{
|
||||||
BackgroundJob.Enqueue(() => _externalMetadataService.FixSeriesMatch(seriesId, aniListId, malId));
|
BackgroundJob.Enqueue(() => _externalMetadataService.FixSeriesMatch(seriesId, aniListId, malId, cbrId));
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,5 @@ internal class SeriesDetailPlusApiDto
|
||||||
public ExternalSeriesDetailDto? Series { get; set; }
|
public ExternalSeriesDetailDto? Series { get; set; }
|
||||||
public int? AniListId { get; set; }
|
public int? AniListId { get; set; }
|
||||||
public long? MalId { get; set; }
|
public long? MalId { get; set; }
|
||||||
|
public int? CbrId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ public class ExternalSeriesDetailDto
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int? AniListId { get; set; }
|
public int? AniListId { get; set; }
|
||||||
public long? MALId { get; set; }
|
public long? MALId { get; set; }
|
||||||
|
public int? CbrId { get; set; }
|
||||||
public IList<string> Synonyms { get; set; } = [];
|
public IList<string> Synonyms { get; set; } = [];
|
||||||
public PlusMediaFormat PlusMediaFormat { get; set; }
|
public PlusMediaFormat PlusMediaFormat { get; set; }
|
||||||
public string? SiteUrl { get; set; }
|
public string? SiteUrl { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ public record PlusSeriesRequestDto
|
||||||
public long? MalId { get; set; }
|
public long? MalId { get; set; }
|
||||||
public string? GoogleBooksId { get; set; }
|
public string? GoogleBooksId { get; set; }
|
||||||
public string? MangaDexId { get; set; }
|
public string? MangaDexId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// ComicBookRoundup Id
|
||||||
|
/// </summary>
|
||||||
|
public int? CbrId { get; set; }
|
||||||
public string SeriesName { get; set; }
|
public string SeriesName { get; set; }
|
||||||
public string? AltSeriesName { get; set; }
|
public string? AltSeriesName { get; set; }
|
||||||
public PlusMediaFormat MediaFormat { get; set; }
|
public PlusMediaFormat MediaFormat { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ public interface IExternalMetadataService
|
||||||
|
|
||||||
Task<IList<MalStackDto>> GetStacksForUser(int userId);
|
Task<IList<MalStackDto>> GetStacksForUser(int userId);
|
||||||
Task<IList<ExternalSeriesMatchDto>> MatchSeries(MatchSeriesDto dto);
|
Task<IList<ExternalSeriesMatchDto>> MatchSeries(MatchSeriesDto dto);
|
||||||
Task FixSeriesMatch(int seriesId, int anilistId, long? malId);
|
Task FixSeriesMatch(int seriesId, int? aniListId, long? malId, int? cbrId);
|
||||||
Task UpdateSeriesDontMatch(int seriesId, bool dontMatch);
|
Task UpdateSeriesDontMatch(int seriesId, bool dontMatch);
|
||||||
Task<bool> WriteExternalMetadataToSeries(ExternalSeriesDetailDto externalMetadata, int seriesId);
|
Task<bool> WriteExternalMetadataToSeries(ExternalSeriesDetailDto externalMetadata, int seriesId);
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +196,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||||
{
|
{
|
||||||
var license = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey)).Value;
|
var license = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey)).Value;
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(dto.SeriesId,
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(dto.SeriesId,
|
||||||
SeriesIncludes.Metadata | SeriesIncludes.ExternalMetadata);
|
SeriesIncludes.Metadata | SeriesIncludes.ExternalMetadata | SeriesIncludes.Library);
|
||||||
if (series == null) return [];
|
if (series == null) return [];
|
||||||
|
|
||||||
var potentialAnilistId = ScrobblingService.ExtractId<int?>(dto.Query, ScrobblingService.AniListWeblinkWebsite);
|
var potentialAnilistId = ScrobblingService.ExtractId<int?>(dto.Query, ScrobblingService.AniListWeblinkWebsite);
|
||||||
|
|
@ -210,7 +210,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||||
|
|
||||||
var matchRequest = new MatchSeriesRequestDto()
|
var matchRequest = new MatchSeriesRequestDto()
|
||||||
{
|
{
|
||||||
Format = series.Format == MangaFormat.Epub ? PlusMediaFormat.LightNovel : PlusMediaFormat.Manga,
|
Format = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
|
||||||
Query = dto.Query,
|
Query = dto.Query,
|
||||||
SeriesName = series.Name,
|
SeriesName = series.Name,
|
||||||
AlternativeNames = altNames.Where(s => !string.IsNullOrEmpty(s)).ToList(),
|
AlternativeNames = altNames.Where(s => !string.IsNullOrEmpty(s)).ToList(),
|
||||||
|
|
@ -312,8 +312,10 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||||
/// This will override any sort of matching that was done prior and force it to be what the user Selected
|
/// This will override any sort of matching that was done prior and force it to be what the user Selected
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="seriesId"></param>
|
/// <param name="seriesId"></param>
|
||||||
/// <param name="anilistId"></param>
|
/// <param name="aniListId"></param>
|
||||||
public async Task FixSeriesMatch(int seriesId, int anilistId, long? malId)
|
/// <param name="malId"></param>
|
||||||
|
/// <param name="cbrId"></param>
|
||||||
|
public async Task FixSeriesMatch(int seriesId, int? aniListId, long? malId, int? cbrId)
|
||||||
{
|
{
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Library);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Library);
|
||||||
if (series == null) return;
|
if (series == null) return;
|
||||||
|
|
@ -329,15 +331,17 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||||
var metadata = await FetchExternalMetadataForSeries(seriesId, series.Library.Type,
|
var metadata = await FetchExternalMetadataForSeries(seriesId, series.Library.Type,
|
||||||
new PlusSeriesRequestDto()
|
new PlusSeriesRequestDto()
|
||||||
{
|
{
|
||||||
AniListId = anilistId,
|
AniListId = aniListId,
|
||||||
MalId = malId,
|
MalId = malId,
|
||||||
|
CbrId = cbrId,
|
||||||
|
MediaFormat = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
|
||||||
SeriesName = series.Name // Required field, not used since AniList/Mal Id are passed
|
SeriesName = series.Name // Required field, not used since AniList/Mal Id are passed
|
||||||
});
|
});
|
||||||
|
|
||||||
if (metadata.Series == null)
|
if (metadata.Series == null)
|
||||||
{
|
{
|
||||||
_logger.LogError("Unable to Match {SeriesName} with Kavita+ Series AniList Id: {AniListId}",
|
_logger.LogError("Unable to Match {SeriesName} with Kavita+ Series with Id: {AniListId}/{MalId}/{CbrId}",
|
||||||
series.Name, anilistId);
|
series.Name, aniListId, malId, cbrId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,8 +425,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||||
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
|
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
|
||||||
.WithKavitaPlusHeaders(license, token)
|
.WithKavitaPlusHeaders(license, token)
|
||||||
.PostJsonAsync(data)
|
.PostJsonAsync(data)
|
||||||
.ReceiveJson<
|
.ReceiveJson<SeriesDetailPlusApiDto>(); // This returns an AniListSeries and Match returns ExternalSeriesDto
|
||||||
SeriesDetailPlusApiDto>(); // This returns an AniListSeries and Match returns ExternalSeriesDto
|
|
||||||
}
|
}
|
||||||
catch (FlurlHttpException ex)
|
catch (FlurlHttpException ex)
|
||||||
{
|
{
|
||||||
|
|
@ -438,8 +441,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||||
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
|
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
|
||||||
.WithKavitaPlusHeaders(license, token)
|
.WithKavitaPlusHeaders(license, token)
|
||||||
.PostJsonAsync(data)
|
.PostJsonAsync(data)
|
||||||
.ReceiveJson<
|
.ReceiveJson<SeriesDetailPlusApiDto>();
|
||||||
SeriesDetailPlusApiDto>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,9 @@ export interface MetadataTagDto {
|
||||||
|
|
||||||
export interface ExternalSeriesDetail {
|
export interface ExternalSeriesDetail {
|
||||||
name: string;
|
name: string;
|
||||||
aniListId?: number;
|
aniListId?: number | null;
|
||||||
malId?: number;
|
malId?: number | null;
|
||||||
|
cbrId?: number | null;
|
||||||
synonyms: Array<string>;
|
synonyms: Array<string>;
|
||||||
plusMediaFormat: PlusMediaFormat;
|
plusMediaFormat: PlusMediaFormat;
|
||||||
siteUrl?: string;
|
siteUrl?: string;
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ export class SeriesService {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMatch(seriesId: number, series: ExternalSeriesDetail) {
|
updateMatch(seriesId: number, series: ExternalSeriesDetail) {
|
||||||
return this.httpClient.post<string>(this.baseUrl + `series/update-match?seriesId=${seriesId}&aniListId=${series.aniListId}${series.malId ? '&malId=' + series.malId : ''}`, {}, TextResonse);
|
return this.httpClient.post<string>(this.baseUrl + `series/update-match?seriesId=${seriesId}&aniListId=${series.aniListId || 0}&malId=${series.malId || 0}&cbrId=${series.cbrId || 0}`, {}, TextResonse);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDontMatch(seriesId: number, dontMatch: boolean) {
|
updateDontMatch(seriesId: number, dontMatch: boolean) {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export class MatchSeriesModalComponent implements OnInit {
|
||||||
data.tags = data.tags || [];
|
data.tags = data.tags || [];
|
||||||
data.genres = data.genres || [];
|
data.genres = data.genres || [];
|
||||||
|
|
||||||
this.seriesService.updateMatch(this.series.id, data).subscribe(_ => {
|
this.seriesService.updateMatch(this.series.id, item.series).subscribe(_ => {
|
||||||
this.save();
|
this.save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
import {
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
ChangeDetectionStrategy,
|
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
DestroyRef,
|
|
||||||
inject,
|
|
||||||
Input,
|
|
||||||
OnInit
|
|
||||||
} from '@angular/core';
|
|
||||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
NgbActiveModal,
|
NgbActiveModal,
|
||||||
|
|
@ -133,6 +125,11 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||||
return libType === LibraryType.Manga || libType === LibraryType.LightNovel;
|
return libType === LibraryType.Manga || libType === LibraryType.LightNovel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get IsMetadataDownloadEligible() {
|
||||||
|
const libType = parseInt(this.libraryForm.get('type')?.value + '', 10) as LibraryType;
|
||||||
|
return libType === LibraryType.Manga || libType === LibraryType.LightNovel || libType === LibraryType.ComicVine;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.settingService.getLibraryTypes().subscribe((types) => {
|
this.settingService.getLibraryTypes().subscribe((types) => {
|
||||||
this.libraryTypes = types;
|
this.libraryTypes = types;
|
||||||
|
|
@ -151,10 +148,18 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||||
|
|
||||||
if (this.library && !(this.library.type === LibraryType.Manga || this.library.type === LibraryType.LightNovel) ) {
|
if (this.library && !(this.library.type === LibraryType.Manga || this.library.type === LibraryType.LightNovel) ) {
|
||||||
this.libraryForm.get('allowScrobbling')?.setValue(false);
|
this.libraryForm.get('allowScrobbling')?.setValue(false);
|
||||||
this.libraryForm.get('allowMetadataMatching')?.setValue(false);
|
|
||||||
this.libraryForm.get('allowScrobbling')?.disable();
|
this.libraryForm.get('allowScrobbling')?.disable();
|
||||||
|
|
||||||
|
if (this.IsMetadataDownloadEligible) {
|
||||||
|
this.libraryForm.get('allowMetadataMatching')?.setValue(this.library.allowMetadataMatching);
|
||||||
|
this.libraryForm.get('allowMetadataMatching')?.enable();
|
||||||
|
} else {
|
||||||
|
this.libraryForm.get('allowMetadataMatching')?.setValue(false);
|
||||||
this.libraryForm.get('allowMetadataMatching')?.disable();
|
this.libraryForm.get('allowMetadataMatching')?.disable();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.libraryForm.get('name')?.valueChanges.pipe(
|
this.libraryForm.get('name')?.valueChanges.pipe(
|
||||||
debounceTime(100),
|
debounceTime(100),
|
||||||
|
|
@ -218,11 +223,16 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||||
|
|
||||||
if (!this.IsKavitaPlusEligible) {
|
if (!this.IsKavitaPlusEligible) {
|
||||||
this.libraryForm.get('allowScrobbling')?.disable();
|
this.libraryForm.get('allowScrobbling')?.disable();
|
||||||
this.libraryForm.get('allowMetadataMatching')?.disable();
|
|
||||||
} else {
|
} else {
|
||||||
this.libraryForm.get('allowScrobbling')?.enable();
|
this.libraryForm.get('allowScrobbling')?.enable();
|
||||||
this.libraryForm.get('allowMetadataMatching')?.enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.IsMetadataDownloadEligible) {
|
||||||
|
this.libraryForm.get('allowMetadataMatching')?.enable();
|
||||||
|
} else {
|
||||||
|
this.libraryForm.get('allowMetadataMatching')?.disable();
|
||||||
|
}
|
||||||
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}),
|
}),
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
|
|
@ -241,7 +251,7 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||||
this.libraryForm.get('manageReadingLists')?.setValue(this.library.manageReadingLists);
|
this.libraryForm.get('manageReadingLists')?.setValue(this.library.manageReadingLists);
|
||||||
this.libraryForm.get('collapseSeriesRelationships')?.setValue(this.library.collapseSeriesRelationships);
|
this.libraryForm.get('collapseSeriesRelationships')?.setValue(this.library.collapseSeriesRelationships);
|
||||||
this.libraryForm.get('allowScrobbling')?.setValue(this.IsKavitaPlusEligible ? this.library.allowScrobbling : false);
|
this.libraryForm.get('allowScrobbling')?.setValue(this.IsKavitaPlusEligible ? this.library.allowScrobbling : false);
|
||||||
this.libraryForm.get('allowMetadataMatching')?.setValue(this.IsKavitaPlusEligible ? this.library.allowMetadataMatching : false);
|
this.libraryForm.get('allowMetadataMatching')?.setValue(this.IsMetadataDownloadEligible ? this.library.allowMetadataMatching : false);
|
||||||
this.selectedFolders = this.library.folders;
|
this.selectedFolders = this.library.folders;
|
||||||
|
|
||||||
this.madeChanges = false;
|
this.madeChanges = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue