feat(meta): substitute TBA for unnamed episodes
This commit is contained in:
@@ -25,4 +25,5 @@ pub use client::{TmdbClient, TmdbClientBuilder, DEFAULT_BASE_URL, DEFAULT_CACHE_
|
||||
pub use error::{Error, Result};
|
||||
pub use model::{
|
||||
Episode, ExternalIds, FindResults, Movie, MovieSearchResult, Season, Series, SeriesSearchResult,
|
||||
UNTITLED_EPISODE,
|
||||
};
|
||||
|
||||
@@ -140,6 +140,10 @@ pub struct SeasonSummary {
|
||||
pub episode_count: u32,
|
||||
}
|
||||
|
||||
/// Placeholder for an episode TMDB has not named yet. #121's refresh replaces
|
||||
/// it once TMDB fills the title in.
|
||||
pub const UNTITLED_EPISODE: &str = "TBA";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Season {
|
||||
pub number: u32,
|
||||
@@ -285,7 +289,13 @@ impl From<RawSeason> for Season {
|
||||
.into_iter()
|
||||
.map(|episode| Episode {
|
||||
number: episode.episode_number,
|
||||
title: episode.name,
|
||||
// TMDB leaves an unaired episode's name empty; a placeholder
|
||||
// keeps "" out of search, filenames and the compat shim.
|
||||
title: if episode.name.is_empty() {
|
||||
UNTITLED_EPISODE.to_owned()
|
||||
} else {
|
||||
episode.name
|
||||
},
|
||||
air_date: episode.air_date.as_deref().and_then(parse_date),
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -7,7 +7,7 @@ use {reqwest as _, serde as _, serde_json as _, thiserror as _, tracing as _};
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use arr_meta::{Error, TmdbClient};
|
||||
use arr_meta::{Error, TmdbClient, UNTITLED_EPISODE};
|
||||
use chrono::NaiveDate;
|
||||
use wiremock::matchers::{header_exists, method, path, query_param};
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
@@ -464,6 +464,33 @@ async fn debug_output_does_not_leak_the_api_key() {
|
||||
assert!(rendered.contains("redacted"), "{rendered}");
|
||||
}
|
||||
|
||||
/// TMDB leaves an unaired episode's name as `""`. It must not reach the
|
||||
/// library as an empty string — search haystacks, §7.4 filenames and the
|
||||
/// compat shim all treat it as real text (#153) — so it becomes a placeholder
|
||||
/// that a later refresh replaces once TMDB names it.
|
||||
#[tokio::test]
|
||||
async fn season_episode_without_a_name_gets_the_placeholder_title() {
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/3/tv/82728/season/1"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_string(
|
||||
r#"{"season_number": 1, "episodes": [
|
||||
{"episode_number": 1, "name": "Magic Xylophone", "air_date": "2018-10-01"},
|
||||
{"episode_number": 2, "name": "", "air_date": null}
|
||||
]}"#,
|
||||
))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let season = client(&server)
|
||||
.season(82_728, 1)
|
||||
.await
|
||||
.expect("lookup succeeds");
|
||||
|
||||
assert_eq!(season.episodes[0].title, "Magic Xylophone");
|
||||
assert_eq!(season.episodes[1].title, UNTITLED_EPISODE);
|
||||
}
|
||||
|
||||
/// §6.1: `t=tvsearch` is addressed by TVDB id, and `/tv/{id}/external_ids` is
|
||||
/// where TMDB keeps the mapping.
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user