feat: quality (#1) and main-language (#6) overlay badges#57
Open
PNRxA wants to merge 12 commits into
Open
Conversation
Adds two new non-rating overlay badges, drawn after the rating badges (bypassing ratings order/exclude/limit) and configurable per request and per key/global. Quality (#1) — caller-supplied, since no quality metadata exists server-side: ?quality=4k,1080p,720p,hdr,dv (stackable) ?quality_style=text|logo (text chip, or brand logo on a white plate) Main language (#6) — derived from TMDB original_language: ?lang_icon=off|flag|text (country flag, or uppercase ISO code) ?lang_code=<iso> (optional override) - New QualityTier/QualityStyle/LangIcon enums + validators + cache tokens (db.rs) - OverlayBadge renderer (Text/Logo/Flag) reusing badge styling (badge.rs) - icons.rs: bundled flag set (flagcdn) + quality logos (Wikimedia), keyed lookups with graceful text fallback; scripts/fetch-{flags,quality-logos}.sh - original_language threaded onto ResolvedId (inherited from the show for episodes) - quality_style/lang_icon persisted (entity column + migration + DTOs); quality/lang_code are per-request only; both folded into the cache key - Frontend: free "Try it out" card controls + persisted settings form - Tests: enum/cache-suffix units, icon decode/lookup, HTTP accept/reject; docs
The quality (#1) and main-language (#6) overlay badges can now be placed at their own anchor positions, independent of the rating badges and of each other. ?quality_position=bc|tc|l|r|tl|tr|bl|br (default tr) ?lang_position=bc|tc|l|r|tl|tr|bl|br (default tl) - New OverlaySpec { quality, quality_position, language, language_position } threaded through generate; each group is laid out at its own anchor in poster/backdrop/episode (logos still stack all badges below the logo, so positions are ignored there) - quality_position/lang_position persisted (entity column + migration + DTOs) and query-overridable; both folded into the cache key (.qp{pos}/.lp{pos}) - Frontend: position selects in the free card + settings form - Tests (accept/reject + cache-key) and README updated
The overlay-badge 'Quality badge position' / 'Main-language badge position' labels also contain 'Badge position', so the loose substring locator matched 3 elements. Match the poster label exactly.
… badges
When multiple quality tiers are shown, their layout direction can now be set
independently of the rating badges:
?quality_direction=d|h|v (default d = auto, same as the rating badges)
- New quality_direction (BadgeDirection) setting; Default (auto) resolves to the
image's rating badge_direction via BadgeDirection::resolve_or
- Carried on OverlaySpec; applied to the quality group's layout in
poster/backdrop/episode (logos stack below the logo, unaffected)
- Persisted (entity column + migration + DTOs) and query-overridable; encoded in
the cache key as .qd{dir} only when overridden from auto
- Frontend: quality direction select in the free card + settings form
- Tests (accept/reject + cache-key) and README updated
…at corners) Previously "auto" (Default) copied the rating badges' raw direction, which on posters is always horizontal, so quality at the default top-right rendered as a row. Now auto resolves from the quality badge's own anchor via BadgeDirection::resolve(quality_position): a column at corner/side positions, a row at top/bottom-center — so the default top-right quality stack is a column. An explicit ?quality_direction=h|v still overrides. (Removed the now-unused BadgeDirection::resolve_or helper.)
Show the main-language badge for every title except the language(s) you
already understand:
?lang_exclude=en (hide it on English titles; show on everything else)
?lang_exclude=en,es (comma-separated)
- New persisted, query-overridable lang_exclude (Arc<str>, default empty),
validated like a language list; matched against the title's main language and
folded into the cache key (.lx{codes}, present only when the badge is on)
- serve skips the language badge when the resolved/overridden code is excluded
- Centralized language canonicalization (db::canonical_lang): strips
region/script, maps TMDB aliases (cn->zh), and drops "no language" sentinels
(xx/und/zxx/mul). Applied to the flag lookup, exclusion, and cache token so
they stay consistent — fixes cn titles showing a "CN" text badge / escaping a
zh exclude, and suppresses ugly XX badges
- Full persistence + DTO lockstep; frontend input (settings form + free card)
- Tests (canonical_lang, exclusion incl. alias, accept/reject, cache key) + docs
Found via an adversarial review pass of the lang_exclude change.
- id: always fetch parent show so episode language badge inherits the show's original_language even when the episode has its own still - db: canonicalize lang_code before encoding the overlay cache key so equivalent forms (pt-BR/PT-BR/pt, cn/zh) share one cache entry - db/serve: logos ignore anchor positions and quality direction, so use a logo-specific overlay token that omits .qp/.lp/.qd to avoid spurious cache entries for byte-identical logo renders - web: emit lang_icon=off when explicitly chosen so it overrides a non-off server default - web: add missing quality_direction field to RenderSettings interface
Episodes no longer render the quality (#1) or main-language (#6) overlay badges — only posters, backdrops, and logos do. - render_episode_sync no longer takes an OverlaySpec or runs the quality/language layout; generate_episode stops building it - The episode cache key omits the `.q*`/`.li*` overlay token (it had no effect) - The shared query params (quality/quality_style/lang_icon/lang_code/ lang_exclude/positions/direction) are still accepted on the episode endpoint but ignored, consistent with other type-specific params - README applicability updated; test asserts episode requests with these params still return non-400
Posters and backdrops now have independent overlay-badge anchor positions, with their own defaults — backdrops default to quality top-left / language bottom-left; posters keep quality top-right / language top-left. - Split quality_position/lang_position into poster_/backdrop_ variants (4 persisted settings) with per-type defaults; entity columns + migrations, parse_global/get_effective/Upsert, admin/api_keys/free-key DTOs all updated - overlay_cache_suffix(settings, kind) picks the per-type anchors (logos still omit anchor tokens; episodes render no overlay) - build_overlay_badges(settings, resolved, kind) selects the per-type anchors - The ?quality_position= / ?lang_position= query params map to the requested image type (poster vs backdrop), like ?position= - Frontend: per-type position selects in the settings form; the free card holds per-type state and emits the param for the selected type - Tests (per-type cache key + accept/reject) and README updated
The language badge can now be turned on/off (and set to flag vs text) independently per image type: poster, logo, and backdrop each have their own lang_icon. Episodes never show it. - Split lang_icon into poster_/logo_/backdrop_lang_icon (default off each); entity columns + migrations, RenderSettings/Default/parse_global/get_effective/ Upsert, admin/api_keys/free-key DTOs all updated - overlay_cache_suffix(settings, kind) + build_overlay_badges(settings, resolved, kind) select the per-type lang_icon - ?lang_icon= query param maps to the requested image type, like ?label_style= - Frontend: per-type lang_icon select in each section of the settings form; the free card holds per-type state and gates lang_code/lang_exclude/lang_position on the selected type's lang_icon - Tests (per-type cache key) and README updated
# Conflicts: # README.md # api/src/image/generate.rs # api/tests/query_overrides.rs # web/src/views/SettingsView.vue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1
Closes #6
Adds two new non-rating overlay badges, drawn after the rating badges (so they bypass
ratings_limit/ratings_order/ratings_exclude), flowing through the existing row/stack/split layout and honoring badge shape/background/size. Both are configurable per request and persisted per-key/global, and are folded into the cache key + CDN settings-hash.#1 Quality icons
There is no quality/resolution metadata anywhere server-side (TMDB/OMDb/MDBList/Trakt/Fanart), so quality is caller-supplied by the addon/media server that knows the stream — modeled like the existing
?lang=/?textless=flow (no auto-detection).?quality=4k,1080p,720p,hdr,dv— stackable (e.g.?quality=4k,dv)?quality_style=text|logo— crisp text chips (4K/HDR/…), or real brand logos (Ultra HD Blu-ray, Full HD 1080, HD ready, HDR10+, Dolby Vision) rendered on a white plate so any logo colour stays legible. A tier without a bundled logo falls back to text.#6 Main-language icons
Auto-derived from TMDB
original_language(inherited from the parent show for episodes).?lang_icon=off|flag|text— a country flag, or the uppercase ISO code?lang_code=<iso>— optional override (independent of?lang=, which selects artwork language)en→US,pt→Portugal, etc. (a language code isn't a country —textmode and?lang_codeare the unambiguous escape hatches). Languages without a mapped flag fall back to text.Implementation notes
QualityTier/QualityStyle/LangIconenums + validators + compact cache tokens (db.rs)OverlayBadgerenderer (Text / Logo / Flag) reusing the existing badge styling primitives (badge.rs); merged into eachrender_*_syncbadge list (generate.rs)icons.rs: bundled flag set (public-domain, from flagcdn) + quality logos (Wikimedia Commons), keyed lookups with graceful text fallback. Reproducible viascripts/fetch-flags.shandscripts/fetch-quality-logos.sh.original_languagethreaded ontoResolvedIdquality_style/lang_iconpersisted (entity column + idempotent migration + admin/per-key/free-key DTOs);quality/lang_codeare per-request only. Defaults keep existing cache keys unchanged (no migration of cached images).Testing
cargo test— all suites green, incl. new enum/cache-suffix units, icon decode/lookup, and HTTP accept/reject/stacking/zero-ratings integration testscargo build --release --features test-support(CI parity)npx vitest run(315 passed) andnpm run build-only(CI gates)Docs
README query-param list + applicability note, Cache Architecture suffix/short-value tables and an example key;
scripts/README.mdentries for the two fetch scripts.