Add Picture Streams architecture with postprocessing templates and stream test UI

Introduce Picture Stream abstraction that separates the capture pipeline into
composable layers: raw streams (display + capture engine + FPS) and processed
streams (source stream + postprocessing template). Devices reference a picture
stream instead of managing individual capture settings.

- Add PictureStream and PostprocessingTemplate data models and stores
- Add CRUD API endpoints for picture streams and postprocessing templates
- Add stream chain resolution in ProcessorManager for start_processing
- Add picture stream test endpoint with postprocessing preview support
- Add Stream Settings modal with border_width and interpolation_mode controls
- Add stream test modal with capture preview and performance metrics
- Add full frontend: Picture Streams tab, Processing Templates tab, stream
  selector on device cards, test buttons on stream cards
- Add localization keys for all new features (en, ru)
- Migrate existing devices to picture streams on startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 00:00:30 +03:00
parent 3db7ba4b0e
commit 493f14fba9
23 changed files with 2773 additions and 200 deletions

View File

@@ -13,7 +13,6 @@ class CaptureTemplate:
name: str
engine_type: str
engine_config: Dict[str, Any]
is_default: bool
created_at: datetime
updated_at: datetime
description: Optional[str] = None
@@ -29,7 +28,6 @@ class CaptureTemplate:
"name": self.name,
"engine_type": self.engine_type,
"engine_config": self.engine_config,
"is_default": self.is_default,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
"description": self.description,
@@ -50,7 +48,6 @@ class CaptureTemplate:
name=data["name"],
engine_type=data["engine_type"],
engine_config=data.get("engine_config", {}),
is_default=data.get("is_default", False),
created_at=datetime.fromisoformat(data["created_at"])
if isinstance(data.get("created_at"), str)
else data.get("created_at", datetime.utcnow()),