feat: orden por canal y estado + selects oscuros legibles (color-scheme)

This commit is contained in:
urieljareth
2026-08-22 17:21:24 -06:00
parent e219cfcbfa
commit ca2ae23f70
4 changed files with 188 additions and 3 deletions
+55
View File
@@ -58,6 +58,12 @@ _VIDEO_COLUMNS: dict[str, str] = {
# 1 cuando upload_date viene del discovery aproximado (texto relativo de
# YouTube: "hace 3 semanas"), 0/NULL cuando es exacto (extraccion).
"upload_date_approx": "INTEGER DEFAULT 0",
"video_download_status": "TEXT DEFAULT 'not_downloaded'",
"video_path": "TEXT",
"video_filename": "TEXT",
"video_size": "INTEGER",
"video_downloaded_at": "TEXT",
"video_error": "TEXT",
}
_CHANNEL_COLUMNS: dict[str, str] = {
@@ -247,6 +253,18 @@ def _order_clause(sort: str | None) -> str:
"likes_desc": by_likes,
"title": f"videos.title COLLATE NOCASE ASC, {_NEWEST_FIRST}",
"title_asc": f"videos.title COLLATE NOCASE ASC, {_NEWEST_FIRST}",
# Por nombre de canal (no por UUID): el subquery consulta channels,
# que tiene decenas de filas y PK sobre channel_id.
"channel": (
"(SELECT c.name FROM channels c WHERE c.channel_id = videos.channel_id) "
"COLLATE NOCASE ASC, videos.channel_id, videos.channel_seq DESC, videos.video_id DESC"
),
# Agrupa por ciclo de vida: hecho, luego pendientes, luego los que
# necesitan atencion (sin subs), errores al final.
"status": (
"CASE videos.status WHEN 'done' THEN 0 WHEN 'pending' THEN 1 "
"WHEN 'no_subtitles' THEN 2 ELSE 3 END ASC, " + _NEWEST_FIRST
),
}.get((sort or "").strip(), _NEWEST_FIRST)
@@ -296,6 +314,12 @@ class VideoRow:
channel_seq: int | None = None
# 1 cuando upload_date es aproximado (discovery), 0/NULL si es exacto.
upload_date_approx: int | None = None
video_download_status: str | None = None
video_path: str | None = None
video_filename: str | None = None
video_size: int | None = None
video_downloaded_at: str | None = None
video_error: str | None = None
# Date the row was ordered by. Equals `upload_date` when it is known; for a
# video discovery has not extracted yet it is inferred from `channel_seq`
# (see `query_videos`). Only populated by queries that compute it.
@@ -764,6 +788,31 @@ class Store:
(status, reason[:2000] if reason else None, now, video_id),
)
def update_video_download(
self,
video_id: str,
status: str,
*,
path: str | None = None,
filename: str | None = None,
size: int | None = None,
error: str | None = None,
) -> None:
"""Persist media-download state independently from transcript state."""
now = _now_iso() if status == "done" else None
with self._cursor() as cur:
cur.execute(
"""UPDATE videos SET
video_download_status = ?,
video_path = COALESCE(?, video_path),
video_filename = COALESCE(?, video_filename),
video_size = COALESCE(?, video_size),
video_downloaded_at = COALESCE(?, video_downloaded_at),
video_error = ?
WHERE video_id = ?""",
(status, path, filename, size, now, error[:2000] if error else None, video_id),
)
def set_availability(self, video_id: str, availability: str | None) -> None:
"""Record what a full extraction learned; more authoritative than the
flat listing, which omits the field for most entries."""
@@ -1194,6 +1243,12 @@ def _row_to_videorow(row: sqlite3.Row) -> VideoRow:
availability=row["availability"] if "availability" in keys else None,
channel_seq=row["channel_seq"] if "channel_seq" in keys else None,
upload_date_approx=row["upload_date_approx"] if "upload_date_approx" in keys else None,
video_download_status=row["video_download_status"] if "video_download_status" in keys else None,
video_path=row["video_path"] if "video_path" in keys else None,
video_filename=row["video_filename"] if "video_filename" in keys else None,
video_size=row["video_size"] if "video_size" in keys else None,
video_downloaded_at=row["video_downloaded_at"] if "video_downloaded_at" in keys else None,
video_error=row["video_error"] if "video_error" in keys else None,
sort_date=row["sort_date"] if "sort_date" in keys else None,
)