Extracts the inline DDG language map from FixYouTube.cs into a dedicated TrailerLanguageMap.cs file
and expands coverage from 22 languages to all 93 Jellyfin-supported locales.
## Changes
- New file: TrailerLanguageMap.cs — static class with all Jellyfin language codes mapped to
DDG search keywords (English name + native name per language)
- FixYouTube.cs: inline langMap replaced with __LANG_MAP__ placeholder, injected via .Replace()
from TrailerLanguageMap.JsObject at build time
## Why
Previously, if a user had a language not in the 22-entry inline map (e.g., Hindi, Bengali, Georgian,
Kazakh, etc.), the DDG search would fall back to an empty string — searching just "{title} Trailer
site:youtube.com" with no language qualifier. This made language matching impossible for those users.
Now every Jellyfin-supported language resolves to proper search keywords. For example:
- hi -> "Hindi हिन्दी"
- ka -> "Georgian ქართული"
- bn -> "Bengali বাংলা"
- kk -> "Kazakh Қазақша"
Fixes a long-standing Jellyfin upstream bug (jellyfin/jellyfin#12817) where movies configured with
non-English metadata languages (German, Spanish, French, etc.) show no trailer button because TMDB
returns empty video data for non-English language queries.
## Problem
When Jellyfin fetches metadata with e.g. language=de, TMDB returns 0 trailers even though English
trailers exist in their database. Jellyfin makes no fallback to English. The issue was closed upstream
as "not planned" — unfixed for 2-3 years, affecting every non-English Jellyfin user.
## Solution
Client-side trailer fallback system built into the existing YouTube fix (V16 -> V17). No Jellyfin
server changes required.
When a movie detail page has no trailer button:
1. TMDB: search for trailer in user's language (via include_video_language parameter)
2. DuckDuckGo Lite: search "{title} Trailer {language} site:youtube.com", match by title
3. TMDB: English trailer (cached from step 1, no extra API call)
4. DuckDuckGo Lite: English fallback (cached from step 2, no extra API call)
Max 2 external HTTP requests (1x TMDB + 1x DDG). Steps 3+4 use cached results.
## Why DuckDuckGo Lite instead of Google/YouTube search
Google and YouTube aggressively block automated requests with CAPTCHAs, rate limiting, and bot
detection. DuckDuckGo Lite (lite.duckduckgo.com) returns plain HTML with no JavaScript, no API key
required, no CAPTCHAs, and no bot blocking. DDG Lite has been stable for years with minimal changes
to its HTML structure, making it a reliable fallback for trailer discovery.
## Technical details
- Language detected from Jellyfin user settings (document.documentElement.lang), not TV system language
- TMDB API key extracted at runtime from Jellyfin server plugin configuration (GET /Plugins -> GET
/Plugins/{id}/Configuration), falls back to Jellyfin's default key from TmdbUtils.cs
- DDG results parsed with title extraction: language-matched results prioritized, non-matched results
cached as English fallback
- Trailer button injected as native Jellyfin UI element (same CSS classes, Material Icons)
- Fullscreen overlay player reuses existing player.html bridge
- Closable via Tizen back button, Escape, Backspace, or video end
- httpsGet helper with 8s timeout and double-callback guard
- 22 non-English languages supported in DDG language map
## Fallback flow
```
Movie detail page loaded
|
+-- Trailer button exists? -> Yes -> normal existing fix (CustomPlayer)
|
+-- No -> Service: /trailer?tmdbId=X&lang={Jellyfin language}&tmdbKey={runtime key}
|
+-- 1. TMDB: trailer in user's language?
| -> Found -> play with CustomPlayer
|
+-- 2. DDG Lite: "{title} Trailer {language} site:youtube.com"
| -> Language matched in title? -> play with CustomPlayer
|
+-- 3. TMDB: English trailer (cached from step 1, no extra call)
| -> Found -> play with CustomPlayer
|
+-- 4. DDG Lite: English fallback (cached from step 2, no extra call)
| -> Found -> play with CustomPlayer
|
+-- Nothing found -> no trailer available
```