From 09457868e4a92bd055ac32322953f7049a603171 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 11 May 2026 14:03:31 +0200 Subject: [PATCH] fix: resume interrupted downloads after container restart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store subtitle/year/category in downloads table. On startup, re-queue any download still in queued/downloading state (reset downloading → queued). Co-Authored-By: Claude Sonnet 4.6 --- downloader.py | 34 ++++++++++++++++++++++++++++++++-- main.py | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/downloader.py b/downloader.py index e4e42cc..199aacd 100644 --- a/downloader.py +++ b/downloader.py @@ -95,6 +95,9 @@ class DownloadManager: id TEXT PRIMARY KEY, url TEXT NOT NULL, title TEXT NOT NULL, + subtitle TEXT NOT NULL DEFAULT '', + year INTEGER, + category TEXT NOT NULL DEFAULT '', filename TEXT, state TEXT NOT NULL DEFAULT 'queued', progress REAL DEFAULT 0, @@ -105,6 +108,15 @@ class DownloadManager: error TEXT ) """) + for col, definition in [ + ("subtitle", "TEXT NOT NULL DEFAULT ''"), + ("year", "INTEGER"), + ("category", "TEXT NOT NULL DEFAULT ''"), + ]: + try: + conn.execute(f"ALTER TABLE downloads ADD COLUMN {col} {definition}") + except Exception: + pass conn.execute(""" CREATE TABLE IF NOT EXISTS auto_dl_categories ( category TEXT PRIMARY KEY, @@ -152,14 +164,32 @@ class DownloadManager: now = datetime.now().isoformat() with _db() as conn: conn.execute( - "INSERT INTO downloads (id, url, title, state, started_at) VALUES (?,?,?,'queued',?)", - (dl_id, url, title, now), + "INSERT INTO downloads (id, url, title, subtitle, year, category, state, started_at) VALUES (?,?,?,?,?,?,'queued',?)", + (dl_id, url, title, subtitle, year, category, now), ) with self._lock: self._active[dl_id] = {"state": "queued", "progress": 0, "title": title} await self._queue.put((dl_id, url, title, subtitle, year, category)) return dl_id + async def resume_pending(self): + """Re-queue downloads interrupted by a container restart.""" + with _db() as conn: + rows = conn.execute( + "SELECT id, url, title, subtitle, year, category FROM downloads" + " WHERE state IN ('queued', 'downloading') ORDER BY started_at" + ).fetchall() + conn.execute( + "UPDATE downloads SET state='queued', progress=0, speed='', eta=NULL" + " WHERE state='downloading'" + ) + for r in rows: + with self._lock: + self._active[r["id"]] = {"state": "queued", "progress": 0, "title": r["title"]} + await self._queue.put((r["id"], r["url"], r["title"], r["subtitle"] or "", r["year"], r["category"] or "")) + if rows: + logger.info("Resumed %d pending download(s)", len(rows)) + async def start_worker(self): loop = asyncio.get_running_loop() while True: diff --git a/main.py b/main.py index 286d78a..559f251 100644 --- a/main.py +++ b/main.py @@ -52,6 +52,7 @@ async def _auto_dl_loop(): @asynccontextmanager async def lifespan(app: FastAPI): + await dm.resume_pending() tasks = [ asyncio.create_task(dm.start_worker()), asyncio.create_task(_auto_dl_loop()),