feature/capability-based-deps #53
@@ -108,7 +108,88 @@ def capture_frames(
|
|||||||
)
|
)
|
||||||
ctx.params = params
|
ctx.params = params
|
||||||
|
|
||||||
# Add message overlay stage if enabled
|
# Add stages based on source type (similar to pipeline_runner)
|
||||||
|
from engine.display import DisplayRegistry
|
||||||
|
from engine.pipeline.adapters import create_stage_from_display
|
||||||
|
from engine.data_sources.sources import EmptyDataSource
|
||||||
|
from engine.pipeline.adapters import DataSourceStage
|
||||||
|
|
||||||
|
# Add source stage
|
||||||
|
if preset.source == "empty":
|
||||||
|
source_stage = DataSourceStage(
|
||||||
|
EmptyDataSource(width=preset.viewport_width, height=preset.viewport_height),
|
||||||
|
name="empty",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# For headlines/poetry, use the actual source
|
||||||
|
from engine.data_sources.sources import HeadlinesDataSource, PoetryDataSource
|
||||||
|
|
||||||
|
if preset.source == "headlines":
|
||||||
|
source_stage = DataSourceStage(HeadlinesDataSource(), name="headlines")
|
||||||
|
elif preset.source == "poetry":
|
||||||
|
source_stage = DataSourceStage(PoetryDataSource(), name="poetry")
|
||||||
|
else:
|
||||||
|
# Fallback to empty
|
||||||
|
source_stage = DataSourceStage(
|
||||||
|
EmptyDataSource(
|
||||||
|
width=preset.viewport_width, height=preset.viewport_height
|
||||||
|
),
|
||||||
|
name="empty",
|
||||||
|
)
|
||||||
|
pipeline.add_stage("source", source_stage)
|
||||||
|
|
||||||
|
# Add font stage for headlines/poetry (with viewport filter)
|
||||||
|
if preset.source in ["headlines", "poetry"]:
|
||||||
|
from engine.pipeline.adapters import FontStage, ViewportFilterStage
|
||||||
|
|
||||||
|
# Add viewport filter to prevent rendering all items
|
||||||
|
pipeline.add_stage(
|
||||||
|
"viewport_filter", ViewportFilterStage(name="viewport-filter")
|
||||||
|
)
|
||||||
|
# Add font stage for block character rendering
|
||||||
|
pipeline.add_stage("font", FontStage(name="font"))
|
||||||
|
else:
|
||||||
|
# Fallback to simple conversion for empty/other sources
|
||||||
|
from engine.pipeline.adapters import SourceItemsToBufferStage
|
||||||
|
|
||||||
|
pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))
|
||||||
|
|
||||||
|
# Add camera stage
|
||||||
|
from engine.camera import Camera
|
||||||
|
from engine.pipeline.adapters import CameraStage, CameraClockStage
|
||||||
|
|
||||||
|
# Create camera based on preset
|
||||||
|
if preset.camera == "feed":
|
||||||
|
camera = Camera.feed()
|
||||||
|
elif preset.camera == "scroll":
|
||||||
|
camera = Camera.scroll(speed=0.1)
|
||||||
|
elif preset.camera == "horizontal":
|
||||||
|
camera = Camera.horizontal(speed=0.1)
|
||||||
|
else:
|
||||||
|
camera = Camera.feed()
|
||||||
|
|
||||||
|
camera.set_canvas_size(preset.viewport_width, preset.viewport_height * 2)
|
||||||
|
|
||||||
|
# Add camera update (for animation)
|
||||||
|
pipeline.add_stage("camera_update", CameraClockStage(camera, name="camera-clock"))
|
||||||
|
# Add camera stage
|
||||||
|
pipeline.add_stage("camera", CameraStage(camera, name=preset.camera))
|
||||||
|
|
||||||
|
# Add effects
|
||||||
|
if preset.effects:
|
||||||
|
from engine.effects.registry import EffectRegistry
|
||||||
|
from engine.pipeline.adapters import create_stage_from_effect
|
||||||
|
|
||||||
|
effect_registry = EffectRegistry()
|
||||||
|
for effect_name in preset.effects:
|
||||||
|
effect = effect_registry.get(effect_name)
|
||||||
|
if effect:
|
||||||
|
pipeline.add_stage(
|
||||||
|
f"effect_{effect_name}",
|
||||||
|
create_stage_from_effect(effect, effect_name),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add message overlay stage if enabled (BEFORE display)
|
||||||
if getattr(preset, "enable_message_overlay", False):
|
if getattr(preset, "enable_message_overlay", False):
|
||||||
from engine.pipeline.adapters import MessageOverlayConfig, MessageOverlayStage
|
from engine.pipeline.adapters import MessageOverlayConfig, MessageOverlayStage
|
||||||
|
|
||||||
@@ -120,9 +201,21 @@ def capture_frames(
|
|||||||
"message_overlay", MessageOverlayStage(config=overlay_config)
|
"message_overlay", MessageOverlayStage(config=overlay_config)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add null display stage (LAST)
|
||||||
|
null_display = DisplayRegistry.create("null")
|
||||||
|
if null_display:
|
||||||
|
pipeline.add_stage("display", create_stage_from_display(null_display, "null"))
|
||||||
|
|
||||||
# Build pipeline
|
# Build pipeline
|
||||||
pipeline.build()
|
pipeline.build()
|
||||||
|
|
||||||
|
# Enable recording on null display if available
|
||||||
|
display_stage = pipeline._stages.get("display")
|
||||||
|
if display_stage and hasattr(display_stage, "_display"):
|
||||||
|
backend = display_stage._display
|
||||||
|
if hasattr(backend, "start_recording"):
|
||||||
|
backend.start_recording()
|
||||||
|
|
||||||
# Capture frames
|
# Capture frames
|
||||||
frames = []
|
frames = []
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@@ -132,18 +225,36 @@ def capture_frames(
|
|||||||
stage_result = pipeline.execute()
|
stage_result = pipeline.execute()
|
||||||
frame_time = time.time() - frame_start
|
frame_time = time.time() - frame_start
|
||||||
|
|
||||||
# Extract buffer from result
|
# Get frames from display recording
|
||||||
buffer = stage_result.data if stage_result.success else []
|
display_stage = pipeline._stages.get("display")
|
||||||
|
if display_stage and hasattr(display_stage, "_display"):
|
||||||
|
backend = display_stage._display
|
||||||
|
if hasattr(backend, "get_recorded_data"):
|
||||||
|
recorded_frames = backend.get_recorded_data()
|
||||||
|
# Add render_time_ms to each frame
|
||||||
|
for frame in recorded_frames:
|
||||||
|
frame["render_time_ms"] = frame_time * 1000
|
||||||
|
frames = recorded_frames
|
||||||
|
|
||||||
frames.append(
|
# Fallback: create empty frames if no recording
|
||||||
{
|
if not frames:
|
||||||
"frame_number": i,
|
for i in range(frame_count):
|
||||||
"buffer": buffer,
|
frames.append(
|
||||||
"width": preset.viewport_width,
|
{
|
||||||
"height": preset.viewport_height,
|
"frame_number": i,
|
||||||
"render_time_ms": frame_time * 1000,
|
"buffer": [],
|
||||||
}
|
"width": preset.viewport_width,
|
||||||
)
|
"height": preset.viewport_height,
|
||||||
|
"render_time_ms": frame_time * 1000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Stop recording on null display
|
||||||
|
display_stage = pipeline._stages.get("display")
|
||||||
|
if display_stage and hasattr(display_stage, "_display"):
|
||||||
|
backend = display_stage._display
|
||||||
|
if hasattr(backend, "stop_recording"):
|
||||||
|
backend.stop_recording()
|
||||||
|
|
||||||
total_time = time.time() - start_time
|
total_time = time.time() - start_time
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user