fix(media): a played-out clip can play again; play button beside the Video File label

Reported 2026-11-05: "when I click Loop, the video doesn't play in the preview pane". Two
causes, both real:

- `VideoClipPipe.StartAsync` bailed out once it had run once (`_run != null`), so a clip that
  reached EOF could never be started again — it held its last frame forever.
- Loop is only consulted at end-of-file by a LIVE decoder, so flipping it on a finished clip
  did nothing at all.

Now: the pipe reports running state and is restartable after a natural end; the manager marks a
session finished (keeping its last frame) and exposes `IsRunning`/`RestartAsync`; the layer shows
`IsMediaRunning`/`IsMediaFinished`; flipping Loop on a finished clip restarts it.

UI: `▶ Play` sits to the right of the **Video File** label and is enabled only while Loop is off
(creator ruling: with Loop on the clip already runs continuously, so there is nothing to start).
The panel says "Clip finished — holding the last frame." so a frozen picture explains itself.

Also: verify.sh no longer runs physical-input tests by default. That test moves the real cursor
(SetCursorPos + mouse_event); running it mid-gate hijacks the machine someone is using, which is
exactly how it felt. Opt in with PHYSICAL_TESTS=1 when the desktop is idle; the header documents
the scoped-run command for working hours.

Test: `MediaPlaybackRestartTests` (pipe restart, finished state, Play again, Loop-on-finished).
8 scoped tests green. FULL SUITE NOT RUN — the machine was in use; run scripts/verify.sh in a
quiet window before the next milestone.
This commit is contained in:
2026-11-05 09:15:30 -08:00
parent e37215d9c4
commit 242a13ca30
8 changed files with 327 additions and 9 deletions
+12 -1
View File
@@ -729,7 +729,15 @@
<!-- Media (video file) config — TASK 21 -->
<StackPanel Visibility="{Binding SelectedElement.IsMediaSource, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Video File" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="0,0,0,0">
<Button DockPanel.Dock="Right" Content="&#x25B6; Play"
Command="{Binding ReplayMediaCommand}"
CommandParameter="{Binding SelectedElement}"
IsEnabled="{Binding SelectedElement.CanPlayMedia}"
Style="{StaticResource PropertyButton}" Padding="8,2" FontSize="11"
ToolTip="Play the clip in the preview. While Loop is on the clip runs continuously, so there is nothing to start."/>
<TextBlock Text="Video File" Style="{StaticResource PropertyHeader}" VerticalAlignment="Center"/>
</DockPanel>
<Grid Margin="10,0,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -746,6 +754,9 @@
<CheckBox Style="{StaticResource PropertyPillCheck}" Margin="10,4,0,4"
Content="Loop" IsChecked="{Binding SelectedElement.MediaIsLooping, Mode=TwoWay}"
ToolTip="Restart from the beginning at the end of the file — for starting-soon and BRB loops"/>
<TextBlock Text="Clip finished — holding the last frame."
Style="{StaticResource PropertyHint}"
Visibility="{Binding SelectedElement.IsMediaFinished, Converter={StaticResource BoolToVis}}"/>
<DockPanel Margin="10,0,0,4">
<CheckBox DockPanel.Dock="Left" Style="{StaticResource PropertyPillCheck}"
Content="Mute" IsChecked="{Binding SelectedElement.MediaMuted, Mode=TwoWay}"