Coverage for src / lexigram / contracts / multimedia / protocols.py: 100%
20 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Structural protocols for multimedia generation providers.
3Four separate Protocols rather than one shared base class — Python's
4structural typing makes a base class unnecessary, and each media type's
5request/response shape differs enough that a shared abstract method
6signature would need to be typed loosely (``Any``) to fit all four.
7"""
9from __future__ import annotations
11from collections.abc import Callable
12from typing import TYPE_CHECKING, Protocol, runtime_checkable
14if TYPE_CHECKING:
15 from lexigram.contracts.core.result import Result
16 from lexigram.contracts.multimedia.exceptions import (
17 MultimediaError,
18 VideoGenerationError,
19 )
20 from lexigram.contracts.multimedia.types import (
21 BeatAnalysisRequest,
22 BeatAnalysisResult,
23 ImageRequest,
24 InterpolationRequest,
25 MediaAsset,
26 MusicRequest,
27 TTSRequest,
28 UpscaleRequest,
29 VideoOperation,
30 VideoRequest,
31 )
34@runtime_checkable
35class TTSProvider(Protocol):
36 """Protocol for text-to-speech generation backends."""
38 async def generate(
39 self, request: TTSRequest
40 ) -> Result[MediaAsset, MultimediaError]: ...
43@runtime_checkable
44class InterpolationProvider(Protocol):
45 """Protocol for two-frame midpoint interpolation backends."""
47 async def interpolate(
48 self, request: InterpolationRequest
49 ) -> Result[MediaAsset, MultimediaError]: ...
52@runtime_checkable
53class MusicProvider(Protocol):
54 """Protocol for music/sound generation backends."""
56 async def generate(
57 self, request: MusicRequest
58 ) -> Result[MediaAsset, MultimediaError]: ...
61@runtime_checkable
62class VideoProvider(Protocol):
63 """Protocol for video generation backends."""
65 async def generate(
66 self, request: VideoRequest
67 ) -> Result[MediaAsset, MultimediaError]: ...
70@runtime_checkable
71class VideoProcessor(Protocol):
72 """Protocol for ffmpeg-backed video processing/editing backends.
74 `progress_callback` receives `0.0 <= pct <= 1.0`; implementations
75 should emit `1.0` exactly once when processing finishes successfully.
76 """
78 async def process(
79 self,
80 operation: VideoOperation,
81 *,
82 progress_callback: Callable[[float], None] | None = None,
83 ) -> Result[MediaAsset, VideoGenerationError]: ...
85 async def extract_frames(
86 self, asset: MediaAsset, *, fps: float | None = None
87 ) -> Result[list[MediaAsset], VideoGenerationError]: ...
89 async def assemble_frames(
90 self, frames: list[MediaAsset], *, fps: float
91 ) -> Result[MediaAsset, VideoGenerationError]: ...
94@runtime_checkable
95class BeatAnalysisProvider(Protocol):
96 """Protocol for audio tempo/beat-detection backends."""
98 async def analyze(
99 self, request: BeatAnalysisRequest
100 ) -> Result[BeatAnalysisResult, MultimediaError]: ...
103@runtime_checkable
104class ImageProvider(Protocol):
105 """Protocol for still-image generation backends."""
107 async def generate(
108 self, request: ImageRequest
109 ) -> Result[MediaAsset, MultimediaError]: ...
112@runtime_checkable
113class UpscaleProvider(Protocol):
114 """Protocol for single-image super-resolution backends."""
116 async def upscale(
117 self, request: UpscaleRequest
118 ) -> Result[MediaAsset, MultimediaError]: ...
121__all__ = [
122 "BeatAnalysisProvider",
123 "ImageProvider",
124 "InterpolationProvider",
125 "MusicProvider",
126 "TTSProvider",
127 "UpscaleProvider",
128 "VideoProcessor",
129 "VideoProvider",
130]