feat: season-pack vs per-episode grab selection (#94)
ci / web (push) Successful in 24s
e2e / e2e (push) Successful in 54s
ci / rust (push) Successful in 2m11s

This commit was merged in pull request #94.
This commit is contained in:
2026-08-23 01:40:53 +01:00
parent 35391f02fc
commit 9f812fac11
28 changed files with 2855 additions and 143 deletions
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "\n SELECT se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.title AS \"series_title!: String\",\n s.original_language\n FROM seasons se\n JOIN series s ON s.id = se.series_id\n WHERE s.blocked = 0\n AND EXISTS (\n SELECT 1 FROM episodes e\n WHERE e.season_id = se.id\n AND e.wanted = 1\n AND NOT EXISTS (\n SELECT 1 FROM media_files f\n WHERE f.owner_kind = 'episode' AND f.owner_id = e.id\n )\n AND NOT EXISTS (\n SELECT 1 FROM grabs g\n WHERE g.target_kind = 'episode' AND g.target_id = e.id\n AND g.state IN ('sent', 'downloaded', 'imported')\n )\n )\n AND NOT EXISTS (\n SELECT 1 FROM grabs g\n WHERE g.target_kind = 'season' AND g.target_id = se.id\n AND g.state IN ('sent', 'downloaded', 'imported')\n )\n ORDER BY se.id\n ",
"describe": {
"columns": [
{
"name": "season_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "series_title!: String",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "original_language",
"ordinal": 3,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
true
]
},
"hash": "0a3680a6cf353c210c1609bef5061f5026f61256c901564548d56f816dd4c68d"
}
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", infohash AS \"infohash!: String\", target_id AS \"target_id!: i64\"\n FROM grabs WHERE state = 'sent'",
"query": "SELECT id AS \"id!: i64\", infohash AS \"infohash!: String\",\n target_kind AS \"target_kind!: String\", target_id AS \"target_id!: i64\"\n FROM grabs WHERE state = 'sent'",
"describe": {
"columns": [
{
@@ -14,8 +14,13 @@
"type_info": "Text"
},
{
"name": "target_id!: i64",
"name": "target_kind!: String",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "target_id!: i64",
"ordinal": 3,
"type_info": "Integer"
}
],
@@ -25,8 +30,9 @@
"nullable": [
true,
false,
false,
false
]
},
"hash": "08313d5c002d1355542a40eb94b905f0091229e0433b038fdb55a49e557efa06"
"hash": "107d5702236ede18bc5a88daedd0fff699d5c80399c0d0139e1e89732b58b169"
}
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "INSERT INTO grabs (release_id, target_kind, target_id, infohash, state)\n VALUES (?, 'movie', ?, ?, 'sent')\n ON CONFLICT (infohash) DO NOTHING\n RETURNING id AS \"id!: i64\"",
"query": "INSERT INTO grabs (release_id, target_kind, target_id, infohash, state)\n VALUES (?, ?, ?, ?, 'sent')\n ON CONFLICT (infohash) DO NOTHING\n RETURNING id AS \"id!: i64\"",
"describe": {
"columns": [
{
@@ -10,11 +10,11 @@
}
],
"parameters": {
"Right": 3
"Right": 4
},
"nullable": [
true
]
},
"hash": "0e3b37aa1da32a0a5266c3841012fae52ce6394fcdc6c8e42eaeec0d7d6fb663"
"hash": "12f31ea2737b9a80b7d23b4c4029bbfebb0291b73f6eb5ec6084b05c38ae753f"
}
@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "\n SELECT e.id AS \"id!: i64\",\n e.number AS \"number!: i64\",\n e.air_date,\n e.wanted AS \"wanted!: bool\",\n e.search_attempts AS \"search_attempts!: i64\",\n e.last_searched_at,\n EXISTS (\n SELECT 1 FROM media_files f\n WHERE f.owner_kind = 'episode' AND f.owner_id = e.id\n ) AS \"has_file!: bool\",\n EXISTS (\n SELECT 1 FROM grabs g\n WHERE g.target_kind = 'episode' AND g.target_id = e.id\n AND g.state IN ('sent', 'downloaded', 'imported')\n ) AS \"in_flight!: bool\"\n FROM episodes e\n WHERE e.season_id = ?\n ORDER BY e.number\n ",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "number!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "air_date",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "wanted!: bool",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "search_attempts!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "last_searched_at",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "has_file!: bool",
"ordinal": 6,
"type_info": "Null"
},
{
"name": "in_flight!: bool",
"ordinal": 7,
"type_info": "Null"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
false,
false,
true,
null,
null
]
},
"hash": "33de7e17f5defa761de0d6e174dca40e2d883b9990bec497355422f5b5e6dfe5"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO media_files (owner_kind, owner_id, path, size, probed, waiver)\n VALUES ('episode', ?, ?, ?, ?, ?)\n ON CONFLICT (path) DO UPDATE SET\n size = excluded.size,\n probed = excluded.probed,\n waiver = excluded.waiver,\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')",
"describe": {
"columns": [],
"parameters": {
"Right": 5
},
"nullable": []
},
"hash": "3d4bd62a88c610eb85c3dc0e39be8d485fa355c2febd8e9030ca8f69ae6a4f38"
}
@@ -0,0 +1,68 @@
{
"db_name": "SQLite",
"query": "\n SELECT g.id AS \"grab_id!: i64\",\n g.infohash AS \"infohash!: String\",\n se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.tmdb_id AS \"series_tmdb_id!: i64\",\n s.title AS \"series_title!: String\",\n s.year AS \"series_year\",\n s.original_language,\n r.name AS \"release_name!: String\"\n FROM grabs g\n JOIN seasons se ON se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n JOIN releases r ON r.id = g.release_id\n WHERE g.state = 'downloaded' AND g.target_kind = 'season'\n ORDER BY g.id\n ",
"describe": {
"columns": [
{
"name": "grab_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "series_title!: String",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "series_year",
"ordinal": 6,
"type_info": "Integer"
},
{
"name": "original_language",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "release_name!: String",
"ordinal": 8,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
false,
false,
true,
true,
false
]
},
"hash": "67719bd71584183785d05f28095debebac93f27189194b680dca887dea725dd9"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE episodes\n SET search_attempts = search_attempts + 1,\n last_searched_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "6c8f695a08095b3941a7b6196830aa04462a1e71a2b5ee823e050104cf1b1889"
}
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "UPDATE movies SET state = 'downloading',\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"query": "UPDATE movies SET state = 'downloading',\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
@@ -8,5 +8,5 @@
},
"nullable": []
},
"hash": "aa1739e8bc9d2dd48fcfc3238d25187a1f48546af2c05cd618ba1c1b9f7ac791"
"hash": "792226f98ff3c19945154c7fda9e36f1fcf4fea39541f19989ee312da55c9f47"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO episode_releases (episode_id, release_id) VALUES (?, ?)\n ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "7cf1d44cc741e98ec06438256b593a70882f3274bed650f7691ff4850573a428"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT EXISTS (\n SELECT 1 FROM grabs\n WHERE target_kind = 'season' AND target_id = ? AND state = 'failed'\n ) AS \"failed!: bool\"",
"describe": {
"columns": [
{
"name": "failed!: bool",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "8cdf7339d395e21af86bd71e011e5681e0c00b1f9692a7142043067ff64a00a1"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE grabs\n SET state = 'imported',\n imported_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "8fac200bd76490e737c10bbaaa968325ea83c6ee558b2291abc5c1fa943334f0"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE grabs\n SET state = 'imported',\n imported_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "9318221b7be54520b2ce3b9df1af806f02bc531356b2939e8179ab3be4d1a998"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE episodes\n SET state = 'missing',\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE season_id = ? AND state = 'downloading'\n AND NOT EXISTS (\n SELECT 1 FROM media_files f\n WHERE f.owner_kind = 'episode' AND f.owner_id = episodes.id\n )",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "a509854dc66a54218a285c1dbcb4b2500a1f9b529d98bc37a39c7da861a4f3dc"
}
@@ -0,0 +1,74 @@
{
"db_name": "SQLite",
"query": "\n SELECT g.id AS \"grab_id!: i64\",\n g.infohash AS \"infohash!: String\",\n e.id AS \"episode_id!: i64\",\n se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.tmdb_id AS \"series_tmdb_id!: i64\",\n s.title AS \"series_title!: String\",\n s.year AS \"series_year\",\n s.original_language,\n r.name AS \"release_name!: String\"\n FROM grabs g\n JOIN episodes e ON 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 JOIN releases r ON r.id = g.release_id\n WHERE g.state = 'downloaded' AND g.target_kind = 'episode'\n ORDER BY g.id\n ",
"describe": {
"columns": [
{
"name": "grab_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "episode_id!: i64",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "season_id!: i64",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "series_title!: String",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "series_year",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "original_language",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "release_name!: String",
"ordinal": 9,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
false,
false,
false,
true,
true,
false
]
},
"hash": "a71c62a2e9cb2ba19f37427347dbf37d194fed7b272371131f07b3ac4eb0bf33"
}
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "\n SELECT e.id AS \"id!: i64\",\n e.number AS \"number!: i64\",\n e.title AS \"title!: String\",\n EXISTS (\n SELECT 1 FROM media_files f\n WHERE f.owner_kind = 'episode' AND f.owner_id = e.id\n ) AS \"has_file!: bool\"\n FROM episodes e\n WHERE e.season_id = ?\n ORDER BY e.number\n ",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "number!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "has_file!: bool",
"ordinal": 3,
"type_info": "Null"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
null
]
},
"hash": "b9511e409f9521914406f522c9cf63bf97436d5af605413f8660066d4779f6ca"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE episodes SET state = 'downloading',\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "c9f250c5b9056ab7e5199f3e3d4553a3996fccc0951b7214562eeb01b554dc58"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE episodes\n SET state = 'available',\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "cc8029578a0bb2d6d95c190922a34c0d8bc2a831f839975f1b5b737e2c04ce8e"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE episodes\n SET state = 'missing',\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "d97e6b799312c8659f3a78532be26f8183b563e011c4e059747da0fa82fb54d6"
}