feat: add batch metadata jobs, series filters, and translate backoffice to French

- Add metadata_batch job type with background processing via tokio::spawn
- Auto-apply metadata only when single result at 100% confidence
- Support primary + fallback provider per library, "none" to opt out
- Add batch report/results API endpoints and job detail UI
- Add series_status and has_missing filters to both series listing pages
- Add GET /series/statuses endpoint for dynamic filter options
- Normalize series_metadata status values (migration 0036)
- Hide ComicVine provider tab when no API key configured
- Translate entire backoffice UI from English to French

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 18:26:44 +01:00
parent 9a8c1577af
commit b955c2697c
46 changed files with 2161 additions and 379 deletions

View File

@@ -590,7 +590,7 @@ fn row_to_link_dto(row: &sqlx::postgres::PgRow) -> ExternalMetadataLinkDto {
}
}
async fn get_provider_for_library(state: &AppState, library_id: Uuid) -> Result<String, ApiError> {
pub(crate) async fn get_provider_for_library(state: &AppState, library_id: Uuid) -> Result<String, ApiError> {
// Check library-level provider first
let row = sqlx::query("SELECT metadata_provider FROM libraries WHERE id = $1")
.bind(library_id)
@@ -623,7 +623,7 @@ async fn get_provider_for_library(state: &AppState, library_id: Uuid) -> Result<
Ok("google_books".to_string())
}
async fn load_provider_config(
pub(crate) async fn load_provider_config(
state: &AppState,
provider_name: &str,
) -> metadata_providers::ProviderConfig {
@@ -661,7 +661,7 @@ async fn load_provider_config(
config
}
async fn sync_series_metadata(
pub(crate) async fn sync_series_metadata(
state: &AppState,
library_id: Uuid,
series_name: &str,
@@ -846,7 +846,7 @@ fn normalize_series_status(raw: &str) -> String {
}
}
async fn sync_books_metadata(
pub(crate) async fn sync_books_metadata(
state: &AppState,
link_id: Uuid,
library_id: Uuid,