93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
"""Proxy manager — background generation and lifecycle of scrub proxies."""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from threading import Thread
|
|
|
|
from gi.repository import GLib
|
|
|
|
from cht.scrub.proxy import proxy_path_for, generate_proxy, cleanup_proxies, PROXY_HEIGHT
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class ProxyManager:
|
|
"""Manages background proxy generation for scrub mode.
|
|
|
|
Usage:
|
|
pm = ProxyManager(session_id="20260403_120000")
|
|
pm.request(segment_path, on_ready=lambda path: ...)
|
|
pm.cancel() # stop pending work
|
|
"""
|
|
|
|
# Proxy states
|
|
PENDING = "pending"
|
|
GENERATING = "generating"
|
|
READY = "ready"
|
|
FAILED = "failed"
|
|
|
|
def __init__(self, session_id: str):
|
|
self._session_id = session_id
|
|
self._state: dict[str, str] = {} # segment_path_str → state
|
|
self._proxies: dict[str, Path] = {} # segment_path_str → proxy_path
|
|
self._cancelled = False
|
|
|
|
def request(self, segment_path: Path, on_ready=None, on_error=None) -> None:
|
|
"""Request proxy for a segment. Calls back on GTK main thread when ready.
|
|
|
|
If proxy already exists, calls back immediately.
|
|
"""
|
|
key = str(segment_path)
|
|
|
|
# Already ready
|
|
proxy = proxy_path_for(segment_path, self._session_id)
|
|
if proxy.exists():
|
|
self._state[key] = self.READY
|
|
self._proxies[key] = proxy
|
|
if on_ready:
|
|
GLib.idle_add(on_ready, proxy)
|
|
return
|
|
|
|
# Already generating
|
|
if self._state.get(key) == self.GENERATING:
|
|
return
|
|
|
|
self._state[key] = self.GENERATING
|
|
|
|
def _generate():
|
|
if self._cancelled:
|
|
return
|
|
try:
|
|
result = generate_proxy(segment_path, proxy)
|
|
self._state[key] = self.READY
|
|
self._proxies[key] = result
|
|
if on_ready and not self._cancelled:
|
|
GLib.idle_add(on_ready, result)
|
|
except Exception as e:
|
|
self._state[key] = self.FAILED
|
|
log.error("Proxy generation failed: %s", e)
|
|
if on_error and not self._cancelled:
|
|
GLib.idle_add(on_error, str(e))
|
|
|
|
Thread(target=_generate, daemon=True,
|
|
name=f"proxy_{segment_path.stem}").start()
|
|
|
|
def get_state(self, segment_path: Path) -> str | None:
|
|
"""Return current state of proxy for segment, or None if not requested."""
|
|
return self._state.get(str(segment_path))
|
|
|
|
def get_proxy(self, segment_path: Path) -> Path | None:
|
|
"""Return proxy path if ready, None otherwise."""
|
|
return self._proxies.get(str(segment_path))
|
|
|
|
def cancel(self) -> None:
|
|
"""Cancel pending work. Already-running ffmpeg will finish but callbacks are suppressed."""
|
|
self._cancelled = True
|
|
|
|
def cleanup(self) -> None:
|
|
"""Delete all proxies for this session."""
|
|
self.cancel()
|
|
cleanup_proxies(self._session_id)
|
|
self._state.clear()
|
|
self._proxies.clear()
|