Updated Matched Metadat to have a library type filter, given that a user might want to do just Comics or Manga at a time.

This commit is contained in:
Joseph Milazzo 2025-06-06 05:23:10 -05:00
parent 5db4c80410
commit ef77474e33
9 changed files with 48 additions and 16 deletions

View file

@ -15,5 +15,9 @@ public enum MatchStateOption
public sealed record ManageMatchFilterDto
{
public MatchStateOption MatchStateOption { get; set; } = MatchStateOption.All;
/// <summary>
/// Library Type in int form. -1 indicates to ignore the field.
/// </summary>
public int LibraryType { get; set; } = -1;
public string SearchTerm { get; set; } = string.Empty;
}

View file

@ -234,6 +234,7 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
.Include(s => s.ExternalSeriesMetadata)
.Where(s => !ExternalMetadataService.NonEligibleLibraryTypes.Contains(s.Library.Type))
.Where(s => s.Library.AllowMetadataMatching)
.WhereIf(filter.LibraryType >= 0, s => s.Library.Type == (LibraryType) filter.LibraryType)
.FilterMatchState(filter.MatchStateOption)
.OrderBy(s => s.NormalizedName)
.ProjectTo<ManageMatchSeriesDto>(_mapper.ConfigurationProvider)

View file

@ -1,6 +1,8 @@
import {MatchStateOption} from "./match-state-option";
import {LibraryType} from "../library/library";
export interface ManageMatchFilter {
matchStateOption: MatchStateOption;
libraryType: LibraryType | -1;
searchTerm: string;
}

View file

@ -13,6 +13,8 @@ export enum LibraryType {
}
export const allLibraryTypes = [LibraryType.Manga, LibraryType.ComicVine, LibraryType.Comic, LibraryType.Book, LibraryType.LightNovel, LibraryType.Images];
export const allKavitaPlusMetadataApplicableTypes = [LibraryType.Manga, LibraryType.LightNovel, LibraryType.ComicVine, LibraryType.Comic];
export const allKavitaPlusEligibleTypes = [LibraryType.Manga, LibraryType.LightNovel];
export interface Library {
id: number;

View file

@ -3,8 +3,17 @@
<form [formGroup]="filterGroup">
<div class="row g-0">
<div class="col-auto ms-auto">
<label for="match-filter">Match State</label>
<div class="col-auto ms-auto me-3">
<label for="libtype-filter">{{t('library-type')}}</label>
<select class="form-select" formControlName="libraryType" id="libtype-filter">
<option [value]="-1">{{t('all-status-label')}}</option>
@for(libType of allLibraryTypes; track libType) {
<option [value]="libType">{{libType | libraryType}}</option>
}
</select>
</div>
<div class="col-auto">
<label for="match-filter">{{t('matched-state-label')}}</label>
<select class="form-select" formControlName="matchState" id="match-filter">
@for(state of allMatchStates; track state) {
<option [value]="state">{{state | matchStateOption}}</option>

View file

@ -21,20 +21,23 @@ import {LibraryNamePipe} from "../../_pipes/library-name.pipe";
import {AsyncPipe} from "@angular/common";
import {EVENTS, MessageHubService} from "../../_services/message-hub.service";
import {ScanSeriesEvent} from "../../_models/events/scan-series-event";
import {LibraryTypePipe} from "../../_pipes/library-type.pipe";
import {allKavitaPlusMetadataApplicableTypes} from "../../_models/library/library";
@Component({
selector: 'app-manage-matched-metadata',
imports: [
TranslocoDirective,
ImageComponent,
VirtualScrollerModule,
ReactiveFormsModule,
MatchStateOptionPipe,
UtcToLocalTimePipe,
DefaultValuePipe,
NgxDatatableModule,
LibraryNamePipe,
AsyncPipe,
TranslocoDirective,
ImageComponent,
VirtualScrollerModule,
ReactiveFormsModule,
MatchStateOptionPipe,
UtcToLocalTimePipe,
DefaultValuePipe,
NgxDatatableModule,
LibraryNamePipe,
AsyncPipe,
LibraryTypePipe,
],
templateUrl: './manage-matched-metadata.component.html',
styleUrl: './manage-matched-metadata.component.scss',
@ -44,6 +47,7 @@ export class ManageMatchedMetadataComponent implements OnInit {
protected readonly ColumnMode = ColumnMode;
protected readonly MatchStateOption = MatchStateOption;
protected readonly allMatchStates = allMatchStates.filter(m => m !== MatchStateOption.Matched); // Matched will have too many
protected readonly allLibraryTypes = allKavitaPlusMetadataApplicableTypes;
private readonly licenseService = inject(LicenseService);
private readonly actionService = inject(ActionService);
@ -58,6 +62,7 @@ export class ManageMatchedMetadataComponent implements OnInit {
data: Array<ManageMatchSeries> = [];
filterGroup = new FormGroup({
'matchState': new FormControl(MatchStateOption.Error, []),
'libraryType': new FormControl(-1, []), // Denotes all
});
ngOnInit() {
@ -99,6 +104,7 @@ export class ManageMatchedMetadataComponent implements OnInit {
loadData() {
const filter: ManageMatchFilter = {
matchStateOption: parseInt(this.filterGroup.get('matchState')!.value + '', 10),
libraryType: parseInt(this.filterGroup.get('libraryType')!.value + '', 10),
searchTerm: ''
};

View file

@ -20,7 +20,13 @@ import {
} from 'src/app/admin/_modals/directory-picker/directory-picker.component';
import {ConfirmService} from 'src/app/shared/confirm.service';
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
import {allLibraryTypes, Library, LibraryType} from 'src/app/_models/library/library';
import {
allKavitaPlusEligibleTypes,
allKavitaPlusMetadataApplicableTypes,
allLibraryTypes,
Library,
LibraryType
} from 'src/app/_models/library/library';
import {ImageService} from 'src/app/_services/image.service';
import {LibraryService} from 'src/app/_services/library.service';
import {UploadService} from 'src/app/_services/upload.service';
@ -125,13 +131,12 @@ export class LibrarySettingsModalComponent implements OnInit {
get IsKavitaPlusEligible() {
const libType = parseInt(this.libraryForm.get('type')?.value + '', 10) as LibraryType;
return libType === LibraryType.Manga || libType === LibraryType.LightNovel;
return allKavitaPlusEligibleTypes.includes(libType);
}
get IsMetadataDownloadEligible() {
const libType = parseInt(this.libraryForm.get('type')?.value + '', 10) as LibraryType;
return libType === LibraryType.Manga || libType === LibraryType.LightNovel
|| libType === LibraryType.ComicVine || libType === LibraryType.Comic;
return allKavitaPlusMetadataApplicableTypes.includes(libType);
}
ngOnInit(): void {

View file

@ -190,6 +190,7 @@ export class PreferenceNavComponent implements AfterViewInit {
} else {
return this.manageService.getAllKavitaPlusSeries({
matchStateOption: MatchStateOption.Error,
libraryType: -1,
searchTerm: ''
}).pipe(
takeUntilDestroyed(this.destroyRef),

View file

@ -773,6 +773,8 @@
"library-name-header": "Library",
"valid-until-header": "Next Refresh",
"actions-header": "Actions",
"library-type": "Library Type",
"matched-state-label": "Match State",
"matched-status-label": "Matched",
"unmatched-status-label": "Not Matched",
"blacklist-status-label": "Needs Manual Match",