refactor(daemon): extract tv queue roll-up
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"season_id!: i64\"\n FROM grabs g\n JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n GROUP BY s.id, s.title, s.year, se.id\n ",
|
||||
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"season_id!: i64\"\n FROM grabs g\n JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n GROUP BY s.id, s.title, s.year, se.id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -34,5 +34,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d0d0b0aadb52ee815d690714be3b4c0a2ccbd759dc33ecbaff2e7ea397ed5705"
|
||||
"hash": "4ddb143ab51ca61ac782f22cff84f01f7d58d8a424310ae0743fb0c91577665e"
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n e.id AS \"episode_id!: i64\"\n FROM episodes e\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n JOIN roots root ON root.id = s.root_id\n WHERE root.audience = 'kids'\n AND s.blocked = 0\n AND e.wanted = 1 AND e.state = 'missing' AND e.search_attempts > 0\n AND NOT EXISTS (\n SELECT 1 FROM episode_releases er\n JOIN releases r ON r.id = er.release_id\n WHERE er.episode_id = e.id AND r.verdict IN ('eligible', 'waived')\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "series_id!: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "title!: String",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "year",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "episode_id!: i64",
|
||||
"ordinal": 3,
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "652036a60ca873fa8868e392e05a341d929fb531ff3e3b79c6fa8630a8689d28"
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n e.id AS \"episode_id!: i64\"\n FROM episodes e\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n JOIN roots root ON root.id = s.root_id\n WHERE root.audience = 'kids'\n AND s.blocked = 0\n AND e.wanted = 1 AND e.state = 'missing' AND e.search_attempts > 0\n AND NOT EXISTS (\n SELECT 1 FROM episode_releases er\n JOIN releases r ON r.id = er.release_id\n WHERE er.episode_id = e.id AND r.verdict IN ('eligible', 'waived')\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "series_id!: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "title!: String",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "year",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "episode_id!: i64",
|
||||
"ordinal": 3,
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "a071c1bedfdd1347713cac523cb1aa9522dc9a07fb5d681655622971398a0ff4"
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"episode_id!: i64\"\n FROM grabs g\n JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n GROUP BY s.id, s.title, s.year, e.id\n HAVING count(DISTINCT g.release_id) >= 2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "series_id!: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "title!: String",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "year",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "episode_id!: i64",
|
||||
"ordinal": 3,
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f942fe3b9b6ac564d2ca4705d367faeafa2078e5c139c8e3166ad63c0d70f151"
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"episode_id!: i64\"\n FROM grabs g\n JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n GROUP BY s.id, s.title, s.year, e.id\n HAVING count(DISTINCT g.release_id) >= 2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "series_id!: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "title!: String",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "year",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "episode_id!: i64",
|
||||
"ordinal": 3,
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "faa9e5cf0cf869161dd4878f6fb8707e378b7d9c6516d602afb49b1e30792207"
|
||||
}
|
||||
@@ -147,78 +147,10 @@ impl AttentionAction {
|
||||
});
|
||||
}
|
||||
|
||||
let mut tv: HashMap<i64, (String, Option<i64>, TvEntry)> = HashMap::new();
|
||||
|
||||
let tv_no_pt_source = sqlx::query!(
|
||||
r#"
|
||||
SELECT s.id AS "series_id!: i64", s.title AS "title!: String", s.year,
|
||||
e.id AS "episode_id!: i64"
|
||||
FROM episodes e
|
||||
JOIN seasons se ON se.id = e.season_id
|
||||
JOIN series s ON s.id = se.series_id
|
||||
JOIN roots root ON root.id = s.root_id
|
||||
WHERE root.audience = 'kids'
|
||||
AND s.blocked = 0
|
||||
AND e.wanted = 1 AND e.state = 'missing' AND e.search_attempts > 0
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM episode_releases er
|
||||
JOIN releases r ON r.id = er.release_id
|
||||
WHERE er.episode_id = e.id AND r.verdict IN ('eligible', 'waived')
|
||||
)
|
||||
"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
for row in tv_no_pt_source {
|
||||
tv_entry(&mut tv, row.series_id, row.title, row.year)
|
||||
.no_pt_source
|
||||
.push(row.episode_id);
|
||||
}
|
||||
|
||||
let episode_hard_fails = sqlx::query!(
|
||||
r#"
|
||||
SELECT s.id AS "series_id!: i64", s.title AS "title!: String", s.year,
|
||||
g.target_id AS "episode_id!: i64"
|
||||
FROM grabs g
|
||||
JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id
|
||||
JOIN seasons se ON se.id = e.season_id
|
||||
JOIN series s ON s.id = se.series_id
|
||||
WHERE g.state = 'failed'
|
||||
GROUP BY s.id, s.title, s.year, e.id
|
||||
HAVING count(DISTINCT g.release_id) >= 2
|
||||
"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
for row in episode_hard_fails {
|
||||
tv_entry(&mut tv, row.series_id, row.title, row.year)
|
||||
.hard_failed_episodes
|
||||
.push(row.episode_id);
|
||||
}
|
||||
|
||||
let season_pack_fails = sqlx::query!(
|
||||
r#"
|
||||
SELECT s.id AS "series_id!: i64", s.title AS "title!: String", s.year,
|
||||
g.target_id AS "season_id!: i64"
|
||||
FROM grabs g
|
||||
JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id
|
||||
JOIN series s ON s.id = se.series_id
|
||||
WHERE g.state = 'failed'
|
||||
GROUP BY s.id, s.title, s.year, se.id
|
||||
"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
for row in season_pack_fails {
|
||||
tv_entry(&mut tv, row.series_id, row.title, row.year)
|
||||
.failed_season_packs
|
||||
.push(row.season_id);
|
||||
}
|
||||
|
||||
for (&series_id, (title, year, entry)) in &tv {
|
||||
for (series_id, title, year, entry) in queue_tv(database).await? {
|
||||
queued.insert(
|
||||
Subject::Series(series_id),
|
||||
(title_with_year(title, *year), entry.reason()),
|
||||
(title_with_year(&title, year), entry.reason()),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -262,18 +194,98 @@ fn title_with_year(title: &str, year: Option<i64>) -> String {
|
||||
}
|
||||
|
||||
/// The series' roll-up entry, created empty on first sight.
|
||||
fn tv_entry<'a>(
|
||||
tv: &'a mut HashMap<i64, (String, Option<i64>, TvEntry)>,
|
||||
fn tv_entry(
|
||||
tv: &mut HashMap<i64, (String, Option<i64>, TvEntry)>,
|
||||
series_id: i64,
|
||||
title: String,
|
||||
year: Option<i64>,
|
||||
) -> &'a mut TvEntry {
|
||||
) -> &mut TvEntry {
|
||||
&mut tv
|
||||
.entry(series_id)
|
||||
.or_insert_with(|| (title, year, TvEntry::default()))
|
||||
.2
|
||||
}
|
||||
|
||||
/// TV roll-up (§9.5): every queued series with what put it there — wanted
|
||||
/// episodes whose every candidate was rejected for language, episodes two
|
||||
/// different releases hard-failed post-probe, and seasons whose pack grab
|
||||
/// hard-failed. One entry per series, so the notification can be one per
|
||||
/// series however long the broken season is.
|
||||
async fn queue_tv(database: &Db) -> Result<Vec<(i64, String, Option<i64>, TvEntry)>, sqlx::Error> {
|
||||
let mut tv = HashMap::new();
|
||||
|
||||
let tv_no_pt_source = sqlx::query!(
|
||||
r#"
|
||||
SELECT s.id AS "series_id!: i64", s.title AS "title!: String", s.year,
|
||||
e.id AS "episode_id!: i64"
|
||||
FROM episodes e
|
||||
JOIN seasons se ON se.id = e.season_id
|
||||
JOIN series s ON s.id = se.series_id
|
||||
JOIN roots root ON root.id = s.root_id
|
||||
WHERE root.audience = 'kids'
|
||||
AND s.blocked = 0
|
||||
AND e.wanted = 1 AND e.state = 'missing' AND e.search_attempts > 0
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM episode_releases er
|
||||
JOIN releases r ON r.id = er.release_id
|
||||
WHERE er.episode_id = e.id AND r.verdict IN ('eligible', 'waived')
|
||||
)
|
||||
"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
for row in tv_no_pt_source {
|
||||
tv_entry(&mut tv, row.series_id, row.title, row.year)
|
||||
.no_pt_source
|
||||
.push(row.episode_id);
|
||||
}
|
||||
|
||||
let episode_hard_fails = sqlx::query!(
|
||||
r#"
|
||||
SELECT s.id AS "series_id!: i64", s.title AS "title!: String", s.year,
|
||||
g.target_id AS "episode_id!: i64"
|
||||
FROM grabs g
|
||||
JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id
|
||||
JOIN seasons se ON se.id = e.season_id
|
||||
JOIN series s ON s.id = se.series_id
|
||||
WHERE g.state = 'failed'
|
||||
GROUP BY s.id, s.title, s.year, e.id
|
||||
HAVING count(DISTINCT g.release_id) >= 2
|
||||
"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
for row in episode_hard_fails {
|
||||
tv_entry(&mut tv, row.series_id, row.title, row.year)
|
||||
.hard_failed_episodes
|
||||
.push(row.episode_id);
|
||||
}
|
||||
|
||||
let season_pack_fails = sqlx::query!(
|
||||
r#"
|
||||
SELECT s.id AS "series_id!: i64", s.title AS "title!: String", s.year,
|
||||
g.target_id AS "season_id!: i64"
|
||||
FROM grabs g
|
||||
JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id
|
||||
JOIN series s ON s.id = se.series_id
|
||||
WHERE g.state = 'failed'
|
||||
GROUP BY s.id, s.title, s.year, se.id
|
||||
"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
for row in season_pack_fails {
|
||||
tv_entry(&mut tv, row.series_id, row.title, row.year)
|
||||
.failed_season_packs
|
||||
.push(row.season_id);
|
||||
}
|
||||
|
||||
Ok(tv
|
||||
.into_iter()
|
||||
.map(|(id, (t, y, e))| (id, t, y, e))
|
||||
.collect())
|
||||
}
|
||||
|
||||
impl Action for AttentionAction {
|
||||
fn name(&self) -> &'static str {
|
||||
"attention"
|
||||
@@ -359,7 +371,7 @@ mod tests {
|
||||
VALUES (?, ?, ?, 1, 'missing', 1)",
|
||||
)
|
||||
.bind(season_id)
|
||||
.bind(number as i64 + 1)
|
||||
.bind(i64::try_from(number).unwrap_or(0) + 1)
|
||||
.bind(format!("Episode {number}"))
|
||||
.execute(database.pool())
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user