api: derive status from episode season numbers
with_status drops its seasons slice for the same reason as derive_series_status; tv_by_series no longer loads seasons at all. Offline query data regenerated.
This commit is contained in:
@@ -196,6 +196,7 @@ struct EpisodeRow {
|
||||
series_id: i64,
|
||||
id: i64,
|
||||
season_id: i64,
|
||||
season_number: i64,
|
||||
number: i64,
|
||||
title: String,
|
||||
air_date: Option<String>,
|
||||
@@ -257,6 +258,7 @@ fn core_episode(row: &EpisodeRow) -> arr_core::Episode {
|
||||
arr_core::Episode {
|
||||
id: EpisodeId(row.id),
|
||||
season_id: SeasonId(row.season_id),
|
||||
season_number: u16::try_from(row.season_number).unwrap_or_default(),
|
||||
number: u16::try_from(row.number).unwrap_or_default(),
|
||||
title: row.title.clone(),
|
||||
air_date: air_date(row.air_date.as_deref()),
|
||||
@@ -277,31 +279,14 @@ fn status_name(status: SeriesStatus) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn core_season(id: i64, series_id: i64, number: i64) -> arr_core::Season {
|
||||
arr_core::Season {
|
||||
id: SeasonId(id),
|
||||
series_id: SeriesId(series_id),
|
||||
number: u16::try_from(number).unwrap_or_default(),
|
||||
tracked: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_status(
|
||||
row: &SeriesRow,
|
||||
seasons: &[arr_core::Season],
|
||||
episodes: &[arr_core::Episode],
|
||||
now: SystemTime,
|
||||
) -> Series {
|
||||
fn with_status(row: &SeriesRow, episodes: &[arr_core::Episode], now: SystemTime) -> Series {
|
||||
// §4.2: season 0 is invisible to status, so it stays out of the counters
|
||||
// too. A series reading `complete` next to `42/52 eps` is the confusion
|
||||
// this avoids. `UNIQUE (series_id, number)` means there is at most one.
|
||||
let specials = seasons
|
||||
.iter()
|
||||
.find(|season| season.number == 0)
|
||||
.map(|season| season.id);
|
||||
// this avoids. The season number rides on each episode (#131), so no
|
||||
// parallel slice can be forgotten.
|
||||
let wanted = episodes
|
||||
.iter()
|
||||
.filter(|episode| episode.wanted && specials != Some(episode.season_id));
|
||||
.filter(|episode| episode.wanted && episode.season_number != 0);
|
||||
let available = wanted
|
||||
.clone()
|
||||
.filter(|episode| episode.state == MediaState::Available);
|
||||
@@ -317,53 +302,26 @@ fn with_status(
|
||||
overrides: row.overrides.clone(),
|
||||
upstream_ended: row.upstream_ended,
|
||||
blocked: row.blocked,
|
||||
status: status_name(derive_series_status(
|
||||
&core_series(row),
|
||||
seasons,
|
||||
episodes,
|
||||
now,
|
||||
))
|
||||
.to_owned(),
|
||||
status: status_name(derive_series_status(&core_series(row), episodes, now)).to_owned(),
|
||||
wanted_episodes: i64::try_from(wanted.count()).unwrap_or(i64::MAX),
|
||||
available_episodes: i64::try_from(available.count()).unwrap_or(i64::MAX),
|
||||
}
|
||||
}
|
||||
|
||||
/// Every season and episode in the library, keyed by the series they belong to.
|
||||
/// Every episode in the library, keyed by the series it belongs to.
|
||||
///
|
||||
/// One query each rather than one per series: the whole table is a few thousand
|
||||
/// One query rather than one per series: the whole table is a few thousand
|
||||
/// rows for a single household (§10), and the status of every listed series
|
||||
/// needs all of them anyway.
|
||||
async fn tv_by_series(
|
||||
state: &AppState,
|
||||
) -> Result<
|
||||
(
|
||||
HashMap<i64, Vec<arr_core::Season>>,
|
||||
HashMap<i64, Vec<arr_core::Episode>>,
|
||||
),
|
||||
ApiError,
|
||||
> {
|
||||
let season_rows = sqlx::query!(
|
||||
r#"SELECT id AS "id!: i64", series_id AS "series_id!: i64", number AS "number!: i64" FROM seasons"#
|
||||
)
|
||||
.fetch_all(pool(state)?)
|
||||
.await?;
|
||||
async fn tv_by_series(state: &AppState) -> Result<HashMap<i64, Vec<arr_core::Episode>>, ApiError> {
|
||||
let rows = sqlx::query_as!(
|
||||
EpisodeRow,
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", se.number AS "season_number!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
FROM episodes e JOIN seasons se ON se.id = e.season_id"#
|
||||
)
|
||||
.fetch_all(pool(state)?)
|
||||
.await?;
|
||||
|
||||
let mut seasons: HashMap<i64, Vec<arr_core::Season>> = HashMap::new();
|
||||
for row in &season_rows {
|
||||
seasons.entry(row.series_id).or_default().push(core_season(
|
||||
row.id,
|
||||
row.series_id,
|
||||
row.number,
|
||||
));
|
||||
}
|
||||
let mut episodes: HashMap<i64, Vec<arr_core::Episode>> = HashMap::new();
|
||||
for row in &rows {
|
||||
episodes
|
||||
@@ -371,7 +329,7 @@ async fn tv_by_series(
|
||||
.or_default()
|
||||
.push(core_episode(row));
|
||||
}
|
||||
Ok((seasons, episodes))
|
||||
Ok(episodes)
|
||||
}
|
||||
|
||||
async fn load_series_row(state: &AppState, id: i64) -> Result<SeriesRow, ApiError> {
|
||||
@@ -383,26 +341,16 @@ async fn load_series_row(state: &AppState, id: i64) -> Result<SeriesRow, ApiErro
|
||||
|
||||
async fn load_series(state: &AppState, id: i64) -> Result<Series, ApiError> {
|
||||
let row = load_series_row(state, id).await?;
|
||||
let season_rows = sqlx::query!(
|
||||
r#"SELECT id AS "id!: i64", series_id AS "series_id!: i64", number AS "number!: i64" FROM seasons WHERE series_id = ?"#,
|
||||
id
|
||||
)
|
||||
.fetch_all(pool(state)?)
|
||||
.await?;
|
||||
let seasons: Vec<_> = season_rows
|
||||
.iter()
|
||||
.map(|row| core_season(row.id, row.series_id, row.number))
|
||||
.collect();
|
||||
let episodes = sqlx::query_as!(
|
||||
EpisodeRow,
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", se.number AS "season_number!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
FROM episodes e JOIN seasons se ON se.id = e.season_id WHERE se.series_id = ?"#,
|
||||
id
|
||||
)
|
||||
.fetch_all(pool(state)?)
|
||||
.await?;
|
||||
let episodes: Vec<_> = episodes.iter().map(core_episode).collect();
|
||||
Ok(with_status(&row, &seasons, &episodes, SystemTime::now()))
|
||||
Ok(with_status(&row, &episodes, SystemTime::now()))
|
||||
}
|
||||
|
||||
async fn require_tv_root(state: &AppState, root_id: i64) -> Result<(), ApiError> {
|
||||
@@ -448,20 +396,12 @@ pub async fn list(
|
||||
.await?
|
||||
};
|
||||
|
||||
let (seasons, episodes) = tv_by_series(&state).await?;
|
||||
let episodes = tv_by_series(&state).await?;
|
||||
let now = SystemTime::now();
|
||||
let no_seasons: Vec<arr_core::Season> = Vec::new();
|
||||
let no_episodes: Vec<arr_core::Episode> = Vec::new();
|
||||
Ok(Json(
|
||||
rows.iter()
|
||||
.map(|row| {
|
||||
with_status(
|
||||
row,
|
||||
seasons.get(&row.id).unwrap_or(&no_seasons),
|
||||
episodes.get(&row.id).unwrap_or(&no_episodes),
|
||||
now,
|
||||
)
|
||||
})
|
||||
.map(|row| with_status(row, episodes.get(&row.id).unwrap_or(&no_episodes), now))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
@@ -607,7 +547,7 @@ async fn load_seasons(state: &AppState, series_id: i64) -> Result<Vec<Season>, A
|
||||
.await?;
|
||||
let episodes = sqlx::query_as!(
|
||||
EpisodeRow,
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", se.number AS "season_number!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
FROM episodes e JOIN seasons se ON se.id = e.season_id WHERE se.series_id = ? ORDER BY se.number, e.number"#,
|
||||
series_id
|
||||
)
|
||||
@@ -714,6 +654,7 @@ pub async fn create_season(
|
||||
.map(|episode| arr_core::Episode {
|
||||
id: EpisodeId(0),
|
||||
season_id: SeasonId(0),
|
||||
season_number: u16::try_from(input.number).unwrap_or_default(),
|
||||
number: u16::try_from(episode.number).unwrap_or_default(),
|
||||
title: episode.title.clone(),
|
||||
air_date: air_date(episode.air_date.as_deref()),
|
||||
@@ -837,7 +778,7 @@ pub async fn update_season(
|
||||
async fn load_episode(state: &AppState, id: i64) -> Result<Episode, ApiError> {
|
||||
let row = sqlx::query_as!(
|
||||
EpisodeRow,
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
r#"SELECT se.series_id AS "series_id!: i64", e.id AS "id!: i64", e.season_id AS "season_id!: i64", se.number AS "season_number!: i64", e.number AS "number!: i64", e.title AS "title!: String", e.air_date, e.wanted AS "wanted!: bool", e.state AS "state!: String", e.vanished AS "vanished!: bool", e.search_attempts AS "search_attempts!: i64", e.last_searched_at
|
||||
FROM episodes e JOIN seasons se ON se.id = e.season_id WHERE e.id = ?"#,
|
||||
id
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user