scrub optimization
This commit is contained in:
92
cht/scrub/manager.py
Normal file
92
cht/scrub/manager.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user