diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs
index dadfc74b7..c7134c34f 100644
--- a/API/Controllers/SeriesController.cs
+++ b/API/Controllers/SeriesController.cs
@@ -648,13 +648,13 @@ public class SeriesController : BaseApiController
///
/// This will perform the fix 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();
}
diff --git a/API/DTOs/KavitaPlus/ExternalMetadata/SeriesDetailPlusApiDto.cs b/API/DTOs/KavitaPlus/ExternalMetadata/SeriesDetailPlusApiDto.cs
index f67fb1f4c..26411bce7 100644
--- a/API/DTOs/KavitaPlus/ExternalMetadata/SeriesDetailPlusApiDto.cs
+++ b/API/DTOs/KavitaPlus/ExternalMetadata/SeriesDetailPlusApiDto.cs
@@ -13,4 +13,5 @@ internal class SeriesDetailPlusApiDto
public ExternalSeriesDetailDto? Series { get; set; }
public int? AniListId { get; set; }
public long? MalId { get; set; }
+ public int? CbrId { get; set; }
}
diff --git a/API/DTOs/KavitaPlus/Metadata/ExternalSeriesDetailDto.cs b/API/DTOs/KavitaPlus/Metadata/ExternalSeriesDetailDto.cs
index a5a037cc3..73adead93 100644
--- a/API/DTOs/KavitaPlus/Metadata/ExternalSeriesDetailDto.cs
+++ b/API/DTOs/KavitaPlus/Metadata/ExternalSeriesDetailDto.cs
@@ -15,6 +15,7 @@ public class ExternalSeriesDetailDto
public string Name { get; set; }
public int? AniListId { get; set; }
public long? MALId { get; set; }
+ public int? CbrId { get; set; }
public IList Synonyms { get; set; } = [];
public PlusMediaFormat PlusMediaFormat { get; set; }
public string? SiteUrl { get; set; }
diff --git a/API/DTOs/Scrobbling/PlusSeriesDto.cs b/API/DTOs/Scrobbling/PlusSeriesDto.cs
index 75e443d2e..50a3c5784 100644
--- a/API/DTOs/Scrobbling/PlusSeriesDto.cs
+++ b/API/DTOs/Scrobbling/PlusSeriesDto.cs
@@ -9,6 +9,10 @@ public record PlusSeriesRequestDto
public long? MalId { get; set; }
public string? GoogleBooksId { get; set; }
public string? MangaDexId { get; set; }
+ ///
+ /// ComicBookRoundup Id
+ ///
+ public int? CbrId { get; set; }
public string SeriesName { get; set; }
public string? AltSeriesName { get; set; }
public PlusMediaFormat MediaFormat { get; set; }
diff --git a/API/Services/Plus/ExternalMetadataService.cs b/API/Services/Plus/ExternalMetadataService.cs
index a73701cf8..3c2a9cee5 100644
--- a/API/Services/Plus/ExternalMetadataService.cs
+++ b/API/Services/Plus/ExternalMetadataService.cs
@@ -49,7 +49,7 @@ public interface IExternalMetadataService
Task> GetStacksForUser(int userId);
Task> 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 WriteExternalMetadataToSeries(ExternalSeriesDetailDto externalMetadata, int seriesId);
}
@@ -196,7 +196,7 @@ public class ExternalMetadataService : IExternalMetadataService
{
var license = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey)).Value;
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(dto.SeriesId,
- SeriesIncludes.Metadata | SeriesIncludes.ExternalMetadata);
+ SeriesIncludes.Metadata | SeriesIncludes.ExternalMetadata | SeriesIncludes.Library);
if (series == null) return [];
var potentialAnilistId = ScrobblingService.ExtractId(dto.Query, ScrobblingService.AniListWeblinkWebsite);
@@ -210,7 +210,7 @@ public class ExternalMetadataService : IExternalMetadataService
var matchRequest = new MatchSeriesRequestDto()
{
- Format = series.Format == MangaFormat.Epub ? PlusMediaFormat.LightNovel : PlusMediaFormat.Manga,
+ Format = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
Query = dto.Query,
SeriesName = series.Name,
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
///
///
- ///
- public async Task FixSeriesMatch(int seriesId, int anilistId, long? malId)
+ ///
+ ///
+ ///
+ public async Task FixSeriesMatch(int seriesId, int? aniListId, long? malId, int? cbrId)
{
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Library);
if (series == null) return;
@@ -329,15 +331,17 @@ public class ExternalMetadataService : IExternalMetadataService
var metadata = await FetchExternalMetadataForSeries(seriesId, series.Library.Type,
new PlusSeriesRequestDto()
{
- AniListId = anilistId,
+ AniListId = aniListId,
MalId = malId,
+ CbrId = cbrId,
+ MediaFormat = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
SeriesName = series.Name // Required field, not used since AniList/Mal Id are passed
});
if (metadata.Series == null)
{
- _logger.LogError("Unable to Match {SeriesName} with Kavita+ Series AniList Id: {AniListId}",
- series.Name, anilistId);
+ _logger.LogError("Unable to Match {SeriesName} with Kavita+ Series with Id: {AniListId}/{MalId}/{CbrId}",
+ series.Name, aniListId, malId, cbrId);
return;
}
@@ -421,8 +425,7 @@ public class ExternalMetadataService : IExternalMetadataService
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
.WithKavitaPlusHeaders(license, token)
.PostJsonAsync(data)
- .ReceiveJson<
- SeriesDetailPlusApiDto>(); // This returns an AniListSeries and Match returns ExternalSeriesDto
+ .ReceiveJson(); // This returns an AniListSeries and Match returns ExternalSeriesDto
}
catch (FlurlHttpException ex)
{
@@ -438,8 +441,7 @@ public class ExternalMetadataService : IExternalMetadataService
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
.WithKavitaPlusHeaders(license, token)
.PostJsonAsync(data)
- .ReceiveJson<
- SeriesDetailPlusApiDto>();
+ .ReceiveJson();
}
}
diff --git a/UI/Web/src/app/_models/series-detail/external-series-detail.ts b/UI/Web/src/app/_models/series-detail/external-series-detail.ts
index aa62d3960..db25782ca 100644
--- a/UI/Web/src/app/_models/series-detail/external-series-detail.ts
+++ b/UI/Web/src/app/_models/series-detail/external-series-detail.ts
@@ -27,8 +27,9 @@ export interface MetadataTagDto {
export interface ExternalSeriesDetail {
name: string;
- aniListId?: number;
- malId?: number;
+ aniListId?: number | null;
+ malId?: number | null;
+ cbrId?: number | null;
synonyms: Array;
plusMediaFormat: PlusMediaFormat;
siteUrl?: string;
diff --git a/UI/Web/src/app/_services/series.service.ts b/UI/Web/src/app/_services/series.service.ts
index f8644748b..f221b2f1a 100644
--- a/UI/Web/src/app/_services/series.service.ts
+++ b/UI/Web/src/app/_services/series.service.ts
@@ -242,7 +242,7 @@ export class SeriesService {
}
updateMatch(seriesId: number, series: ExternalSeriesDetail) {
- return this.httpClient.post(this.baseUrl + `series/update-match?seriesId=${seriesId}&aniListId=${series.aniListId}${series.malId ? '&malId=' + series.malId : ''}`, {}, TextResonse);
+ return this.httpClient.post(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) {
diff --git a/UI/Web/src/app/_single-module/match-series-modal/match-series-modal.component.ts b/UI/Web/src/app/_single-module/match-series-modal/match-series-modal.component.ts
index be670684e..012fc5c14 100644
--- a/UI/Web/src/app/_single-module/match-series-modal/match-series-modal.component.ts
+++ b/UI/Web/src/app/_single-module/match-series-modal/match-series-modal.component.ts
@@ -92,7 +92,7 @@ export class MatchSeriesModalComponent implements OnInit {
data.tags = data.tags || [];
data.genres = data.genres || [];
- this.seriesService.updateMatch(this.series.id, data).subscribe(_ => {
+ this.seriesService.updateMatch(this.series.id, item.series).subscribe(_ => {
this.save();
});
}
diff --git a/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts b/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts
index c1b6871b7..1d8355dab 100644
--- a/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts
+++ b/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts
@@ -1,12 +1,4 @@
-import {
- ChangeDetectionStrategy,
- ChangeDetectorRef,
- Component,
- DestroyRef,
- inject,
- Input,
- OnInit
-} from '@angular/core';
+import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {
NgbActiveModal,
@@ -133,6 +125,11 @@ export class LibrarySettingsModalComponent implements OnInit {
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 {
this.settingService.getLibraryTypes().subscribe((types) => {
this.libraryTypes = types;
@@ -151,11 +148,19 @@ export class LibrarySettingsModalComponent implements OnInit {
if (this.library && !(this.library.type === LibraryType.Manga || this.library.type === LibraryType.LightNovel) ) {
this.libraryForm.get('allowScrobbling')?.setValue(false);
- this.libraryForm.get('allowMetadataMatching')?.setValue(false);
this.libraryForm.get('allowScrobbling')?.disable();
- this.libraryForm.get('allowMetadataMatching')?.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('name')?.valueChanges.pipe(
debounceTime(100),
distinctUntilChanged(),
@@ -218,11 +223,16 @@ export class LibrarySettingsModalComponent implements OnInit {
if (!this.IsKavitaPlusEligible) {
this.libraryForm.get('allowScrobbling')?.disable();
- this.libraryForm.get('allowMetadataMatching')?.disable();
} else {
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();
}),
takeUntilDestroyed(this.destroyRef)
@@ -241,7 +251,7 @@ export class LibrarySettingsModalComponent implements OnInit {
this.libraryForm.get('manageReadingLists')?.setValue(this.library.manageReadingLists);
this.libraryForm.get('collapseSeriesRelationships')?.setValue(this.library.collapseSeriesRelationships);
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.madeChanges = false;