Introduces Synchronization Clocks — shared, controllable time bases that CSS sources can optionally reference for synchronized animation. Backend: - New SyncClock dataclass, JSON store, Pydantic schemas, REST API - Runtime clock with thread-safe pause/resume/reset and speed control - Ref-counted runtime pool with eager creation for API control - clock_id field on all ColorStripSource types - Stream integration: clock time/speed replaces source-local values - Paused clock skips rendering (saves CPU + stops frame pushes) - Included in backup/restore via STORE_MAP Frontend: - Sync Clocks tab in Streams section with cards and controls - Clock dropdown in CSS editor (hidden speed slider when clock set) - Clock crosslink badge on CSS source cards (replaces speed badge) - Targets tab uses DataCache for picture/audio sources and sync clocks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
44 lines
1.3 KiB
Python
44 lines
1.3 KiB
Python
"""Synchronization clock data model.
|
||
|
||
A SyncClock provides a shared, controllable time base for animation-based
|
||
color strip sources. Multiple CSS sources referencing the same clock
|
||
animate in sync and share speed / pause / resume / reset controls.
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
from typing import Optional
|
||
|
||
|
||
@dataclass
|
||
class SyncClock:
|
||
"""Persistent configuration for a synchronization clock."""
|
||
|
||
id: str
|
||
name: str
|
||
speed: float # animation speed multiplier (0.1–10.0)
|
||
created_at: datetime
|
||
updated_at: datetime
|
||
description: Optional[str] = None
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"id": self.id,
|
||
"name": self.name,
|
||
"speed": self.speed,
|
||
"description": self.description,
|
||
"created_at": self.created_at.isoformat(),
|
||
"updated_at": self.updated_at.isoformat(),
|
||
}
|
||
|
||
@staticmethod
|
||
def from_dict(data: dict) -> "SyncClock":
|
||
return SyncClock(
|
||
id=data["id"],
|
||
name=data["name"],
|
||
speed=float(data.get("speed", 1.0)),
|
||
description=data.get("description"),
|
||
created_at=datetime.fromisoformat(data["created_at"]),
|
||
updated_at=datetime.fromisoformat(data["updated_at"]),
|
||
)
|