| … | … | @@ -599,7 +599,7 @@ TV_GENRES = [ |
| 599 | 599 | |
| 600 | 600 | MANIFEST = { |
| 601 | 601 | "id": "org.tabertron.vsembed", |
| 602 | "version": "0.18.0", | |
| 602 | "version": "0.18.1", | |
| 603 | 603 | "name": "Tabertron VsEmbed", |
| 604 | 604 | "description": "Self-hosted HLS extractor with TMDB browse + search, played via vidcore", |
| 605 | 605 | "types": ["movie", "series"], |
| … | … | @@ -1797,16 +1797,31 @@ SUBTITLE_TIMEOUT = 25 |
| 1797 | 1797 | # nothing against a set this size and is rude to a host doing us a favour. |
| 1798 | 1798 | SUBTITLE_WORKERS = 8 |
| 1799 | 1799 | |
| 1800 | # wyzie speaks OpenSubtitles' codes, and several are not ISO at all. Verified on | |
| 1801 | # 639988 against the endpoint's own `display` field. Anything not listed is | |
| 1802 | # passed through: an unrecognised code still names a distinct file, and dropping | |
| 1803 | # a subtitle because we did not recognise its label is the worse failure. | |
| 1800 | # wyzie speaks OpenSubtitles' codes, and several are not ISO at all. | |
| 1801 | # | |
| 1802 | # The targets are NOT guessed: they are the codes Jellyfin's own | |
| 1803 | # /Localization/Cultures list answers to, checked 2026-09-04 after a first | |
| 1804 | # attempt shipped `yue` and `zh-Hant` and Jellyfin filed the Cantonese track as | |
| 1805 | # "Undefined". Both are perfectly good language tags; they are simply not in the | |
| 1806 | # table doing the lookup, which is the only thing that matters here. | |
| 1807 | # | |
| 1808 | # pb -> pt-br "Portuguese (Brazil)" | |
| 1809 | # ea -> es-419 "Spanish; Latin" | |
| 1810 | # zt -> zh-tw "Chinese (Traditional)" (zh-Hant is NOT in the table) | |
| 1811 | # zc -> zh-hk "Chinese (Hong Kong)" (yue is NOT in the table) | |
| 1812 | # | |
| 1813 | # `ze` is deliberately absent: Jellyfin knows it natively as "Chinese | |
| 1814 | # (Bilingual)", so mapping it to `zh` threw away a distinction the server was | |
| 1815 | # ready to make. | |
| 1816 | # | |
| 1817 | # Anything not listed is passed through: an unrecognised code still names a | |
| 1818 | # distinct file, and dropping a subtitle because we did not recognise its label | |
| 1819 | # is the worse failure. | |
| 1804 | 1820 | WYZIE_LANG_MAP = { |
| 1805 | 1821 | "pb": "pt-BR", # Portuguese (BR) |
| 1806 | 1822 | "ea": "es-419", # Spanish (LA) |
| 1807 | "zt": "zh-Hant", # Chinese (traditional) | |
| 1808 | "zc": "yue", # Chinese (Cantonese) | |
| 1809 | "ze": "zh", # Chinese bilingual | |
| 1823 | "zt": "zh-TW", # Chinese (traditional) | |
| 1824 | "zc": "zh-HK", # Chinese (Cantonese) | |
| 1810 | 1825 | } |
| 1811 | 1826 | |
| 1812 | 1827 |
| … | … | @@ -114,22 +114,33 @@ def test_the_language_code_is_the_suffix_jellyfin_reads(mp4, fetched): |
| 114 | 114 | assert os.path.basename(paths[0]) == "No Other Choice (2025).en.srt" |
| 115 | 115 | |
| 116 | 116 | |
| 117 | def test_opensubtitles_only_codes_are_mapped_to_real_ones(mp4, fetched): | |
| 118 | """wyzie speaks OpenSubtitles' codes, several of which are not ISO at all. | |
| 119 | Verified on 639988: pb='Portuguese (BR)', ea='Spanish (LA)', | |
| 120 | zt='Chinese (traditional)', zc='Chinese (Cantonese)', ze='Chinese bilingual'.""" | |
| 117 | def test_opensubtitles_only_codes_are_mapped_to_ones_jellyfin_RESOLVES(mp4, fetched): | |
| 118 | """The targets are the codes Jellyfin's /Localization/Cultures answers to. | |
| 119 | ||
| 120 | Not a detail: the first version shipped `yue` and `zh-Hant`, which are | |
| 121 | perfectly good language tags and are simply absent from that table, so | |
| 122 | Jellyfin filed the Cantonese track as "Undefined". Measured, not reasoned. | |
| 123 | """ | |
| 121 | 124 | paths = addon._write_sidecars(mp4, _tracks( |
| 122 | 125 | ("pb", "Portuguese (BR)"), ("ea", "Spanish (LA)"), |
| 123 | 126 | ("zt", "Chinese (traditional)"), ("zc", "Chinese (Cantonese)"))) |
| 124 | 127 | names = sorted(os.path.basename(p) for p in paths) |
| 125 | 128 | assert names == sorted([ |
| 126 | "No Other Choice (2025).pt-BR.srt", | |
| 127 | "No Other Choice (2025).es-419.srt", | |
| 128 | "No Other Choice (2025).zh-Hant.srt", | |
| 129 | "No Other Choice (2025).yue.srt", | |
| 129 | "No Other Choice (2025).pt-BR.srt", # -> Portuguese (Brazil) | |
| 130 | "No Other Choice (2025).es-419.srt", # -> Spanish; Latin | |
| 131 | "No Other Choice (2025).zh-TW.srt", # -> Chinese (Traditional) | |
| 132 | "No Other Choice (2025).zh-HK.srt", # -> Chinese (Hong Kong) | |
| 130 | 133 | ]) |
| 131 | 134 | |
| 132 | 135 | |
| 136 | def test_a_code_jellyfin_already_knows_is_left_alone(mp4, fetched): | |
| 137 | """`ze` is native to Jellyfin as "Chinese (Bilingual)". Mapping it to `zh` | |
| 138 | threw away a distinction the server was ready to make -- so the map must not | |
| 139 | contain it.""" | |
| 140 | paths = addon._write_sidecars(mp4, _tracks(("ze", "Chinese bilingual"))) | |
| 141 | assert os.path.basename(paths[0]) == "No Other Choice (2025).ze.srt" | |
| 142 | ||
| 143 | ||
| 133 | 144 | def test_two_tracks_in_one_language_do_not_overwrite_each_other(mp4, fetched): |
| 134 | 145 | paths = addon._write_sidecars(mp4, _tracks(("en", "English"), ("en", "English"))) |
| 135 | 146 | assert len(set(paths)) == 2, "the second track overwrote the first" |