fix(anime): thread episode metadata through stream playback and fix seas

- Split anime browser stream titles into series/season/episode fields (episode-metadata.ts) instead of one joined string, so stats grouping, the mpv title, and the Jimaku/TsukiHime modals all get real values instead of a filename-derived "m3u8"
- Fix AniList season resolver to walk through split-cour sequels and specials without counting them as a season, and to surface rate-limit/network failures instead of falling through to a wrong air-order guess
- Add a Season field to the TsukiHime modal and include later seasons in its search query
- Rewrite WebVTT subtitle tracks as SRT before handing them to alass, since alass has no VTT support and streams serve VTT
- Update CHANGELOG and troubleshooting docs
This commit is contained in:
2026-08-15 21:44:51 -07:00
parent 035ff8e7bf
commit c94830531c
25 changed files with 1725 additions and 69 deletions
+1
View File
@@ -188,6 +188,7 @@ function createModalHarness(
setAttribute: () => {},
},
tsukihimeTitleInput: { value: '' },
tsukihimeSeasonInput: { value: '' },
tsukihimeEpisodeInput: { value: '' },
tsukihimeSearchButton: { addEventListener: () => {} },
tsukihimeCloseButton: { addEventListener: () => {} },
+18 -6
View File
@@ -191,16 +191,27 @@ export function createTsukihimeModal(
});
}
function readNumericInput(input: HTMLInputElement): number | null {
if (!input.value) return null;
const parsed = Number.parseInt(input.value, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
function getSearchQuery(): string {
const title = ctx.dom.tsukihimeTitleInput.value.trim();
if (!title) return '';
const episodeValue = ctx.dom.tsukihimeEpisodeInput.value
? Number.parseInt(ctx.dom.tsukihimeEpisodeInput.value, 10)
: null;
if (episodeValue !== null && Number.isFinite(episodeValue)) {
return `${title} ${String(episodeValue).padStart(2, '0')}`;
const season = readNumericInput(ctx.dom.tsukihimeSeasonInput);
const episode = readNumericInput(ctx.dom.tsukihimeEpisodeInput);
// Season 1 is left out on purpose: releases of a first season almost never
// put "S01" in the name, so adding it narrows the search to nothing.
const parts = [title];
if (season !== null && season > 1) {
parts.push(`S${String(season).padStart(2, '0')}`);
}
return title;
if (episode !== null) {
parts.push(String(episode).padStart(2, '0'));
}
return parts.join(' ');
}
async function performTsukihimeSearch(): Promise<void> {
@@ -369,6 +380,7 @@ export function createTsukihimeModal(
.getJimakuMediaInfo()
.then(async (info: JimakuMediaInfo) => {
ctx.dom.tsukihimeTitleInput.value = info.title || '';
ctx.dom.tsukihimeSeasonInput.value = info.season ? String(info.season) : '';
ctx.dom.tsukihimeEpisodeInput.value = info.episode ? String(info.episode) : '';
if (info.confidence === 'high' && info.title && info.episode) {