bae2166bc2
Introduces an engine+template abstraction for audio capture, mirroring the existing screen capture engine pattern. This enables multiple audio backends (WASAPI for Windows, sounddevice for cross-platform) with per-source engine configuration via reusable templates. Backend: - AudioCaptureEngine ABC with WasapiEngine and SounddeviceEngine implementations - AudioEngineRegistry for engine discovery and factory creation - AudioAnalyzer class decouples FFT/RMS/beat analysis from engine-specific capture - ManagedAudioStream wraps engine stream + analyzer in background thread - AudioCaptureTemplate model and AudioTemplateStore with JSON CRUD - AudioCaptureManager keyed by (engine_type, device_index, is_loopback) - Auto-migration: default template created on startup, assigned to existing sources - Full REST API: CRUD for audio templates + engine listing with availability flags - audio_template_id added to MultichannelAudioSource model and API schemas Frontend: - Audio template cards in Streams > Audio tab with engine badge and config details - Audio template editor modal with engine selector and dynamic config fields - Audio template dropdown in multichannel audio source editor - Template name crosslink badge on multichannel audio source cards - Confirm modal z-index fix (always stacks above editor modals) - i18n keys for EN and RU Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
"""Audio capture template data model."""
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Any, Dict, Optional
|
|
|
|
|
|
@dataclass
|
|
class AudioCaptureTemplate:
|
|
"""Represents an audio capture template configuration."""
|
|
|
|
id: str
|
|
name: str
|
|
engine_type: str
|
|
engine_config: Dict[str, Any]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
description: Optional[str] = None
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert template to dictionary."""
|
|
return {
|
|
"id": self.id,
|
|
"name": self.name,
|
|
"engine_type": self.engine_type,
|
|
"engine_config": self.engine_config,
|
|
"created_at": self.created_at.isoformat(),
|
|
"updated_at": self.updated_at.isoformat(),
|
|
"description": self.description,
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> "AudioCaptureTemplate":
|
|
"""Create template from dictionary."""
|
|
return cls(
|
|
id=data["id"],
|
|
name=data["name"],
|
|
engine_type=data["engine_type"],
|
|
engine_config=data.get("engine_config", {}),
|
|
created_at=datetime.fromisoformat(data["created_at"])
|
|
if isinstance(data.get("created_at"), str)
|
|
else data.get("created_at", datetime.utcnow()),
|
|
updated_at=datetime.fromisoformat(data["updated_at"])
|
|
if isinstance(data.get("updated_at"), str)
|
|
else data.get("updated_at", datetime.utcnow()),
|
|
description=data.get("description"),
|
|
)
|