Enhance PVideoDl with finished_at tracking for downloads and UI updates. Added finished_at field to Download model, updated storage to handle legacy databases, and improved frontend to display recent downloads. Refactored scripts for better clarity and error handling.
This commit is contained in:
+24
-3
@@ -8,7 +8,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -17,6 +17,10 @@ import aiosqlite
|
||||
from app.models import Download, DownloadStatus
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class Storage(ABC):
|
||||
@abstractmethod
|
||||
async def init(self) -> None: ...
|
||||
@@ -52,6 +56,7 @@ _COLUMNS = (
|
||||
"eta",
|
||||
"error",
|
||||
"created_at",
|
||||
"finished_at",
|
||||
)
|
||||
|
||||
|
||||
@@ -59,6 +64,8 @@ def _row_to_download(row: aiosqlite.Row) -> Download:
|
||||
data = dict(row)
|
||||
data["status"] = DownloadStatus(data["status"])
|
||||
data["created_at"] = datetime.fromisoformat(data["created_at"])
|
||||
if data.get("finished_at"):
|
||||
data["finished_at"] = datetime.fromisoformat(data["finished_at"])
|
||||
return Download.model_validate(data)
|
||||
|
||||
|
||||
@@ -98,22 +105,36 @@ class SqliteStorage(Storage):
|
||||
speed REAL,
|
||||
eta REAL,
|
||||
error TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
created_at TEXT NOT NULL,
|
||||
finished_at TEXT
|
||||
)
|
||||
"""
|
||||
)
|
||||
# Миграция старых БД, созданных до появления finished_at.
|
||||
await self._migrate_add_column("finished_at", "TEXT")
|
||||
# На старте всё, что осталось "качающимся" после прошлого запуска,
|
||||
# помечаем упавшим — воркеры этого процесса о них не знают.
|
||||
await self._db.execute(
|
||||
"UPDATE downloads SET status = ?, error = ? WHERE status = ?",
|
||||
"UPDATE downloads SET status = ?, error = ?, finished_at = ? "
|
||||
"WHERE status = ?",
|
||||
(
|
||||
DownloadStatus.FAILED.value,
|
||||
"Прервано при перезапуске приложения",
|
||||
_now().isoformat(),
|
||||
DownloadStatus.DOWNLOADING.value,
|
||||
),
|
||||
)
|
||||
await self._db.commit()
|
||||
|
||||
async def _migrate_add_column(self, name: str, decl: str) -> None:
|
||||
"""Добавить колонку, если её ещё нет (idempotent-миграция старых БД)."""
|
||||
async with self._conn.execute("PRAGMA table_info(downloads)") as cursor:
|
||||
cols = {row["name"] for row in await cursor.fetchall()}
|
||||
if name not in cols:
|
||||
await self._conn.execute(
|
||||
f"ALTER TABLE downloads ADD COLUMN {name} {decl}"
|
||||
)
|
||||
|
||||
async def close(self) -> None:
|
||||
if self._db is not None:
|
||||
await self._db.close()
|
||||
|
||||
Reference in New Issue
Block a user