feat(facets): facet registry, Batch A server params, genre model v2, facets endpoint
Phase 1 + backend of Phase 2 of the faceted-navigation plan. One
declarative facet registry (services/facet_registry.go) drives the
bookshelf filter system; a drift-guard unit test reflects over the
generated SearchMediaItemsUnifiedParams so a registry entry and its
sqlc.narg block can never disagree silently.
- Nine new filter params on SearchMediaItemsUnified: story_arc,
imprint, edition (scalar-fuzzy), age_rating, format_group,
manga_type, reading_direction (exact), explicit, abridged
(presence-bound bools). Comma = OR-within multi-select on every
text facet; AND-across-facets unchanged.
- Genre model v2 (user rule: a subgenre is not a genre, it's a tag):
genre_lexicon.go rebuilt as shelf set + subgenre->shelf map;
pickSmartGenre picks direct shelf > mapped subgenre > Fiction floor
> empty (unknown-only stays tags-only). Type-words (Manga, Comics,
Graphic Novel) join the demote list. Both 'adventure' and 'action &
adventure' are shelves — provider data uses both and the verified
traces pin them.
- Language filter UX: comma elements normalize through the ingest
language map, so display names ('English') match rows stored as ISO
codes ('en'). The language autocomplete now serves display names.
- NEW GET /api/media-items/facets?facet=X&library_id=... — registry-
driven value+count listing (14 static per-column queries, family-lens
join); language values carry display names; bool/range facets 400.
- Family-lens gates: an explicit hidden-library library_id is now a
403 on the search endpoints (was a silent empty set) — JSON branch
and HTMX grid partial both.
- handlers.ParseMediaSearchParams is the SINGLE param parser for the
search JSON endpoint and the HTMX grid partial; the HTML branch
previously dropped publisher_filter and narrator_filter silently.
Pins: docs/contract_pins.md changelog entries (additive request
params; new facets endpoint). Tests: facet_registry_test.go (shape +
SQL drift guard + library-type union), series_parsing_test.go genre-v2
cases, facets_integration_test.go (param filtering e2e, counts,
language display, 403s) — container runner.
This commit is contained in:
1 parent
7537063cb7
commit
7e586df48a
13 files changed
+2297
-283
No files matched your search
@@ -217,6 +217,36 @@ func normalizeLanguageCode(v string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// languageCodeToName — canonical English display name per ISO code,
|
||||
// mirroring languageNameToCode (server owns display; the column stores
|
||||
// ISO 639-1).
|
||||
var languageCodeToName = map[string]string{
|
||||
"en": "English", "es": "Spanish", "fr": "French", "de": "German",
|
||||
"it": "Italian", "pt": "Portuguese", "nl": "Dutch", "ru": "Russian",
|
||||
"uk": "Ukrainian", "pl": "Polish", "cs": "Czech", "sk": "Slovak",
|
||||
"sv": "Swedish", "no": "Norwegian", "da": "Danish", "fi": "Finnish",
|
||||
"is": "Icelandic", "hu": "Hungarian", "ro": "Romanian", "bg": "Bulgarian",
|
||||
"el": "Greek", "tr": "Turkish", "ar": "Arabic", "he": "Hebrew",
|
||||
"fa": "Persian", "hi": "Hindi", "bn": "Bengali", "ur": "Urdu",
|
||||
"th": "Thai", "vi": "Vietnamese", "id": "Indonesian", "ms": "Malay",
|
||||
"ja": "Japanese", "ko": "Korean", "zh": "Chinese", "sw": "Swahili",
|
||||
"af": "Afrikaans", "ca": "Catalan", "hr": "Croatian", "sr": "Serbian",
|
||||
"sl": "Slovenian", "lt": "Lithuanian", "lv": "Latvian", "et": "Estonian",
|
||||
"ka": "Georgian", "hy": "Armenian", "cy": "Welsh", "ga": "Irish",
|
||||
"eu": "Basque", "gl": "Galician",
|
||||
}
|
||||
|
||||
// LanguageDisplayName renders a stored language value for DISPLAY.
|
||||
// Returns "" when the value is not a known code — callers keep the raw
|
||||
// value then (legacy verbatim rows like "Pig Latin" display as
|
||||
// themselves).
|
||||
func LanguageDisplayName(code string) string {
|
||||
if name, ok := languageCodeToName[strings.ToLower(strings.TrimSpace(code))]; ok {
|
||||
return name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var languageNameToCode = map[string]string{
|
||||
"english": "en", "spanish": "es", "español": "es", "french": "fr",
|
||||
"français": "fr", "german": "de", "deutsch": "de", "italian": "it",
|
||||
|
||||
Reference in new issue
Block a user