Fetch TVDB's English-translated episode names instead of original-language

Plain /episodes/default returns names in the show's original airing
language — for a lot of anime that's Japanese with no English name at all,
which is what was ending up embedded in generated filenames. TVDB carries
real crowd-sourced English translations at /episodes/default/eng (same
numbering/air-date fields, just a better name) — try that first and only
fall back to the original-language endpoint if a show has no English data.
This commit is contained in:
Breadway 2026-07-21 20:09:24 +08:00
parent dcf9ee241c
commit 17d82b84c2

View file

@ -110,8 +110,6 @@ impl TvdbClient {
} }
pub async fn episodes(&self, series_id: &str) -> Result<Vec<EpisodeInfo>> { pub async fn episodes(&self, series_id: &str) -> Result<Vec<EpisodeInfo>> {
let token = self.token().await?;
#[derive(Deserialize)] #[derive(Deserialize)]
struct EpisodesResponse { struct EpisodesResponse {
data: EpisodesData, data: EpisodesData,
@ -131,20 +129,47 @@ impl TvdbClient {
aired: Option<String>, aired: Option<String>,
} }
let resp: EpisodesResponse = self // TVDB's plain `/episodes/default` returns names in the show's
.client // original airing language — for a lot of anime that's Japanese,
.get(format!( // with no English name at all (verified live: entire shows came
"https://api4.thetvdb.com/v4/series/{series_id}/episodes/default" // back Japanese-only). `/episodes/default/eng` is the same episode
)) // list (same numbering/air-date fields) but with TVDB's own
// crowd-sourced English translation substituted in for `name`
// wherever one exists — a real translation, not a guess, so it's
// tried first and only falls back to the original-language
// endpoint if TVDB has no English data for this show at all.
let token = self.token().await?;
let fetch = |url: String| {
let client = self.client.clone();
let token = token.clone();
async move {
client
.get(url)
.bearer_auth(token) .bearer_auth(token)
.send() .send()
.await .await
.context("tvdb episodes request failed")? .context("tvdb episodes request failed")?
.error_for_status() .error_for_status()
.context("tvdb episodes returned an error status")? .context("tvdb episodes returned an error status")?
.json() .json::<EpisodesResponse>()
.await .await
.context("tvdb episodes response was not valid JSON")?; .context("tvdb episodes response was not valid JSON")
}
};
let resp = match fetch(format!(
"https://api4.thetvdb.com/v4/series/{series_id}/episodes/default/eng"
))
.await
{
Ok(resp) => resp,
Err(_) => {
fetch(format!(
"https://api4.thetvdb.com/v4/series/{series_id}/episodes/default"
))
.await?
}
};
Ok(resp Ok(resp
.data .data