feat(api): carry poster and rating through search and lists
Widen the unified search so both §9.2 halves can render a thumbnail and a rating: TMDB results gain vote_average/vote_count from the search bodies, library results read the #145 artwork columns, and episodes take their series' art. GET /api/movies and /api/series gain the same two fields for the #151 grid. Mirrors in web updated.
This commit is contained in:
@@ -12,7 +12,9 @@ use serde::{Deserialize, Serialize};
|
||||
const RELEASE_TYPE_DIGITAL: u8 = 4;
|
||||
|
||||
/// One row of a TMDB search.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
///
|
||||
/// `vote_average` is the one float, so this is `PartialEq` only.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MovieSearchResult {
|
||||
/// TMDB's own id, the key everything else hangs off.
|
||||
pub tmdb_id: u32,
|
||||
@@ -30,6 +32,11 @@ pub struct MovieSearchResult {
|
||||
pub overview: Option<String>,
|
||||
/// Path fragment, not a URL. TMDB's image base is a separate concern.
|
||||
pub poster_path: Option<String>,
|
||||
/// TMDB's rating, out of 10, when TMDB has votes for it; zero normalised
|
||||
/// away like [`Movie::vote_average`].
|
||||
pub vote_average: Option<f64>,
|
||||
/// How many votes the rating rests on.
|
||||
pub vote_count: u32,
|
||||
}
|
||||
|
||||
impl MovieSearchResult {
|
||||
@@ -86,6 +93,8 @@ pub struct Movie {
|
||||
/// artwork (§9.6). TMDB sends `0` where "no rating yet" is meant, so a
|
||||
/// zero rating is normalised away here rather than stored as one.
|
||||
pub vote_average: Option<f64>,
|
||||
/// How many votes the rating rests on.
|
||||
pub vote_count: u32,
|
||||
}
|
||||
|
||||
impl Movie {
|
||||
@@ -106,7 +115,7 @@ impl Movie {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SeriesSearchResult {
|
||||
pub tmdb_id: u32,
|
||||
pub title: String,
|
||||
@@ -114,6 +123,10 @@ pub struct SeriesSearchResult {
|
||||
pub first_air_date: Option<NaiveDate>,
|
||||
pub overview: Option<String>,
|
||||
pub poster_path: Option<String>,
|
||||
/// Zero normalised away, like [`Movie::vote_average`].
|
||||
pub vote_average: Option<f64>,
|
||||
/// How many votes the rating rests on.
|
||||
pub vote_count: u32,
|
||||
}
|
||||
|
||||
impl SeriesSearchResult {
|
||||
@@ -141,6 +154,8 @@ pub struct Series {
|
||||
/// TMDB's rating, out of 10, when TMDB has votes for it. Stored with the
|
||||
/// artwork (§9.6); zero is normalised away like [`Movie::vote_average`].
|
||||
pub vote_average: Option<f64>,
|
||||
/// How many votes the rating rests on.
|
||||
pub vote_count: u32,
|
||||
pub seasons: Vec<SeasonSummary>,
|
||||
}
|
||||
|
||||
@@ -310,6 +325,10 @@ pub(crate) struct RawSeriesSearchResult {
|
||||
overview: Option<String>,
|
||||
#[serde(default)]
|
||||
poster_path: Option<String>,
|
||||
#[serde(default)]
|
||||
vote_average: f64,
|
||||
#[serde(default)]
|
||||
vote_count: u32,
|
||||
}
|
||||
|
||||
impl From<RawSeriesSearchResult> for SeriesSearchResult {
|
||||
@@ -321,6 +340,8 @@ impl From<RawSeriesSearchResult> for SeriesSearchResult {
|
||||
first_air_date: raw.first_air_date.as_deref().and_then(parse_date),
|
||||
overview: non_empty(raw.overview),
|
||||
poster_path: non_empty(raw.poster_path),
|
||||
vote_average: rating(raw.vote_average),
|
||||
vote_count: raw.vote_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +366,8 @@ pub(crate) struct RawSeries {
|
||||
#[serde(default)]
|
||||
vote_average: f64,
|
||||
#[serde(default)]
|
||||
vote_count: u32,
|
||||
#[serde(default)]
|
||||
seasons: Vec<RawSeasonSummary>,
|
||||
#[serde(default)]
|
||||
external_ids: Option<RawExternalIds>,
|
||||
@@ -382,6 +405,7 @@ impl From<RawSeries> for Series {
|
||||
poster_path: non_empty(raw.poster_path),
|
||||
backdrop_path: non_empty(raw.backdrop_path),
|
||||
vote_average: rating(raw.vote_average),
|
||||
vote_count: raw.vote_count,
|
||||
seasons: raw
|
||||
.seasons
|
||||
.into_iter()
|
||||
@@ -436,7 +460,7 @@ impl From<RawSeason> for Season {
|
||||
/// What one `IMDb` id resolved to. An id names one title, so at most one of
|
||||
/// the two lists is non-empty — but TMDB answers both kinds in the same
|
||||
/// response, and both are surfaced rather than filtered here.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct FindResults {
|
||||
pub movies: Vec<MovieSearchResult>,
|
||||
pub series: Vec<SeriesSearchResult>,
|
||||
@@ -465,6 +489,10 @@ pub(crate) struct RawSearchResult {
|
||||
overview: Option<String>,
|
||||
#[serde(default)]
|
||||
poster_path: Option<String>,
|
||||
#[serde(default)]
|
||||
vote_average: f64,
|
||||
#[serde(default)]
|
||||
vote_count: u32,
|
||||
}
|
||||
|
||||
impl From<RawSearchResult> for MovieSearchResult {
|
||||
@@ -477,6 +505,8 @@ impl From<RawSearchResult> for MovieSearchResult {
|
||||
release_date: raw.release_date.as_deref().and_then(parse_date),
|
||||
overview: non_empty(raw.overview),
|
||||
poster_path: non_empty(raw.poster_path),
|
||||
vote_average: rating(raw.vote_average),
|
||||
vote_count: raw.vote_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,6 +541,8 @@ pub(crate) struct RawMovie {
|
||||
#[serde(default)]
|
||||
vote_average: f64,
|
||||
#[serde(default)]
|
||||
vote_count: u32,
|
||||
#[serde(default)]
|
||||
release_dates: Option<RawReleaseDates>,
|
||||
}
|
||||
|
||||
@@ -572,6 +604,7 @@ impl From<RawMovie> for Movie {
|
||||
poster_path: non_empty(raw.poster_path),
|
||||
backdrop_path: non_empty(raw.backdrop_path),
|
||||
vote_average: rating(raw.vote_average),
|
||||
vote_count: raw.vote_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user