fix(streaming): missing HLS segments in edge requests #10

Merged
dylan merged 6 commits from fix/stream-edge-handling into master 2026-07-29 18:50:00 +01:00
Showing only changes of commit 2378e42199 - Show all commits

View File

@@ -45,6 +45,23 @@ class StreamActionsServiceTest {
Files.createFile(tempDir.resolve(name));
}
/**
* Stubs the two-arg {@link CommandRunner#run} so the head re-encode pass
* actually writes a non-empty file at its output path — {@code encodeHead}
* verifies its output exists and is non-empty before continuing. The concat
* pass (no {@code mpegts} muxer) is left untouched. Returns {@code output}
* for both invocations.
*/
private void stubRunnerProducingHead(CommandOutput output) throws IOException, InterruptedException {
when(commandRunner.run(anyList(), any())).thenAnswer(inv -> {
List<String> cmd = inv.getArgument(0);
if (cmd.contains("mpegts")) {
Files.writeString(Path.of(cmd.get(cmd.size() - 1)), "x");
}
return output;
});
}
// ---------------------------------------------------------------
// getSegmentsInRange: missing / invalid directories
// ---------------------------------------------------------------
@@ -279,23 +296,25 @@ class StreamActionsServiceTest {
ProgressTracker progress = new ProgressTracker();
CommandOutput commandOutput = new CommandOutput();
when(commandRunner.run(anyList())).thenReturn(commandOutput);
when(commandRunner.run(anyList(), any())).thenReturn(commandOutput);
stubRunnerProducingHead(commandOutput);
var future = service.saveSection(List.of(segmentA, segmentB), 5.0f, 12.0f, output, progress);
assertThat(future.get()).isSameAs(commandOutput);
assertThat(progress.isComplete()).isTrue();
// saveSection issues two ffmpeg passes: the head re-encode (trims the
// first segment via -ss) followed by the stream-copy concat (-t/-c copy).
@SuppressWarnings("unchecked")
ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);
Mockito.verify(commandRunner).run(captor.capture(), any());
List<String> command = captor.getValue();
Mockito.verify(commandRunner, Mockito.times(2)).run(captor.capture(), any());
List<String> headCommand = captor.getAllValues().get(0);
List<String> concatCommand = captor.getAllValues().get(1);
assertThat(command).containsSequence("-ss", "5.0");
assertThat(command).containsSequence("-t", "12.0");
assertThat(command).containsSequence("-c", "copy");
assertThat(command).endsWith(output.toAbsolutePath().toString());
assertThat(headCommand).containsSequence("-ss", "5.0");
assertThat(concatCommand).containsSequence("-t", "12.0");
assertThat(concatCommand).containsSequence("-c", "copy");
assertThat(concatCommand).endsWith(output.toAbsolutePath().toString());
}
@Test
@@ -303,7 +322,7 @@ class StreamActionsServiceTest {
Path output = Path.of("/tmp/out/clip.mp4");
ProgressTracker progress = new ProgressTracker();
when(commandRunner.run(anyList())).thenReturn(new CommandOutput());
// The head re-encode is the first ffmpeg pass; its failure must surface.
when(commandRunner.run(anyList(), any())).thenThrow(new IOException("ffmpeg boom"));
var future = service.saveSection(List.of(Path.of("/tmp/a.ts")), 0f, 10f, output, progress);
@@ -328,9 +347,12 @@ class StreamActionsServiceTest {
}
@Test
void saveSection_nullOutputFile_completesExceptionally() throws IOException {
void saveSection_nullOutputFile_completesExceptionally() throws Exception {
ProgressTracker progress = new ProgressTracker();
// Let the head pass succeed so execution reaches the null output file.
stubRunnerProducingHead(new CommandOutput());
var future = service.saveSection(List.of(Path.of("/tmp/a.ts")), 0f, 10f, null, progress);
assertThatThrownBy(future::get)