Initial commit: image+audio to YouTube MP4 queue
This commit is contained in:
commit
f2d53a573f
15 changed files with 826 additions and 0 deletions
134
models.py
Normal file
134
models.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import sqlite3
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
image_file TEXT NOT NULL,
|
||||
audio_file TEXT NOT NULL,
|
||||
output_file TEXT,
|
||||
status TEXT DEFAULT 'waiting' NOT NULL,
|
||||
error_message TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
position INTEGER DEFAULT 0 NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
COLUMNS = {
|
||||
'id': 'INTEGER',
|
||||
'title': 'TEXT',
|
||||
'image_file': 'TEXT',
|
||||
'audio_file': 'TEXT',
|
||||
'output_file': 'TEXT',
|
||||
'status': 'TEXT',
|
||||
'error_message': 'TEXT',
|
||||
'created_at': 'TEXT',
|
||||
'updated_at': 'TEXT',
|
||||
'position': 'INTEGER',
|
||||
}
|
||||
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def _connect(db_path: Path) -> sqlite3.Connection:
|
||||
conn = sqlite3.connect(str(db_path), check_same_thread=False)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
|
||||
class JobStore:
|
||||
def __init__(self, db_path: Path):
|
||||
self.db_path = Path(db_path)
|
||||
self.local = threading.local()
|
||||
self.init_db()
|
||||
|
||||
def _conn(self) -> sqlite3.Connection:
|
||||
if not hasattr(self.local, 'conn') or self.local.conn is None:
|
||||
self.local.conn = _connect(self.db_path)
|
||||
return self.local.conn
|
||||
|
||||
def init_db(self):
|
||||
with _lock:
|
||||
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
conn = self._conn()
|
||||
conn.executescript(SCHEMA)
|
||||
conn.commit()
|
||||
self._migrate_columns()
|
||||
|
||||
def _migrate_columns(self):
|
||||
conn = self._conn()
|
||||
existing = {
|
||||
row['name']: row['type']
|
||||
for row in conn.execute("PRAGMA table_info(jobs)")
|
||||
}
|
||||
for col, col_type in COLUMNS.items():
|
||||
if col not in existing:
|
||||
conn.execute(f"ALTER TABLE jobs ADD COLUMN {col} {col_type}")
|
||||
conn.commit()
|
||||
|
||||
def create_job(self, title: str, image_file: str, audio_file: str) -> int:
|
||||
conn = self._conn()
|
||||
max_pos = conn.execute("SELECT COALESCE(MAX(position), 0) FROM jobs").fetchone()[0]
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO jobs (title, image_file, audio_file, status, position)
|
||||
VALUES (?, ?, ?, 'waiting', ?)""",
|
||||
(title, image_file, audio_file, int(max_pos) + 1)
|
||||
)
|
||||
conn.commit()
|
||||
return cur.lastrowid
|
||||
|
||||
def list_jobs(self) -> list[sqlite3.Row]:
|
||||
conn = self._conn()
|
||||
return conn.execute(
|
||||
"SELECT * FROM jobs ORDER BY position ASC, id ASC"
|
||||
).fetchall()
|
||||
|
||||
def get_job(self, job_id: int) -> sqlite3.Row | None:
|
||||
conn = self._conn()
|
||||
row = conn.execute("SELECT * FROM jobs WHERE id = ?", (job_id,)).fetchone()
|
||||
return row
|
||||
|
||||
def update_status(self, job_id: int, status: str, error_message: str | None = None, output_file: str | None = None):
|
||||
conn = self._conn()
|
||||
conn.execute(
|
||||
"""UPDATE jobs
|
||||
SET status = ?, error_message = ?, output_file = ?, updated_at = ?
|
||||
WHERE id = ?""",
|
||||
(status, error_message, output_file, datetime.utcnow().isoformat(), job_id)
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
def delete_job(self, job_id: int) -> bool:
|
||||
conn = self._conn()
|
||||
cur = conn.execute("DELETE FROM jobs WHERE id = ? AND status != 'processing'", (job_id,))
|
||||
conn.commit()
|
||||
return cur.rowcount > 0
|
||||
|
||||
def move_to_end(self, job_id: int) -> bool:
|
||||
conn = self._conn()
|
||||
max_pos = conn.execute("SELECT COALESCE(MAX(position), 0) FROM jobs").fetchone()[0]
|
||||
cur = conn.execute(
|
||||
"UPDATE jobs SET status = 'waiting', error_message = NULL, position = ? WHERE id = ?",
|
||||
(int(max_pos) + 1, job_id)
|
||||
)
|
||||
conn.commit()
|
||||
return cur.rowcount > 0
|
||||
|
||||
def pop_next_waiting(self) -> sqlite3.Row | None:
|
||||
conn = self._conn()
|
||||
with _lock:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM jobs WHERE status = 'waiting' ORDER BY position ASC, id ASC LIMIT 1"
|
||||
).fetchone()
|
||||
if row:
|
||||
conn.execute(
|
||||
"UPDATE jobs SET status = 'processing', updated_at = ? WHERE id = ?",
|
||||
(datetime.utcnow().isoformat(), row['id'])
|
||||
)
|
||||
conn.commit()
|
||||
return row
|
||||
Loading…
Add table
Add a link
Reference in a new issue