File size: 3,734 Bytes
78c8669 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """
SQLite storage for tasks and settings.
The DB file lives at DATA_DIR/uploader.db so it persists across
requests in the same running container.
DATA_DIR defaults to a "data" folder next to this file, which is
writable in an ordinary Docker container. If you attach a persistent
storage volume on Hugging Face, set the DATA_DIR env var / secret to
its mount path (e.g. /data) so the database survives a Space rebuild.
"""
import sqlite3
import os
import time
from contextlib import contextmanager
_DEFAULT_DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
DATA_DIR = os.environ.get("DATA_DIR") or _DEFAULT_DATA_DIR
try:
os.makedirs(DATA_DIR, exist_ok=True)
except PermissionError:
DATA_DIR = "/tmp/fb-uploader-data"
os.makedirs(DATA_DIR, exist_ok=True)
DB_PATH = os.path.join(DATA_DIR, "uploader.db")
SCHEMA = """
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_url TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT,
privacy TEXT NOT NULL DEFAULT 'SELF',
status TEXT NOT NULL DEFAULT 'pending',
video_id TEXT,
cdn_url TEXT,
error_message TEXT,
attempts INTEGER NOT NULL DEFAULT 0,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
);
"""
# status values: pending -> uploading -> processing -> completed
# any of the above -> failed
@contextmanager
def get_conn():
conn = sqlite3.connect(DB_PATH, timeout=30)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL;")
try:
yield conn
conn.commit()
finally:
conn.close()
def init_db():
with get_conn() as conn:
conn.executescript(SCHEMA)
# ---------- settings ----------
def set_setting(key: str, value: str):
with get_conn() as conn:
conn.execute(
"INSERT INTO settings (key, value) VALUES (?, ?) "
"ON CONFLICT(key) DO UPDATE SET value=excluded.value",
(key, value),
)
def get_setting(key: str, default=None):
with get_conn() as conn:
row = conn.execute("SELECT value FROM settings WHERE key=?", (key,)).fetchone()
return row["value"] if row else default
# ---------- tasks ----------
def create_task(video_url: str, title: str, description: str, privacy: str = "SELF") -> int:
now = time.time()
with get_conn() as conn:
cur = conn.execute(
"INSERT INTO tasks (video_url, title, description, privacy, status, "
"created_at, updated_at) VALUES (?, ?, ?, ?, 'pending', ?, ?)",
(video_url, title, description, privacy, now, now),
)
return cur.lastrowid
def list_tasks():
with get_conn() as conn:
rows = conn.execute("SELECT * FROM tasks ORDER BY id DESC").fetchall()
return [dict(r) for r in rows]
def get_task(task_id: int):
with get_conn() as conn:
row = conn.execute("SELECT * FROM tasks WHERE id=?", (task_id,)).fetchone()
return dict(row) if row else None
def get_tasks_by_status(status: str):
with get_conn() as conn:
rows = conn.execute("SELECT * FROM tasks WHERE status=?", (status,)).fetchall()
return [dict(r) for r in rows]
def update_task(task_id: int, **fields):
if not fields:
return
fields["updated_at"] = time.time()
cols = ", ".join(f"{k}=?" for k in fields)
values = list(fields.values()) + [task_id]
with get_conn() as conn:
conn.execute(f"UPDATE tasks SET {cols} WHERE id=?", values)
def delete_task(task_id: int):
with get_conn() as conn:
conn.execute("DELETE FROM tasks WHERE id=?", (task_id,))
|