feat(02-01): implement MPVEngine with position polling and lifespan integration
- Create lightsync/audio/__init__.py package marker - Create lightsync/audio/engine.py with MPVEngine class - Headless mpv wrapper (vo=null, ao from MPV_AO env var default null) - 10Hz polling loop in daemon thread (time.sleep(0.1), not observe_property) - Thread-safe get_state() returning position, paused, duration, loaded, file - load/play/pause/seek/stop methods (AUD-01, AUD-03) - apply_timer_fix() with sys.platform == win32 guard (INF-03) - Update main.py lifespan to create/start/stop MPVEngine - MPV_AO env var controls audio output (default null for server mode) - engine exposed via app.state.engine for API routes
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
from fastapi import FastAPI, Request
|
||||
@@ -6,22 +7,29 @@ from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from lightsync.devices.registry import DeviceRegistry
|
||||
from lightsync.store.show_store import ShowStore
|
||||
from lightsync.audio.engine import MPVEngine
|
||||
|
||||
# Module-level containers populated in lifespan
|
||||
registry: DeviceRegistry | None = None
|
||||
show_store: ShowStore | None = None
|
||||
engine: MPVEngine | None = None
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
global registry, show_store
|
||||
global registry, show_store, engine
|
||||
# Startup
|
||||
registry = DeviceRegistry(Path("devices.json"))
|
||||
await registry.load()
|
||||
show_store = ShowStore(Path("shows"))
|
||||
show_store.ensure_dir()
|
||||
ao = os.environ.get("MPV_AO", "null")
|
||||
engine = MPVEngine(ao=ao)
|
||||
engine.start()
|
||||
app.state.engine = engine
|
||||
yield
|
||||
# Shutdown
|
||||
engine.stop()
|
||||
await registry.save()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user