Rework to avoid using preferences object to retrieve options

This commit is contained in:
Guillaume Tâche
2024-09-22 21:59:10 +02:00
parent 7f99c48e2c
commit c59619da2d
115 changed files with 2294 additions and 765 deletions

View File

@@ -2,7 +2,6 @@ package com.github.gtache.autosubtitle.subtitle.extractor.whisper;
import com.github.gtache.autosubtitle.Audio;
import com.github.gtache.autosubtitle.File;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.impl.OS;
import com.github.gtache.autosubtitle.process.ProcessRunner;
@@ -13,7 +12,7 @@ import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractorListener;
import com.github.gtache.autosubtitle.subtitle.extractor.impl.ExtractEventImpl;
@@ -79,41 +78,41 @@ public abstract class AbstractWhisperSubtitleExtractor implements SubtitleExtrac
@Override
public SubtitleCollection<Subtitle> extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException {
return extract(new AudioOrVideo(video), language, model);
public SubtitleCollection<Subtitle> extract(final Video video, final ExtractOptions options) throws ExtractException {
return extract(new AudioOrVideo(video), options);
}
@Override
public SubtitleCollection<Subtitle> extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException {
return extract(new AudioOrVideo(audio), language, model);
public SubtitleCollection<Subtitle> extract(final Audio audio, final ExtractOptions options) throws ExtractException {
return extract(new AudioOrVideo(audio), options);
}
private SubtitleCollection<Subtitle> extract(final AudioOrVideo av, final Language language, final ExtractionModel model) throws ExtractException {
private SubtitleCollection<Subtitle> extract(final AudioOrVideo av, final ExtractOptions options) throws ExtractException {
if (av.inner() instanceof final File f) {
return extract(f.path(), language, model, av.info().duration());
return extract(f.path(), options, av.info().duration());
} else {
try {
return dumpExtract(av, language, model);
return dumpExtract(av, options);
} catch (final IOException e) {
throw new ExtractException(e);
}
}
}
private SubtitleCollection<Subtitle> dumpExtract(final AudioOrVideo av, final Language language, final ExtractionModel model) throws ExtractException, IOException {
private SubtitleCollection<Subtitle> dumpExtract(final AudioOrVideo av, final ExtractOptions options) throws ExtractException, IOException {
final var path = Files.createTempFile(AUTOSUBTITLE, "." + av.info().format());
try (final var in = av.getInputStream()) {
Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
return extract(path, language, model, av.info().duration());
return extract(path, options, av.info().duration());
} finally {
Files.deleteIfExists(path);
}
}
private SubtitleCollection<Subtitle> extract(final Path path, final Language language, final ExtractionModel model, final long duration) throws ExtractException {
private SubtitleCollection<Subtitle> extract(final Path path, final ExtractOptions options, final long duration) throws ExtractException {
try {
final var outputDir = Files.createTempDirectory(AUTOSUBTITLE);
final var args = createArgs(path, language, model, outputDir);
final var args = createArgs(path, options, outputDir);
final var processListener = processRunner.startListen(args);
var oldProgress = -1.0;
var line = processListener.readLine();
@@ -129,7 +128,7 @@ public abstract class AbstractWhisperSubtitleExtractor implements SubtitleExtrac
final var filename = path.getFileName().toString();
final var subtitleFilename = filename.substring(0, filename.lastIndexOf('.')) + ".json";
final var subtitleFile = outputDir.resolve(subtitleFilename);
return parseResult(subtitleFile);
return parseResult(subtitleFile, options);
} else {
throw new ExtractException("Error extracting subtitles: " + result.output());
}
@@ -138,9 +137,9 @@ public abstract class AbstractWhisperSubtitleExtractor implements SubtitleExtrac
}
}
private SubtitleCollection<Subtitle> parseResult(final Path subtitleFile) throws ExtractException {
private SubtitleCollection<Subtitle> parseResult(final Path subtitleFile, final ExtractOptions options) throws ExtractException {
try {
return converter.parse(subtitleFile);
return converter.parse(subtitleFile, options.parseOptions());
} catch (final ParseException e) {
throw new ExtractException(e);
}
@@ -175,12 +174,11 @@ public abstract class AbstractWhisperSubtitleExtractor implements SubtitleExtrac
* Creates the command line arguments for Whisper
*
* @param path the path to the file
* @param language the language
* @param model the model
* @param options the extraction options
* @param outputDir the output directory
* @return the list of arguments
*/
protected abstract List<String> createArgs(final Path path, final Language language, final ExtractionModel model, final Path outputDir);
protected abstract List<String> createArgs(final Path path, final ExtractOptions options, final Path outputDir);
/**
* @return the path to the python executable

View File

@@ -5,7 +5,6 @@ module com.github.gtache.autosubtitle.whisper.common {
requires transitive com.github.gtache.autosubtitle.conda;
requires transitive java.net.http;
requires org.apache.logging.log4j;
requires transitive com.google.gson;
requires transitive java.compiler; //Don't know why dagger generates @Generated here, need to debug
exports com.github.gtache.autosubtitle.whisper;

View File

@@ -2,7 +2,6 @@ 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.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.impl.FileAudioImpl;
@@ -13,11 +12,12 @@ 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.ParseException;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractorListener;
import com.github.gtache.autosubtitle.subtitle.extractor.impl.ExtractEventImpl;
import org.junit.jupiter.api.BeforeEach;
@@ -52,15 +52,16 @@ class TestAbstractWhisperSubtitleExtractor {
private final AudioInfo audioInfo;
private final VideoInfo videoInfo;
private final ExtractionModel extractionModel;
private final ExtractOptions options;
private final ParseOptions parseOptions;
private final SubtitleCollection<Subtitle> collection;
TestAbstractWhisperSubtitleExtractor(@Mock final SubtitleConverterProvider converterProvider, @Mock final SubtitleConverter<Subtitle> converter,
@Mock final ProcessRunner processRunner, @Mock final ProcessListener processListener,
@Mock final ProcessResult processResult, @Mock final VideoInfo videoInfo,
@Mock final AudioInfo audioInfo, @Mock final ExtractionModel extractionModel,
@Mock final SubtitleCollection<Subtitle> collection) {
@Mock final AudioInfo audioInfo, @Mock final ExtractOptions options,
@Mock final ParseOptions parseOptions, @Mock final SubtitleCollection<Subtitle> collection) {
this.venvPath = Path.of("venv");
this.os = OS.LINUX;
this.converterProvider = Objects.requireNonNull(converterProvider);
@@ -70,7 +71,8 @@ class TestAbstractWhisperSubtitleExtractor {
this.processResult = Objects.requireNonNull(processResult);
this.audioInfo = Objects.requireNonNull(audioInfo);
this.videoInfo = Objects.requireNonNull(videoInfo);
this.extractionModel = Objects.requireNonNull(extractionModel);
this.options = Objects.requireNonNull(options);
this.parseOptions = Objects.requireNonNull(parseOptions);
this.collection = Objects.requireNonNull(collection);
}
@@ -81,6 +83,7 @@ class TestAbstractWhisperSubtitleExtractor {
when(processListener.join(Duration.ofHours(1))).thenReturn(processResult);
when(audioInfo.format()).thenReturn("mp3");
when(videoInfo.format()).thenReturn("mp4");
when(options.parseOptions()).thenReturn(parseOptions);
this.extractor = new DummyWhisperSubtitleExtractor(venvPath, converterProvider, processRunner, os);
}
@@ -126,7 +129,7 @@ class TestAbstractWhisperSubtitleExtractor {
final var audio = mock(Audio.class);
when(audio.info()).thenReturn(audioInfo);
when(audio.getInputStream()).thenThrow(IOException.class);
assertThrows(ExtractException.class, () -> extractor.extract(audio, Language.EN, extractionModel));
assertThrows(ExtractException.class, () -> extractor.extract(audio, options));
}
@Test
@@ -134,16 +137,16 @@ class TestAbstractWhisperSubtitleExtractor {
final var path = Paths.get("path");
final var audio = new FileAudioImpl(path, audioInfo);
doThrow(IOException.class).when(processListener).readLine();
assertThrows(ExtractException.class, () -> extractor.extract(audio, Language.EN, extractionModel));
assertThrows(ExtractException.class, () -> extractor.extract(audio, options));
}
@Test
void testExtractAudioFileParseException() throws ParseException {
final var path = Paths.get("path.path");
final var audio = new FileAudioImpl(path, audioInfo);
doThrow(ParseException.class).when(converter).parse(any(Path.class));
assertThrows(ExtractException.class, () -> extractor.extract(audio, Language.EN, extractionModel));
verify(converter).parse(any(Path.class));
doThrow(ParseException.class).when(converter).parse(any(Path.class), eq(parseOptions));
assertThrows(ExtractException.class, () -> extractor.extract(audio, options));
verify(converter).parse(any(Path.class), eq(parseOptions));
}
@Test
@@ -151,7 +154,7 @@ class TestAbstractWhisperSubtitleExtractor {
final var path = Paths.get("path");
final var audio = new FileAudioImpl(path, audioInfo);
when(processResult.exitCode()).thenReturn(1);
assertThrows(ExtractException.class, () -> extractor.extract(audio, Language.EN, extractionModel));
assertThrows(ExtractException.class, () -> extractor.extract(audio, options));
}
@Test
@@ -159,7 +162,7 @@ class TestAbstractWhisperSubtitleExtractor {
final var video = mock(Video.class);
when(video.info()).thenReturn(videoInfo);
when(video.getInputStream()).thenThrow(IOException.class);
assertThrows(ExtractException.class, () -> extractor.extract(video, Language.EN, extractionModel));
assertThrows(ExtractException.class, () -> extractor.extract(video, options));
}
@Test
@@ -168,8 +171,8 @@ class TestAbstractWhisperSubtitleExtractor {
when(video.info()).thenReturn(videoInfo);
final var in = new ByteArrayInputStream("test".getBytes());
when(video.getInputStream()).thenReturn(in);
when(converter.parse(any(Path.class))).thenReturn(collection);
assertEquals(collection, extractor.extract(video, Language.EN, extractionModel));
when(converter.parse(any(Path.class), eq(parseOptions))).thenReturn(collection);
assertEquals(collection, extractor.extract(video, options));
}
@Test
@@ -179,11 +182,11 @@ class TestAbstractWhisperSubtitleExtractor {
when(videoInfo.duration()).thenReturn(100000L);
final var in = new ByteArrayInputStream("test".getBytes());
when(video.getInputStream()).thenReturn(in);
when(converter.parse(any(Path.class))).thenReturn(collection);
when(converter.parse(any(Path.class), eq(parseOptions))).thenReturn(collection);
when(processListener.readLine()).thenReturn("Progress: 1.7abcd", "[00:12.234 --> 00:13.234] Hello", "98%|bbb", "abcd", null);
final var listener = mock(SubtitleExtractorListener.class);
extractor.addListener(listener);
assertEquals(collection, extractor.extract(video, Language.EN, extractionModel));
assertEquals(collection, extractor.extract(video, options));
verify(listener).listen(new ExtractEventImpl("Progress: 1.7abcd", 0.017));
verify(listener).listen(new ExtractEventImpl("[00:12.234 --> 00:13.234] Hello", 0.13234));
verify(listener).listen(new ExtractEventImpl("98%|bbb", 0.98));
@@ -208,8 +211,8 @@ class TestAbstractWhisperSubtitleExtractor {
}
@Override
protected List<String> createArgs(final Path path, final Language language, final ExtractionModel model, final Path outputDir) {
return List.of(path.toString(), language.toString(), model.toString(), outputDir.toString());
protected List<String> createArgs(final Path path, final ExtractOptions options, final Path outputDir) {
return List.of(path.toString(), options.toString(), outputDir.toString());
}
}
}