Files
Video-Player-Controller/controller/models.py
T

164 lines
3.5 KiB
Python

"""Controller domain records shared by persistence and orchestration."""
from __future__ import annotations
from dataclasses import dataclass
MEDIA_STATES = frozenset(
{"empty", "queued", "downloading", "ready", "error", "deleting"}
)
LIVE_JOB_STATES = frozenset({"reserved", "running"})
LIVE_GUARD_STATES = frozenset({"active", "review_required"})
UTILITY_EXPORT_MODES = frozenset({"mp4", "m4a", "mp3"})
@dataclass(frozen=True)
class SlotRecord:
slot: int
generation: int
media_state: str
url: str | None
yt_id: str | None
title: str
item_count: int
published_item_count: int
created_at: float | None
ready_at: float | None
expires_at: float | None
last_error: str | None
updated_at: float
@dataclass(frozen=True)
class DownloadReservation:
status: str
slot: int | None = None
generation: int | None = None
job_id: str | None = None
reused_slot: bool = False
oldest_expires_at: float | None = None
@dataclass(frozen=True)
class DownloadJob:
job_id: str
slot: int
generation: int
url: str
status: str
submitter: str | None
note: str | None
requested_at: float
started_at: float | None
finished_at: float | None
error: str | None
@dataclass(frozen=True)
class ExportReservation:
status: str
job_id: str | None = None
@dataclass(frozen=True)
class ExportJob:
job_id: str
mode: str
url: str
status: str
submitter: str | None
note: str | None
requested_at: float
started_at: float | None
finished_at: float | None
output_paths: tuple[str, ...]
error: str | None
@dataclass(frozen=True)
class DeleteToken:
slot: int
generation: int
reason: str
@dataclass(frozen=True)
class SlotGuard:
guard_id: str
slot: int
kind: str
state: str
created_at: float
review_after: float | None
cleared_at: float | None
cleared_by: str | None
clear_reason: str | None
@dataclass(frozen=True)
class PlayerObservation:
availability: str
phase: str
active_slot: int | None
active_path: str | None
paused: bool | None
generation: int
reconciliation_state: str
reconciliation_due_at: float | None
reconciliation_id: str | None
last_status_at: float | None
last_telemetry_at: float | None
@dataclass(frozen=True)
class RouterObservation:
mode: str | None
freshness: str
observed_at: float | None
last_warning: str | None
@dataclass(frozen=True)
class AudioLeaseRecord:
lease_id: str
operation_id: str | None
playback_generation: int | None
state: str
previous_mode: str | None
target_mode: str
restoration_mode: str | None
restoration_source: str
controller_changed: bool
confirmation_deadline: float | None
created_at: float
acquired_at: float | None
released_at: float | None
warning: str | None
error: str | None
@dataclass(frozen=True)
class MigrationReport:
migrated: bool
source_path: str | None = None
source_sha256: str | None = None
backup_path: str | None = None
warnings: tuple[str, ...] = ()
class StoreError(RuntimeError):
"""Base persistence error."""
class MigrationError(StoreError):
"""Legacy migration was rejected or could not complete safely."""
class SlotProtectedError(StoreError):
"""A slot cannot be modified because live activity protects it."""
class StaleOperationError(StoreError):
"""An operation token no longer identifies the slot occupant."""