This commit is contained in:
2026-04-29 01:53:16 +03:00
commit ba6bfc5ed3
14 changed files with 1338 additions and 0 deletions

92
src/state.py Normal file
View File

@@ -0,0 +1,92 @@
"""
Хранение состояния скачивания в SQLite.
"""
import sqlite3
from datetime import datetime
from pathlib import Path
from typing import Optional
DB_PATH = Path("/app/state/progress.db")
class StateDB:
def __init__(self, db_path: Path = DB_PATH):
db_path.parent.mkdir(parents=True, exist_ok=True)
self.conn = sqlite3.connect(str(db_path))
self._init()
def _init(self):
self.conn.execute("""
CREATE TABLE IF NOT EXISTS chapters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
manga_url TEXT NOT NULL,
chapter_url TEXT NOT NULL UNIQUE,
title TEXT,
number REAL,
volume INTEGER,
status TEXT DEFAULT 'pending',
output_cbz TEXT,
output_pdf TEXT,
output_epub TEXT,
updated_at TEXT
)
""")
self.conn.commit()
def upsert_chapter(self, manga_url: str, chapter_url: str,
title: str = "", number: float = 0, volume: int = 0):
self.conn.execute("""
INSERT INTO chapters (manga_url, chapter_url, title, number, volume, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(chapter_url) DO UPDATE SET
title = excluded.title,
number = excluded.number,
volume = excluded.volume
""", (manga_url, chapter_url, title, number, volume, _now()))
self.conn.commit()
def mark_done(self, chapter_url: str, fmt: str, output_path: str):
col = f"output_{fmt}"
self.conn.execute(f"""
UPDATE chapters SET status='done', {col}=?, updated_at=?
WHERE chapter_url=?
""", (output_path, _now(), chapter_url))
self.conn.commit()
def mark_failed(self, chapter_url: str):
self.conn.execute("""
UPDATE chapters SET status='failed', updated_at=? WHERE chapter_url=?
""", (_now(), chapter_url))
self.conn.commit()
def get_pending(self, manga_url: str) -> list[dict]:
cur = self.conn.execute("""
SELECT chapter_url, title, number, volume
FROM chapters
WHERE manga_url=? AND status != 'done'
ORDER BY volume, number
""", (manga_url,))
cols = [d[0] for d in cur.description]
return [dict(zip(cols, row)) for row in cur.fetchall()]
def get_all(self, manga_url: str) -> list[dict]:
cur = self.conn.execute("""
SELECT * FROM chapters WHERE manga_url=? ORDER BY volume, number
""", (manga_url,))
cols = [d[0] for d in cur.description]
return [dict(zip(cols, row)) for row in cur.fetchall()]
def chapter_status(self, chapter_url: str) -> Optional[str]:
cur = self.conn.execute(
"SELECT status FROM chapters WHERE chapter_url=?", (chapter_url,))
row = cur.fetchone()
return row[0] if row else None
def close(self):
self.conn.close()
def _now() -> str:
return datetime.utcnow().isoformat()