LoadCultures() in LocalizationManager skipped all iso6392.txt entries
without a two-letter ISO 639-1 code, dropping 302 of 496 languages
including mul (Multiple languages), und (Undetermined), mis (Uncoded
languages), zxx, and many real languages like Achinese, Akkadian, etc.
This caused FindLanguageInfo() to return null for these codes, which
meant:
- ExternalPathParser could not recognize them as valid language codes
in subtitle filenames, so the Language field was never set
- DisplayTitle fell back to the raw code string (e.g. "Mul")
Fix by allowing entries without two-letter codes to be loaded with an
empty TwoLetterISOLanguageName. Also set LocalizedLanguage in
ProbeResultNormalizer for ffprobe-detected streams (the DB repository
path was already handled on master).
Wire up EnableSubtitleExtraction config to MediaEncoder.CanExtractSubtitles
so the setting is actually respected. Gate subtitle extraction check behind
PlayMethod.Transcode since DirectPlay has no competing ffmpeg process.
Add parameterized tests for StreamBuilder.GetSubtitleProfile covering
text and graphical codecs, profile format matching, and extraction
setting behavior. Remove misplaced SubtitleEncoder extraction test.
Replace Enum.Parse(typeof(T), ...) and Enum.GetNames(typeof(T)) with
their generic counterparts Enum.Parse<T>() and Enum.GetNames<T>() in
MediaBrowser.Model/Dlna for improved type safety.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, images with no language were ranked higher (score 3) than
English images (score 2), causing poorly rated languageless images to
be selected over well-rated English alternatives for posters and logos.
Swap the priority so English is preferred over no-language images.
Backdrop images are unaffected as they have their own dedicated sorting.
Add unit tests for OrderByLanguageDescending.
Fixes#13310
StreamInfo.ToUrl() generated URLs like `/master.m3u8?&DeviceId=...` (note `?&`)
because `?` was appended to the path and all parameters started with `&`. When
the first optional parameter (DeviceProfileId) was null, the result was a
malformed query string.
This is harmless when clients hit Jellyfin directly (ASP.NET Core tolerates `?&`),
but when accessed through a reverse proxy that parses and re-serializes the URL
(e.g. Home Assistant ingress via aiohttp/yarl), `?&` becomes `?=&` — introducing
an empty-key query parameter. ParseStreamOptions then crashes on `param.Key[0]`
with IndexOutOfRangeException.
Changes:
- StreamInfo.ToUrl(): Track query start position and replace the first `&` with
`?` after all parameters are appended, producing valid query strings
- ParseStreamOptions: Guard against empty query parameter keys
- Tests: Remove .Replace("?&", "?") workaround that masked the bug
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The DisplayTitle property was using .NET's CultureInfo.GetCultures(NeutralCultures)
to resolve language display names. Since zh-CN is a specific culture (not neutral),
it would fall back to the base 'zh' code, resulting in generic 'Chinese' instead
of 'Chinese (Simplified)'.
This change adds a LocalizedLanguage property to MediaStream that gets populated
via LocalizationManager.FindLanguageInfo() when streams are retrieved from the
database. This leverages Jellyfin's existing iso6392.txt mappings which correctly
map zh-CN to 'Chinese (Simplified)'.
The same pattern is already used for other localized strings like LocalizedDefault
and LocalizedExternal.
The DisplayTitle property was using .NET's CultureInfo.GetCultures(NeutralCultures)
to resolve language display names. Since zh-CN is a specific culture (not neutral),
it would fall back to the base 'zh' code, resulting in generic 'Chinese' instead
of 'Chinese (Simplified)'.
This change adds a LocalizedLanguage property to MediaStream that gets populated
via LocalizationManager.FindLanguageInfo() when streams are retrieved from the
database. This leverages Jellyfin's existing iso6392.txt mappings which correctly
map zh-CN to 'Chinese (Simplified)'.
The same pattern is already used for other localized strings like LocalizedDefault
and LocalizedExternal.
Update password reset to always return the same response structure
Original-merge: 4ad3141875
Merged-by: crobibero <cody@robibe.ro>
Backported-by: Joshua M. Boniface <joshua@boniface.me>