feat(meta): look up series external ids
This commit is contained in:
@@ -9,8 +9,8 @@ use serde::de::DeserializeOwned;
|
||||
use crate::cache::Cache;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::model::{
|
||||
Movie, MovieSearchResult, RawFindPage, RawMovie, RawSearchPage, RawSeason, RawSeries,
|
||||
RawSeriesSearchPage, Season, Series, SeriesSearchResult,
|
||||
ExternalIds, Movie, MovieSearchResult, RawExternalIds, RawFindPage, RawMovie, RawSearchPage,
|
||||
RawSeason, RawSeries, RawSeriesSearchPage, Season, Series, SeriesSearchResult,
|
||||
};
|
||||
|
||||
/// TMDB's v3 API root.
|
||||
@@ -157,6 +157,20 @@ impl TmdbClient {
|
||||
Ok(raw.into())
|
||||
}
|
||||
|
||||
/// External ids for one TV series, of which the TVDB id is the one this
|
||||
/// project needs (§6.1).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`Error::NotFound`] when TMDB has no such id, otherwise any of [`Error`].
|
||||
pub async fn series_external_ids(&self, tmdb_id: u32) -> Result<ExternalIds> {
|
||||
let path = format!("tv/{tmdb_id}/external_ids");
|
||||
let raw: RawExternalIds = self.get_json(&path, &[]).await?;
|
||||
Ok(ExternalIds {
|
||||
tvdb_id: raw.tvdb_id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Episodes in one TV season.
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@@ -23,4 +23,6 @@ mod model;
|
||||
|
||||
pub use client::{TmdbClient, TmdbClientBuilder, DEFAULT_BASE_URL, DEFAULT_CACHE_TTL};
|
||||
pub use error::{Error, Result};
|
||||
pub use model::{Episode, Movie, MovieSearchResult, Season, Series, SeriesSearchResult};
|
||||
pub use model::{
|
||||
Episode, ExternalIds, Movie, MovieSearchResult, Season, Series, SeriesSearchResult,
|
||||
};
|
||||
|
||||
@@ -218,9 +218,16 @@ pub(crate) struct RawSeries {
|
||||
external_ids: Option<RawExternalIds>,
|
||||
}
|
||||
|
||||
/// The slice of `/tv/{id}/external_ids` this project reads.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ExternalIds {
|
||||
/// The Torznab `tvdbid` parameter (§6.1), when TMDB knows one.
|
||||
pub tvdb_id: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawExternalIds {
|
||||
tvdb_id: Option<u32>,
|
||||
pub(crate) struct RawExternalIds {
|
||||
pub(crate) tvdb_id: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": 82728,
|
||||
"imdb_id": "tt7614372",
|
||||
"freebase_id": "/m/0vpgz0b",
|
||||
"freebase_mid": "/m/0vpgz0b",
|
||||
"tvdb_id": 361391,
|
||||
"tvrage_id": null,
|
||||
"facebook_id": "OfficialBluey",
|
||||
"instagram_id": "officialblueytv",
|
||||
"twitter_id": "officialbluey"
|
||||
}
|
||||
@@ -18,6 +18,7 @@ const MOVIE_CIDADE_DE_DEUS: &str = include_str!("fixtures/movie_cidade_de_deus.j
|
||||
const MOVIE_UNRELEASED: &str = include_str!("fixtures/movie_unreleased.json");
|
||||
const MOVIE_THEATRICAL_ONLY: &str = include_str!("fixtures/movie_theatrical_only.json");
|
||||
const MOVIE_FUTURE_DIGITAL: &str = include_str!("fixtures/movie_future_digital.json");
|
||||
const SERIES_EXTERNAL_IDS: &str = include_str!("fixtures/series_external_ids.json");
|
||||
|
||||
fn client(server: &MockServer) -> TmdbClient {
|
||||
TmdbClient::builder("test-key")
|
||||
@@ -461,3 +462,44 @@ async fn debug_output_does_not_leak_the_api_key() {
|
||||
assert!(!rendered.contains("test-key"), "{rendered}");
|
||||
assert!(rendered.contains("redacted"), "{rendered}");
|
||||
}
|
||||
|
||||
/// §6.1: `t=tvsearch` is addressed by TVDB id, and `/tv/{id}/external_ids` is
|
||||
/// where TMDB keeps the mapping.
|
||||
#[tokio::test]
|
||||
async fn series_external_ids_carries_the_tvdb_id() {
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/3/tv/82728/external_ids"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_string(SERIES_EXTERNAL_IDS))
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let ids = client(&server)
|
||||
.series_external_ids(82_728)
|
||||
.await
|
||||
.expect("lookup succeeds");
|
||||
|
||||
assert_eq!(ids.tvdb_id, Some(361_391));
|
||||
}
|
||||
|
||||
/// A series TMDB has no TVDB id for stays null rather than zero, so callers
|
||||
/// can tell "unknown" from a real id and fall back to the text query.
|
||||
#[tokio::test]
|
||||
async fn series_without_a_tvdb_id_maps_to_none() {
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/3/tv/1/external_ids"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200).set_body_string(r#"{"id": 1, "tvdb_id": null}"#),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let ids = client(&server)
|
||||
.series_external_ids(1)
|
||||
.await
|
||||
.expect("lookup succeeds");
|
||||
|
||||
assert_eq!(ids.tvdb_id, None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user