Backend enhancements: auth, channels, restaurants, daemon improvements
- Add admin auth dependency and role checks - Expand channel and restaurant API routes - Improve YouTube transcript fetching - Enhance daemon worker with better error handling and scheduling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,37 +1,92 @@
|
||||
"""Daemon worker: periodic channel scan + video processing."""
|
||||
"""Daemon worker: config-driven channel scan + video processing."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from core.db import conn
|
||||
from core.youtube import scan_all_channels
|
||||
from core.pipeline import process_pending
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CHECK_INTERVAL = 30 # seconds between config checks
|
||||
|
||||
def run_once() -> None:
|
||||
"""Single daemon cycle: scan channels then process pending videos."""
|
||||
logger.info("=== Daemon cycle start ===")
|
||||
|
||||
def _get_config() -> dict | None:
|
||||
"""Read daemon config from DB."""
|
||||
try:
|
||||
new_count = scan_all_channels()
|
||||
logger.info("Scan complete: %d new videos", new_count)
|
||||
with conn() as c:
|
||||
cur = c.cursor()
|
||||
cur.execute(
|
||||
"SELECT scan_enabled, scan_interval_min, process_enabled, "
|
||||
"process_interval_min, process_limit, last_scan_at, last_process_at "
|
||||
"FROM daemon_config WHERE id = 1"
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return {
|
||||
"scan_enabled": bool(row[0]),
|
||||
"scan_interval_min": row[1],
|
||||
"process_enabled": bool(row[2]),
|
||||
"process_interval_min": row[3],
|
||||
"process_limit": row[4],
|
||||
"last_scan_at": row[5],
|
||||
"last_process_at": row[6],
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error("Channel scan failed: %s", e)
|
||||
|
||||
try:
|
||||
rest_count = process_pending(limit=10)
|
||||
logger.info("Processing complete: %d restaurants extracted", rest_count)
|
||||
except Exception as e:
|
||||
logger.error("Video processing failed: %s", e)
|
||||
|
||||
logger.info("=== Daemon cycle end ===")
|
||||
logger.error("Failed to read daemon config: %s", e)
|
||||
return None
|
||||
|
||||
|
||||
def run_loop(interval: int = 3600) -> None:
|
||||
"""Run daemon in a loop with configurable interval (default 1 hour)."""
|
||||
logger.info("Daemon started (interval=%ds)", interval)
|
||||
def _should_run(last_at: datetime | None, interval_min: int) -> bool:
|
||||
"""Check if enough time has passed since last run."""
|
||||
if last_at is None:
|
||||
return True
|
||||
now = datetime.utcnow()
|
||||
# Oracle TIMESTAMP comes as datetime
|
||||
return now - last_at >= timedelta(minutes=interval_min)
|
||||
|
||||
|
||||
def _update_last(field: str) -> None:
|
||||
"""Update last_scan_at or last_process_at."""
|
||||
with conn() as c:
|
||||
c.cursor().execute(
|
||||
f"UPDATE daemon_config SET {field} = SYSTIMESTAMP WHERE id = 1"
|
||||
)
|
||||
|
||||
|
||||
def run_once_if_due() -> None:
|
||||
"""Check config and run tasks if their schedule is due."""
|
||||
cfg = _get_config()
|
||||
if not cfg:
|
||||
return
|
||||
|
||||
if cfg["scan_enabled"] and _should_run(cfg["last_scan_at"], cfg["scan_interval_min"]):
|
||||
logger.info("=== Scheduled scan start ===")
|
||||
try:
|
||||
new_count = scan_all_channels()
|
||||
logger.info("Scan complete: %d new videos", new_count)
|
||||
_update_last("last_scan_at")
|
||||
except Exception as e:
|
||||
logger.error("Channel scan failed: %s", e)
|
||||
|
||||
if cfg["process_enabled"] and _should_run(cfg["last_process_at"], cfg["process_interval_min"]):
|
||||
logger.info("=== Scheduled processing start ===")
|
||||
try:
|
||||
rest_count = process_pending(limit=cfg["process_limit"])
|
||||
logger.info("Processing complete: %d restaurants extracted", rest_count)
|
||||
_update_last("last_process_at")
|
||||
except Exception as e:
|
||||
logger.error("Video processing failed: %s", e)
|
||||
|
||||
|
||||
def run_loop() -> None:
|
||||
"""Run daemon loop, checking config every CHECK_INTERVAL seconds."""
|
||||
logger.info("Daemon started (config-driven, check every %ds)", CHECK_INTERVAL)
|
||||
while True:
|
||||
run_once()
|
||||
time.sleep(interval)
|
||||
run_once_if_due()
|
||||
time.sleep(CHECK_INTERVAL)
|
||||
|
||||
Reference in New Issue
Block a user