""" queue_manager.py — Thread-pool-based job queue with status tracking. """ from __future__ import annotations import threading import time from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass, field from enum import Enum from typing import Any, Callable class State(str, Enum): QUEUED = "queued" RUNNING = "running" UPLOADING = "uploading" SENDING = "sending" DONE = "done" FAILED = "failed" @dataclass class JobStatus: job_id: str state: State = State.QUEUED message: str = "" created_at: float = field(default_factory=time.time) updated_at: float = field(default_factory=time.time) def update(self, state: State, message: str = "") -> None: self.state = state self.message = message self.updated_at = time.time() def display(self) -> str: icons = { State.QUEUED: "⏳", State.RUNNING: "⚙️", State.UPLOADING: "☁️", State.SENDING: "📧", State.DONE: "✅", State.FAILED: "❌", } icon = icons.get(self.state, "❓") lines = [f"{icon} **{self.state.value.title()}**"] if self.message: lines.append(self.message) lines.append(f"*Job ID: `{self.job_id}`*") return "\n\n".join(lines) class JobQueue: def __init__(self, max_workers: int = 8, max_jobs: int = 100) -> None: self._max_jobs = max_jobs self._executor = ThreadPoolExecutor(max_workers=max_workers) self._jobs: dict[str, JobStatus] = {} self._lock = threading.Lock() def is_full(self) -> bool: with self._lock: active = sum( 1 for s in self._jobs.values() if s.state in (State.QUEUED, State.RUNNING, State.UPLOADING, State.SENDING) ) return active >= self._max_jobs def enqueue(self, job_id: str, fn: Callable, kwargs: dict[str, Any]) -> JobStatus: status = JobStatus(job_id=job_id) with self._lock: self._jobs[job_id] = status def _run(): with self._lock: self._jobs[job_id].update(State.RUNNING, "Pipeline started…") try: fn(status_cb=self._make_cb(job_id), **kwargs) with self._lock: self._jobs[job_id].update(State.DONE, "Video sent to your inbox! 🎉") except Exception as exc: with self._lock: self._jobs[job_id].update(State.FAILED, f"Error: {exc}") self._executor.submit(_run) return status def _make_cb(self, job_id: str) -> Callable[[State, str], None]: def cb(state: State, message: str = "") -> None: with self._lock: if job_id in self._jobs: self._jobs[job_id].update(state, message) return cb def get_status(self, job_id: str) -> JobStatus | None: with self._lock: return self._jobs.get(job_id)