From a9c73aad39ba99ac9a5eb74a05e4e2a5dac305a3 Mon Sep 17 00:00:00 2001 From: HyperNylium <96999894+HyperNylium@users.noreply.github.com> Date: Sat, 9 May 2026 08:31:21 -0400 Subject: [PATCH 1/2] Add dub/sub listing for hidive and adn --- adn.ts | 35 +++++++++++++++++++++++++++++++++ hidive.ts | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/adn.ts b/adn.ts index d4592e8..a85a06f 100644 --- a/adn.ts +++ b/adn.ts @@ -56,6 +56,11 @@ export default class AnimationDigitalNetwork implements ServiceClass { private polSubStrings: string[] = ['vpl', 'vostpl']; private deuSubStrings: string[] = ['vde', 'vostde']; private fraSubStrings: string[] = ['vf', 'vostf']; + private regions = [ + { code: 'de', vost: 'vostde', dub: 'vde' }, + { code: 'fr', vost: 'vostf', dub: 'vf' }, + { code: 'pl', vost: 'vostpl', dub: 'vpl' } + ] as const; constructor(private debug = false) { this.cfg = yamlCfg.loadCfg(); @@ -284,11 +289,31 @@ export default class AnimationDigitalNetwork implements ServiceClass { episodeIndex--; } else { console.info(` (${episode.id}) [E${episode.shortNumber}] ${episode.number} - ${episode.name}`); + const langs = episode.languages ?? []; + const audios: string[] = []; + const subs: string[] = []; + if (this.regions.some((r) => langs.includes(r.vost))) audios.push('ja'); + for (const r of this.regions) { + if (langs.includes(r.dub)) audios.push(r.code); + if (langs.includes(r.vost) || langs.includes(r.dub)) subs.push(r.code); + } + if (audios.length > 0) console.info(` - Versions: ${audios.join(', ')}`); + if (subs.length > 0) console.info(` - Subtitles: ${subs.join(', ')}`); } episodeIndex++; } for (const special of specials) { console.info(` (Special) (${special.id}) [${special.shortNumber}] ${special.number} - ${special.name}`); + const langs = special.languages ?? []; + const audios: string[] = []; + const subs: string[] = []; + if (this.regions.some((r) => langs.includes(r.vost))) audios.push('ja'); + for (const r of this.regions) { + if (langs.includes(r.dub)) audios.push(r.code); + if (langs.includes(r.vost) || langs.includes(r.dub)) subs.push(r.code); + } + if (audios.length > 0) console.info(` - Versions: ${audios.join(', ')}`); + if (subs.length > 0) console.info(` - Subtitles: ${subs.join(', ')}`); show.value.videos.splice( show.value.videos.findIndex((i) => i.id === special.id), 1 @@ -296,6 +321,16 @@ export default class AnimationDigitalNetwork implements ServiceClass { } for (const nc of ncs) { console.info(` (NC) (${nc.id}) [${nc.shortNumber}] ${nc.number} - ${nc.name}`); + const langs = nc.languages ?? []; + const audios: string[] = []; + const subs: string[] = []; + if (this.regions.some((r) => langs.includes(r.vost))) audios.push('ja'); + for (const r of this.regions) { + if (langs.includes(r.dub)) audios.push(r.code); + if (langs.includes(r.vost) || langs.includes(r.dub)) subs.push(r.code); + } + if (audios.length > 0) console.info(` - Versions: ${audios.join(', ')}`); + if (subs.length > 0) console.info(` - Subtitles: ${subs.join(', ')}`); show.value.videos.splice( show.value.videos.findIndex((i) => i.id === nc.id), 1 diff --git a/hidive.ts b/hidive.ts index da588ca..0c74322 100644 --- a/hidive.ts +++ b/hidive.ts @@ -415,6 +415,26 @@ export default class Hidive implements ServiceClass { season.value.paging.lastSeen = seasonPage.value.paging.lastSeen; season.value.paging.moreDataAvailable = seasonPage.value.paging.moreDataAvailable; } + let subLocales: string[] = []; + const sampleEp = season.value.episodes[0]; + if (sampleEp) { + const epReq = await this.apiReq(`/v4/vod/${sampleEp.id}?includePlaybackDetails=URL`, '', 'auth', 'GET'); + const epData = epReq.ok && epReq.res ? (JSON.parse(await epReq.res.text()) as NewHidiveEpisode) : undefined; + const pbReq = epData?.playerUrlCallback ? await this.req.getData(epData.playerUrlCallback) : undefined; + if (pbReq?.ok && pbReq.res) { + const pbData = JSON.parse(await pbReq.res.text()) as NewHidivePlayback; + subLocales = [ + ...new Set( + (pbData.dash?.[0]?.subtitles ?? []) + .filter((s) => s.format === 'vtt') + .map((s) => langsData.languages.find((l) => l.new_hd_locale == s.language)?.hd_locale) + .filter((l): l is string => typeof l === 'string') + ) + ]; + } else { + console.warn(`Failed to sample subtitles for season ${season.value.id}`); + } + } for (const episode of season.value.episodes) { const datePattern = /\d{1,2}\/\d{1,2}\/\d{2,4} \d{1,2}:\d{2} UTC/; if (episode.title.includes(' - ')) { @@ -426,6 +446,15 @@ export default class Hidive implements ServiceClass { episodes.push(episode); } console.info(` [E.${episode.id}] ${episode.title}`); + const audioLocales = (episode.offlinePlaybackLanguages ?? []) + .map((c) => langsData.languages.find((l) => l.code == c)?.hd_locale) + .filter((l): l is string => typeof l === 'string'); + if (audioLocales.length > 0) { + console.info(` - Versions: ${audioLocales.join(', ')}`); + } + if (subLocales.length > 0) { + console.info(` - Subtitles: ${subLocales.join(', ')}`); + } } } return { isOk: true, value: episodes, series: series.value }; @@ -445,6 +474,26 @@ export default class Hidive implements ServiceClass { season.value.paging.lastSeen = seasonPage.value.paging.lastSeen; season.value.paging.moreDataAvailable = seasonPage.value.paging.moreDataAvailable; } + let subLocales: string[] = []; + const sampleEp = season.value.episodes[0]; + if (sampleEp) { + const epReq = await this.apiReq(`/v4/vod/${sampleEp.id}?includePlaybackDetails=URL`, '', 'auth', 'GET'); + const epData = epReq.ok && epReq.res ? (JSON.parse(await epReq.res.text()) as NewHidiveEpisode) : undefined; + const pbReq = epData?.playerUrlCallback ? await this.req.getData(epData.playerUrlCallback) : undefined; + if (pbReq?.ok && pbReq.res) { + const pbData = JSON.parse(await pbReq.res.text()) as NewHidivePlayback; + subLocales = [ + ...new Set( + (pbData.dash?.[0]?.subtitles ?? []) + .filter((s) => s.format === 'vtt') + .map((s) => langsData.languages.find((l) => l.new_hd_locale == s.language)?.hd_locale) + .filter((l): l is string => typeof l === 'string') + ) + ]; + } else { + console.warn(`Failed to sample subtitles for season ${season.value.id}`); + } + } const episodes: Episode[] = []; for (const episode of season.value.episodes) { const datePattern = /\d{1,2}\/\d{1,2}\/\d{2,4} \d{1,2}:\d{2} UTC/; @@ -457,6 +506,15 @@ export default class Hidive implements ServiceClass { episodes.push(episode); } console.info(` [E.${episode.id}] ${episode.title}`); + const audioLocales = (episode.offlinePlaybackLanguages ?? []) + .map((c) => langsData.languages.find((l) => l.code == c)?.hd_locale) + .filter((l): l is string => typeof l === 'string'); + if (audioLocales.length > 0) { + console.info(` - Versions: ${audioLocales.join(', ')}`); + } + if (subLocales.length > 0) { + console.info(` - Subtitles: ${subLocales.join(', ')}`); + } } const series: NewHidiveSeriesExtra = { ...season.value.series, season: season.value }; return { isOk: true, value: episodes, series: series }; From 45125726002ded4ef872241cd4af65d41d442b02 Mon Sep 17 00:00:00 2001 From: HyperNylium <96999894+HyperNylium@users.noreply.github.com> Date: Wed, 13 May 2026 18:26:32 -0400 Subject: [PATCH 2/2] If countryOfOrigin is China, make dub title and lang Chinese Chinese dubs can be selected with --dubLang zho --- adn.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/adn.ts b/adn.ts index a85a06f..c97e703 100644 --- a/adn.ts +++ b/adn.ts @@ -292,7 +292,7 @@ export default class AnimationDigitalNetwork implements ServiceClass { const langs = episode.languages ?? []; const audios: string[] = []; const subs: string[] = []; - if (this.regions.some((r) => langs.includes(r.vost))) audios.push('ja'); + if (this.regions.some((r) => langs.includes(r.vost))) audios.push((episode.show.countryOfOrigin ?? '').toLowerCase() === 'chine' ? 'zh' : 'ja'); for (const r of this.regions) { if (langs.includes(r.dub)) audios.push(r.code); if (langs.includes(r.vost) || langs.includes(r.dub)) subs.push(r.code); @@ -307,7 +307,7 @@ export default class AnimationDigitalNetwork implements ServiceClass { const langs = special.languages ?? []; const audios: string[] = []; const subs: string[] = []; - if (this.regions.some((r) => langs.includes(r.vost))) audios.push('ja'); + if (this.regions.some((r) => langs.includes(r.vost))) audios.push((special.show.countryOfOrigin ?? '').toLowerCase() === 'chine' ? 'zh' : 'ja'); for (const r of this.regions) { if (langs.includes(r.dub)) audios.push(r.code); if (langs.includes(r.vost) || langs.includes(r.dub)) subs.push(r.code); @@ -324,7 +324,7 @@ export default class AnimationDigitalNetwork implements ServiceClass { const langs = nc.languages ?? []; const audios: string[] = []; const subs: string[] = []; - if (this.regions.some((r) => langs.includes(r.vost))) audios.push('ja'); + if (this.regions.some((r) => langs.includes(r.vost))) audios.push((nc.show.countryOfOrigin ?? '').toLowerCase() === 'chine' ? 'zh' : 'ja'); for (const r of this.regions) { if (langs.includes(r.dub)) audios.push(r.code); if (langs.includes(r.vost) || langs.includes(r.dub)) subs.push(r.code); @@ -584,7 +584,8 @@ export default class AnimationDigitalNetwork implements ServiceClass { for (const streamName in streams.links.streaming) { let audDub: langsData.LanguageItem; if (this.jpnStrings.includes(streamName)) { - audDub = langsData.languages.find((a) => a.code == 'jpn') as langsData.LanguageItem; + const originCode = (data.show.countryOfOrigin ?? '').toLowerCase() === 'chine' ? 'zho' : 'jpn'; + audDub = langsData.languages.find((a) => a.code == originCode) as langsData.LanguageItem; } else if (this.polStrings.includes(streamName)) { audDub = langsData.languages.find((a) => a.code == 'pol') as langsData.LanguageItem; } else if (this.deuStrings.includes(streamName)) { @@ -856,7 +857,7 @@ export default class AnimationDigitalNetwork implements ServiceClass { fs.writeFileSync(`${tsFile}.txt`, compiledChapters.join('\r\n')); files.push({ path: `${tsFile}.txt`, - lang: langsData.languages.find((a) => a.code == 'jpn'), + lang: langsData.languages.find((a) => a.code == ((data.show.countryOfOrigin ?? '').toLowerCase() === 'chine' ? 'zho' : 'jpn')), type: 'Chapters' }); } catch {