WIP #35

Open
david wants to merge 135 commits from klubhaus/sideline:feature/capability-based-deps into main
Showing only changes of commit 7d4623b009 - Show all commits

View File

@@ -108,7 +108,88 @@ def capture_frames(
)
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):
from engine.pipeline.adapters import MessageOverlayConfig, MessageOverlayStage
@@ -120,9 +201,21 @@ def capture_frames(
"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
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
frames = []
start_time = time.time()
@@ -132,18 +225,36 @@ def capture_frames(
stage_result = pipeline.execute()
frame_time = time.time() - frame_start
# Extract buffer from result
buffer = stage_result.data if stage_result.success else []
# Get frames from display recording
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(
{
"frame_number": i,
"buffer": buffer,
"width": preset.viewport_width,
"height": preset.viewport_height,
"render_time_ms": frame_time * 1000,
}
)
# Fallback: create empty frames if no recording
if not frames:
for i in range(frame_count):
frames.append(
{
"frame_number": i,
"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