Adds tests for FFmpeg
This commit is contained in:
@@ -8,7 +8,6 @@ import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.impl.AudioInfoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.FileAudioImpl;
|
||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
@@ -60,7 +59,7 @@ public class FFmpegVideoConverter implements VideoConverter {
|
||||
public Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles) throws IOException {
|
||||
final var out = getTempFile(video.info().format());
|
||||
addSoftSubtitles(video, subtitles, out);
|
||||
return new FileVideoImpl(out, new VideoInfoImpl(video.info().format(), video.info().width(), video.info().height(), video.info().duration()));
|
||||
return new FileVideoImpl(out, video.info());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,7 +104,7 @@ public class FFmpegVideoConverter implements VideoConverter {
|
||||
args.add("language=" + c.language().iso3());
|
||||
});
|
||||
args.add(path.toString());
|
||||
runListen(args, Duration.ofHours(1));
|
||||
runLog(args, Duration.ofHours(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,13 +122,14 @@ public class FFmpegVideoConverter implements VideoConverter {
|
||||
final var subtitleArg = getSubtitleConverter().formatName().equalsIgnoreCase("ass") ? "ass='" + escapedPath + "'" : "subtitles='" + escapedPath + "'";
|
||||
final var args = List.of(
|
||||
getFFmpegPath(),
|
||||
"-y",
|
||||
"-i",
|
||||
videoPath.toString(),
|
||||
"-vf",
|
||||
subtitleArg,
|
||||
path.toString()
|
||||
);
|
||||
runListen(args, Duration.ofHours(1));
|
||||
runLog(args, Duration.ofHours(1));
|
||||
}
|
||||
|
||||
private SubtitleConverter<?> getSubtitleConverter() {
|
||||
@@ -158,7 +158,7 @@ public class FFmpegVideoConverter implements VideoConverter {
|
||||
"0:v",
|
||||
dumpVideoPath.toString()
|
||||
);
|
||||
runListen(args, Duration.ofHours(1));
|
||||
runLog(args, Duration.ofHours(1));
|
||||
Files.deleteIfExists(dumpVideoPath);
|
||||
return new FileAudioImpl(audioPath, new AudioInfoImpl("wav", video.info().duration()));
|
||||
}
|
||||
@@ -203,7 +203,7 @@ public class FFmpegVideoConverter implements VideoConverter {
|
||||
return Files.isRegularFile(bundledPath) ? bundledPath.toString() : systemPath.toString();
|
||||
}
|
||||
|
||||
private void runListen(final List<String> args, final Duration duration) throws IOException {
|
||||
private void runLog(final List<String> args, final Duration duration) throws IOException {
|
||||
final var listener = processRunner.startListen(args);
|
||||
var line = listener.readLine();
|
||||
final var processName = args.getFirst();
|
||||
@@ -211,6 +211,9 @@ public class FFmpegVideoConverter implements VideoConverter {
|
||||
logger.info("[{}]: {}", processName, line);
|
||||
line = listener.readLine();
|
||||
}
|
||||
listener.join(duration);
|
||||
final var result = listener.join(duration);
|
||||
if (result.exitCode() != 0) {
|
||||
throw new IOException("FFmpeg error : " + result.exitCode() + " - " + result.output());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,15 +37,19 @@ public class FFprobeVideoLoader implements VideoLoader {
|
||||
@Override
|
||||
public Video loadVideo(final Path path) throws IOException {
|
||||
final var result = processRunner.run(List.of(getFFprobePath(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", path.toString()), Duration.ofSeconds(5));
|
||||
final var resolution = result.output().getLast();
|
||||
final var split = resolution.split(",");
|
||||
final var width = Integer.parseInt(split[0]);
|
||||
final var height = Integer.parseInt(split[1]);
|
||||
final var filename = path.getFileName().toString();
|
||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
final var duration = (long) (Double.parseDouble(split[2]) * 1000L);
|
||||
final var info = new VideoInfoImpl(extension, width, height, duration);
|
||||
return new FileVideoImpl(path, info);
|
||||
if (result.exitCode() == 0) {
|
||||
final var resolution = result.output().getLast();
|
||||
final var split = resolution.split(",");
|
||||
final var width = Integer.parseInt(split[0]);
|
||||
final var height = Integer.parseInt(split[1]);
|
||||
final var filename = path.getFileName().toString();
|
||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
final var duration = (long) (Double.parseDouble(split[2]) * 1000L);
|
||||
final var info = new VideoInfoImpl(extension, width, height, duration);
|
||||
return new FileVideoImpl(path, info);
|
||||
} else {
|
||||
throw new IOException("FFprobe error : " + result.exitCode() + " - " + result.output());
|
||||
}
|
||||
}
|
||||
|
||||
private String getFFprobePath() {
|
||||
|
||||
@@ -1,66 +1,311 @@
|
||||
package com.github.gtache.autosubtitle.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.impl.AudioInfoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||
import com.github.gtache.autosubtitle.process.ProcessListener;
|
||||
import com.github.gtache.autosubtitle.process.ProcessResult;
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestFFmpegVideoConverter {
|
||||
|
||||
private final FFmpegVideoConverter converter;
|
||||
private final ProcessRunner runner;
|
||||
private final SubtitleConverter<Subtitle> subtitleConverter;
|
||||
private final ProcessListener listener;
|
||||
private final ProcessResult result;
|
||||
private final SubtitleConverter<?> subtitleConverter;
|
||||
private final SubtitleConverterProvider subtitleConverterProvider;
|
||||
private final Video video;
|
||||
private final InputStream in;
|
||||
private final VideoInfo videoInfo;
|
||||
private final Path tmpFile;
|
||||
private final Path outputPath;
|
||||
private final SubtitleCollection<?> collection;
|
||||
private final SubtitleCollection<?> frCollection;
|
||||
private final SubtitleCollection<?> deCollection;
|
||||
private final Preferences preferences;
|
||||
private final Path systemPath;
|
||||
|
||||
TestFFmpegVideoConverter(@Mock final SubtitleConverter<Subtitle> subtitleConverter, @Mock final ProcessRunner runner, @Mock final SubtitleConverterProvider subtitleConverterProvider, @Mock final Video video,
|
||||
@Mock final VideoInfo videoInfo, @Mock final SubtitleCollection<?> collection, @Mock final Preferences preferences) throws IOException {
|
||||
final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp");
|
||||
final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffmpeg.exe" : "fake-ffmpeg.sh";
|
||||
this.video = Objects.requireNonNull(video);
|
||||
this.runner = Objects.requireNonNull(runner);
|
||||
this.videoInfo = Objects.requireNonNull(videoInfo);
|
||||
TestFFmpegVideoConverter(@Mock final SubtitleConverter<?> subtitleConverter, @Mock final ProcessRunner runner,
|
||||
@Mock final SubtitleConverterProvider subtitleConverterProvider, @Mock final Video video,
|
||||
@Mock final VideoInfo videoInfo, @Mock final SubtitleCollection<?> frCollection, @Mock final SubtitleCollection<?> deCollection,
|
||||
@Mock final Preferences preferences, @Mock final ProcessListener listener, @Mock final InputStream in,
|
||||
@Mock final ProcessResult result) {
|
||||
this.video = requireNonNull(video);
|
||||
this.runner = requireNonNull(runner);
|
||||
this.result = requireNonNull(result);
|
||||
this.videoInfo = requireNonNull(videoInfo);
|
||||
this.subtitleConverter = requireNonNull(subtitleConverter);
|
||||
this.subtitleConverterProvider = requireNonNull(subtitleConverterProvider);
|
||||
this.preferences = requireNonNull(preferences);
|
||||
this.listener = requireNonNull(listener);
|
||||
this.in = requireNonNull(in);
|
||||
this.systemPath = Paths.get("system");
|
||||
this.frCollection = requireNonNull(frCollection);
|
||||
this.deCollection = requireNonNull(deCollection);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws IOException {
|
||||
when(video.info()).thenReturn(videoInfo);
|
||||
this.tmpFile = Files.createTempFile("fake-ffmpeg", resource.substring(resource.lastIndexOf('.')));
|
||||
try (final var in = getClass().getResourceAsStream(resource)) {
|
||||
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
this.outputPath = Path.of(output, "test-ffmpeg-output.txt");
|
||||
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
||||
this.subtitleConverterProvider = Objects.requireNonNull(subtitleConverterProvider);
|
||||
this.preferences = Objects.requireNonNull(preferences);
|
||||
this.converter = new FFmpegVideoConverter(tmpFile, tmpFile, subtitleConverterProvider, preferences, runner);
|
||||
this.collection = Objects.requireNonNull(collection);
|
||||
when(subtitleConverterProvider.getConverter("srt")).thenReturn((SubtitleConverter) subtitleConverter);
|
||||
when(subtitleConverter.formatName()).thenReturn("srt");
|
||||
when(subtitleConverter.format(any(), any())).thenReturn("");
|
||||
when(preferences.get("outputFormat", "srt")).thenReturn("srt");
|
||||
when(video.getInputStream()).thenReturn(in);
|
||||
when(frCollection.language()).thenReturn(Language.FR);
|
||||
when(deCollection.language()).thenReturn(Language.DE);
|
||||
when(runner.startListen(anyList())).thenReturn(listener);
|
||||
when(listener.join(Duration.ofHours(1))).thenReturn(result);
|
||||
when(result.exitCode()).thenReturn(0);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() throws IOException {
|
||||
Files.deleteIfExists(tmpFile);
|
||||
Files.deleteIfExists(outputPath);
|
||||
@Test
|
||||
void testAddSoftSubtitlesMp4() throws IOException {
|
||||
when(videoInfo.format()).thenReturn("mp4");
|
||||
final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner);
|
||||
final var result = converter.addSoftSubtitles(video, List.of(frCollection, deCollection));
|
||||
|
||||
assertEquals(videoInfo, result.info());
|
||||
|
||||
final var args = getArgs();
|
||||
assertEquals(27, args.size());
|
||||
final var argMap = new HashMap<Integer, String>();
|
||||
argMap.put(0, systemPath.toString());
|
||||
argMap.put(1, "-y");
|
||||
argMap.put(2, "-i");
|
||||
argMap.put(4, "-i");
|
||||
argMap.put(6, "-i");
|
||||
argMap.put(8, "-map");
|
||||
argMap.put(9, "0:v");
|
||||
argMap.put(10, "-map");
|
||||
argMap.put(11, "0:a");
|
||||
argMap.put(12, "-map");
|
||||
argMap.put(13, "1");
|
||||
argMap.put(14, "-map");
|
||||
argMap.put(15, "2");
|
||||
argMap.put(16, "-c:v");
|
||||
argMap.put(17, "copy");
|
||||
argMap.put(18, "-c:a");
|
||||
argMap.put(19, "copy");
|
||||
argMap.put(20, "-c:s");
|
||||
argMap.put(21, "mov_text");
|
||||
argMap.put(22, "-metadata:s:s:0");
|
||||
argMap.put(23, "language=fra");
|
||||
argMap.put(24, "-metadata:s:s:1");
|
||||
argMap.put(25, "language=deu");
|
||||
|
||||
checkEquals(argMap, args);
|
||||
assertTrue(args.get(3).endsWith(".mp4"));
|
||||
assertTrue(args.get(5).endsWith(".srt"));
|
||||
assertTrue(args.get(7).endsWith(".srt"));
|
||||
assertTrue(args.get(26).endsWith(".mp4"));
|
||||
verify(subtitleConverter).format(frCollection, videoInfo);
|
||||
verify(subtitleConverter).format(deCollection, videoInfo);
|
||||
verify(in).transferTo(any());
|
||||
}
|
||||
|
||||
//TODO tests
|
||||
private static void checkEquals(final Map<Integer, String> argMap, final List<String> args) {
|
||||
argMap.forEach((key, value) -> assertEquals(value, args.get(key)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddSoftSubtitlesMkvPathFile() throws IOException {
|
||||
when(videoInfo.format()).thenReturn("mkv");
|
||||
doReturn(null).when(subtitleConverterProvider).getConverter("srt");
|
||||
when(subtitleConverterProvider.getConverter("ass")).thenReturn((SubtitleConverter) subtitleConverter);
|
||||
when(subtitleConverter.formatName()).thenReturn("ass");
|
||||
when(preferences.get("outputFormat", "srt")).thenReturn("ass");
|
||||
final var fileVideo = mock(FileVideoImpl.class);
|
||||
when(fileVideo.info()).thenReturn(videoInfo);
|
||||
final var path = Paths.get("path.mkv");
|
||||
when(fileVideo.path()).thenReturn(path);
|
||||
final var out = Paths.get("out.mkv");
|
||||
final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner);
|
||||
converter.addSoftSubtitles(fileVideo, List.of(frCollection, deCollection), out);
|
||||
|
||||
final var args = getArgs();
|
||||
assertEquals(27, args.size());
|
||||
final var argMap = new HashMap<Integer, String>();
|
||||
argMap.put(0, systemPath.toString());
|
||||
argMap.put(1, "-y");
|
||||
argMap.put(2, "-i");
|
||||
argMap.put(3, path.toString());
|
||||
argMap.put(4, "-i");
|
||||
argMap.put(6, "-i");
|
||||
argMap.put(8, "-map");
|
||||
argMap.put(9, "0:v");
|
||||
argMap.put(10, "-map");
|
||||
argMap.put(11, "0:a");
|
||||
argMap.put(12, "-map");
|
||||
argMap.put(13, "1");
|
||||
argMap.put(14, "-map");
|
||||
argMap.put(15, "2");
|
||||
argMap.put(16, "-c:v");
|
||||
argMap.put(17, "copy");
|
||||
argMap.put(18, "-c:a");
|
||||
argMap.put(19, "copy");
|
||||
argMap.put(20, "-c:s");
|
||||
argMap.put(21, "copy");
|
||||
argMap.put(22, "-metadata:s:s:0");
|
||||
argMap.put(23, "language=fra");
|
||||
argMap.put(24, "-metadata:s:s:1");
|
||||
argMap.put(25, "language=deu");
|
||||
argMap.put(26, out.toString());
|
||||
|
||||
checkEquals(argMap, args);
|
||||
assertTrue(args.get(5).endsWith(".ass"));
|
||||
assertTrue(args.get(7).endsWith(".ass"));
|
||||
verify(subtitleConverter).format(frCollection, videoInfo);
|
||||
verify(subtitleConverter).format(deCollection, videoInfo);
|
||||
verifyNoInteractions(in);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddHardSubtitles() throws IOException {
|
||||
when(videoInfo.format()).thenReturn("mp4");
|
||||
final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner);
|
||||
final var result = converter.addHardSubtitles(video, frCollection);
|
||||
|
||||
assertEquals(videoInfo, result.info());
|
||||
|
||||
final var args = getArgs();
|
||||
assertEquals(7, args.size());
|
||||
final var argMap = new HashMap<Integer, String>();
|
||||
argMap.put(0, systemPath.toString());
|
||||
argMap.put(1, "-y");
|
||||
argMap.put(2, "-i");
|
||||
argMap.put(4, "-vf");
|
||||
|
||||
checkEquals(argMap, args);
|
||||
assertTrue(args.get(3).endsWith(".mp4"));
|
||||
assertTrue(args.get(5).startsWith("subtitles="));
|
||||
assertTrue(args.get(6).endsWith(".mp4"));
|
||||
verify(subtitleConverter).format(frCollection, videoInfo);
|
||||
verify(in).transferTo(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddHardAssSubtitles() throws IOException {
|
||||
doReturn(null).when(subtitleConverterProvider).getConverter("srt");
|
||||
when(subtitleConverterProvider.getConverter("ass")).thenReturn((SubtitleConverter) subtitleConverter);
|
||||
when(subtitleConverter.formatName()).thenReturn("ass");
|
||||
when(preferences.get("outputFormat", "srt")).thenReturn("ass");
|
||||
when(videoInfo.format()).thenReturn("mp4");
|
||||
final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner);
|
||||
final var result = converter.addHardSubtitles(video, frCollection);
|
||||
|
||||
assertEquals(videoInfo, result.info());
|
||||
|
||||
final var args = getArgs();
|
||||
assertEquals(7, args.size());
|
||||
final var argMap = new HashMap<Integer, String>();
|
||||
argMap.put(0, systemPath.toString());
|
||||
argMap.put(1, "-y");
|
||||
argMap.put(2, "-i");
|
||||
argMap.put(4, "-vf");
|
||||
|
||||
checkEquals(argMap, args);
|
||||
assertTrue(args.get(3).endsWith(".mp4"));
|
||||
assertTrue(args.get(5).startsWith("ass="));
|
||||
assertTrue(args.get(6).endsWith(".mp4"));
|
||||
verify(subtitleConverter).format(frCollection, videoInfo);
|
||||
verify(in).transferTo(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAudio() throws IOException {
|
||||
when(videoInfo.format()).thenReturn("mp4");
|
||||
when(videoInfo.duration()).thenReturn(25000L);
|
||||
final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner);
|
||||
final var result = converter.getAudio(video);
|
||||
|
||||
assertEquals(new AudioInfoImpl("wav", 25000L), result.info());
|
||||
|
||||
final var args = getArgs();
|
||||
assertEquals(10, args.size());
|
||||
final var argMap = new HashMap<Integer, String>();
|
||||
argMap.put(0, systemPath.toString());
|
||||
argMap.put(1, "-y");
|
||||
argMap.put(2, "-i");
|
||||
argMap.put(4, "-map");
|
||||
argMap.put(5, "0:a");
|
||||
argMap.put(7, "-map");
|
||||
argMap.put(8, "0:v");
|
||||
|
||||
checkEquals(argMap, args);
|
||||
assertTrue(args.get(3).endsWith(".mp4"));
|
||||
assertTrue(args.get(6).endsWith(".wav"));
|
||||
assertTrue(args.get(9).endsWith(".mp4"));
|
||||
verify(in).transferTo(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAudioException() {
|
||||
when(result.exitCode()).thenReturn(1);
|
||||
when(videoInfo.format()).thenReturn("mp4");
|
||||
when(videoInfo.duration()).thenReturn(25000L);
|
||||
final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner);
|
||||
assertThrows(IOException.class, () -> converter.getAudio(video));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAudioBundled(@TempDir final Path tempDir) throws IOException {
|
||||
final var bundled = tempDir.resolve("ffmpeg");
|
||||
Files.createFile(bundled);
|
||||
when(videoInfo.format()).thenReturn("mp4");
|
||||
when(videoInfo.duration()).thenReturn(25000L);
|
||||
final var converter = new FFmpegVideoConverter(bundled, systemPath, subtitleConverterProvider, preferences, runner);
|
||||
final var result = converter.getAudio(video);
|
||||
|
||||
assertEquals(new AudioInfoImpl("wav", 25000L), result.info());
|
||||
|
||||
final var args = getArgs();
|
||||
assertEquals(10, args.size());
|
||||
final var argMap = new HashMap<Integer, String>();
|
||||
argMap.put(0, bundled.toString());
|
||||
argMap.put(1, "-y");
|
||||
argMap.put(2, "-i");
|
||||
argMap.put(4, "-map");
|
||||
argMap.put(5, "0:a");
|
||||
argMap.put(7, "-map");
|
||||
argMap.put(8, "0:v");
|
||||
|
||||
checkEquals(argMap, args);
|
||||
assertTrue(args.get(3).endsWith(".mp4"));
|
||||
assertTrue(args.get(6).endsWith(".wav"));
|
||||
assertTrue(args.get(9).endsWith(".mp4"));
|
||||
verify(in).transferTo(any());
|
||||
}
|
||||
|
||||
private List<String> getArgs() throws IOException {
|
||||
final var captor = ArgumentCaptor.forClass(List.class);
|
||||
verify(runner).startListen(captor.capture());
|
||||
return captor.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.github.gtache.autosubtitle.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||
import com.github.gtache.autosubtitle.process.ProcessResult;
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@@ -15,48 +15,70 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestFFmpegVideoLoader {
|
||||
|
||||
private final FFprobeVideoLoader loader;
|
||||
private final ProcessRunner runner;
|
||||
private final Path tmpFile;
|
||||
private final Path outputPath;
|
||||
private final ProcessResult result;
|
||||
private final Path systemPath;
|
||||
private final Path in;
|
||||
|
||||
TestFFmpegVideoLoader(@Mock final ProcessRunner runner) throws IOException {
|
||||
final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp");
|
||||
final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffprobe.exe" : "fake-ffprobe.sh";
|
||||
this.tmpFile = Files.createTempFile("fake-ffprobe", resource.substring(resource.lastIndexOf('.')));
|
||||
try (final var in = getClass().getResourceAsStream(resource)) {
|
||||
if (in == null) {
|
||||
throw new IOException("Unable to find " + resource);
|
||||
}
|
||||
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
this.outputPath = Path.of(output, "test-ffprobe-output.txt");
|
||||
TestFFmpegVideoLoader(@Mock final ProcessRunner runner, @Mock final ProcessResult result) {
|
||||
this.runner = Objects.requireNonNull(runner);
|
||||
this.loader = new FFprobeVideoLoader(tmpFile, tmpFile, runner);
|
||||
this.result = Objects.requireNonNull(result);
|
||||
this.systemPath = Paths.get("system");
|
||||
this.in = Paths.get("in.mp4");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() throws IOException {
|
||||
Files.deleteIfExists(tmpFile);
|
||||
Files.deleteIfExists(outputPath);
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
when(result.exitCode()).thenReturn(0);
|
||||
when(result.output()).thenReturn(List.of("1920,1080,25"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Doesn't work")
|
||||
void testLoadVideo() throws IOException {
|
||||
final var in = Paths.get("in.mp4");
|
||||
void testLoadVideoBundled(@TempDir final Path tempDir) throws IOException {
|
||||
final var bundledPath = tempDir.resolve("ffprobe");
|
||||
Files.createFile(bundledPath);
|
||||
final var args = List.of(bundledPath.toString(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", in.toString());
|
||||
when(runner.run(args, Duration.ofSeconds(5)))
|
||||
.thenReturn(result);
|
||||
final var loader = new FFprobeVideoLoader(bundledPath, systemPath, runner);
|
||||
final var expectedInfo = new VideoInfoImpl("mp4", 1920, 1080, 25000L);
|
||||
final var expected = new FileVideoImpl(in, expectedInfo);
|
||||
final var result = loader.loadVideo(in);
|
||||
assertEquals(expected, result);
|
||||
assertEquals("-v error -select_streams v -show_entries stream=width,height,duration -of csv=p=0 in.mp4", Files.readString(outputPath));
|
||||
final var actual = loader.loadVideo(in);
|
||||
assertEquals(expected, actual);
|
||||
verify(runner).run(args, Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadVideoSystem() throws IOException {
|
||||
final var args = List.of(systemPath.toString(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", in.toString());
|
||||
when(runner.run(args, Duration.ofSeconds(5))).thenReturn(result);
|
||||
final var loader = new FFprobeVideoLoader(Paths.get("bundled"), systemPath, runner);
|
||||
final var expectedInfo = new VideoInfoImpl("mp4", 1920, 1080, 25000L);
|
||||
final var expected = new FileVideoImpl(in, expectedInfo);
|
||||
final var actual = loader.loadVideo(in);
|
||||
assertEquals(expected, actual);
|
||||
verify(runner).run(args, Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadVideoSystemError() throws IOException {
|
||||
final var args = List.of(systemPath.toString(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", in.toString());
|
||||
when(runner.run(args, Duration.ofSeconds(5))).thenReturn(result);
|
||||
final var loader = new FFprobeVideoLoader(Paths.get("bundled"), systemPath, runner);
|
||||
when(result.exitCode()).thenReturn(1);
|
||||
assertThrows(IOException.class, () -> loader.loadVideo(in));
|
||||
verify(runner).run(args, Duration.ofSeconds(5));
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
$TempDir = [System.IO.Path]::GetTempPath()
|
||||
$Output = "$TempDir\test-ffmpeg-output.txt"
|
||||
|
||||
Write-Output "$args" > $Output
|
||||
exit
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -o errexit -o noclobber -o pipefail -o nounset
|
||||
|
||||
output=/tmp/test-ffmpeg-output.txt
|
||||
|
||||
echo "$@" >| $output
|
||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
$TempDir = [System.IO.Path]::GetTempPath()
|
||||
$Output = "$TempDir\test-ffprobe-output.txt"
|
||||
|
||||
Write-Output "$args" > $Output
|
||||
Write-Host "1920,1080,25"
|
||||
exit
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -o errexit -o noclobber -o pipefail -o nounset
|
||||
|
||||
output=/tmp/test-ffprobe-output.txt
|
||||
|
||||
echo "$@" >| $output
|
||||
echo "1920,1080,25"
|
||||
25
pom.xml
25
pom.xml
@@ -192,6 +192,25 @@
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.12</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@@ -221,6 +240,12 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -33,6 +33,10 @@ public record AudioOrVideo(Audio audio, Video video) implements Audio {
|
||||
this(null, video);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> The type of the inner object
|
||||
* @return The inner object (audio or video)
|
||||
*/
|
||||
public <T> T inner() {
|
||||
return (T) (audio == null ? video : audio);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.extractor.whisper;
|
||||
|
||||
import com.github.gtache.autosubtitle.Audio;
|
||||
import com.github.gtache.autosubtitle.AudioInfo;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestAudioOrVideo {
|
||||
|
||||
private final Audio audio;
|
||||
private final Video video;
|
||||
private final InputStream in;
|
||||
|
||||
|
||||
TestAudioOrVideo(@Mock final Audio audio, @Mock final Video video, @Mock final InputStream in) {
|
||||
this.audio = Objects.requireNonNull(audio);
|
||||
this.video = Objects.requireNonNull(video);
|
||||
this.in = Objects.requireNonNull(in);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAudio() throws IOException {
|
||||
final var audioOrVideo = new AudioOrVideo(audio);
|
||||
when(audio.getInputStream()).thenReturn(in);
|
||||
assertEquals(in, audioOrVideo.getInputStream());
|
||||
final var info = mock(AudioInfo.class);
|
||||
when(audio.info()).thenReturn(info);
|
||||
|
||||
assertEquals(info, audioOrVideo.info());
|
||||
assertEquals(audio, audioOrVideo.inner());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVideo() throws IOException {
|
||||
final var audioOrVideo = new AudioOrVideo(video);
|
||||
when(video.getInputStream()).thenReturn(in);
|
||||
assertEquals(in, audioOrVideo.getInputStream());
|
||||
final var info = mock(VideoInfo.class);
|
||||
when(video.info()).thenReturn(info);
|
||||
|
||||
assertEquals(info, audioOrVideo.info());
|
||||
assertEquals(video, audioOrVideo.inner());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(NullPointerException.class, () -> new AudioOrVideo((Audio) null));
|
||||
assertThrows(NullPointerException.class, () -> new AudioOrVideo((Video) null));
|
||||
assertThrows(NullPointerException.class, () -> new AudioOrVideo(null, null));
|
||||
assertThrows(IllegalArgumentException.class, () -> new AudioOrVideo(audio, video));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user