fix(library): multi-genre local index with track_genre and backfill (#1059)

* fix(library): multi-genre local index with track_genre and backfill

Restore atomic genre browse, filters, and counts via track_genre:
OpenSubsonic genres[] first with Navidrome-default split fallback, sync
write path, read-path query switches, blocking startup backfill with
progress, and v12 repair migration for DBs that recorded legacy 002–011.
TS fallback adds genreTagsFor and migration gate i18n across locales.

* fix(library): address multi-genre review — robust TS genres and scope join

genreTagsFor routes raw genres through parseItemGenres (single-object
Subsonic quirk and bare strings). Library-scoped genre browse/counts join
track for raw_json library_id fallback. Statistics keeps empty-genre bucket.

* docs: CHANGELOG and credits for multi-genre local index (PR #1059)

* docs(changelog): credit HiveMind on Discord for multi-genre report (PR #1059)
This commit is contained in:
cucadmuh
2026-06-11 01:02:35 +03:00
committed by GitHub
parent 8593858f3a
commit 5cd01c90ac
55 changed files with 1322 additions and 145 deletions
+19
View File
@@ -1,30 +1,49 @@
import { create } from 'zustand';
import type { GenreTagsInspectDto } from '../api/library';
import type { MigrationInspectReport, MigrationProgressEvent } from '../api/migration';
export type MigrationPhase = 'idle' | 'inspecting' | 'running' | 'completed' | 'error';
export type MigrationStep = 'serverIndex' | 'genreTags';
export interface GenreTagsProgressEvent {
done: number;
total: number;
}
interface MigrationState {
phase: MigrationPhase;
step: MigrationStep | null;
needsMigration: boolean;
inspect: MigrationInspectReport | null;
progress: MigrationProgressEvent | null;
genreTagsInspect: GenreTagsInspectDto | null;
genreTagsProgress: GenreTagsProgressEvent | null;
lastError: string | null;
setPhase: (phase: MigrationPhase) => void;
setStep: (step: MigrationStep | null) => void;
setNeedsMigration: (needsMigration: boolean) => void;
setInspect: (report: MigrationInspectReport | null) => void;
setProgress: (event: MigrationProgressEvent | null) => void;
setGenreTagsInspect: (report: GenreTagsInspectDto | null) => void;
setGenreTagsProgress: (event: GenreTagsProgressEvent | null) => void;
setError: (error: string | null) => void;
}
export const useMigrationStore = create<MigrationState>(set => ({
phase: 'idle',
step: null,
needsMigration: false,
inspect: null,
progress: null,
genreTagsInspect: null,
genreTagsProgress: null,
lastError: null,
setPhase: phase => set({ phase }),
setStep: step => set({ step }),
setNeedsMigration: needsMigration => set({ needsMigration }),
setInspect: inspect => set({ inspect }),
setProgress: progress => set({ progress }),
setGenreTagsInspect: genreTagsInspect => set({ genreTagsInspect }),
setGenreTagsProgress: genreTagsProgress => set({ genreTagsProgress }),
setError: lastError => set({ lastError }),
}));