Adds WhisperX, reworks UI (still needs some work), theoretically usable
This commit is contained in:
219
.idea/csv-editor.xml
generated
219
.idea/csv-editor.xml
generated
@@ -1,219 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CsvFileAttributes">
|
||||
<option name="attributeMap">
|
||||
<map>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-arccos.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-arccosh.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-arcsin.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-arcsinh.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-arctan.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-arctanh.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-cbrt.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-cos.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-cosh.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-exp.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-exp2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-expm1.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-log.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-log10.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-log1p.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-log2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-sin.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-sinh.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-tan.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\core\tests\data\umath-validation-set-tanh.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\mt19937-testset-1.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\mt19937-testset-2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\pcg64-testset-1.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\pcg64-testset-2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\pcg64dxsm-testset-1.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\pcg64dxsm-testset-2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\philox-testset-1.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\philox-testset-2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\sfc64-testset-1.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="\tools\whisper\whisper-env\Lib\site-packages\numpy\random\tests\data\sfc64-testset-2.csv">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/encodings.xml
generated
8
.idea/encodings.xml
generated
@@ -7,6 +7,8 @@
|
||||
<file url="file://$PROJECT_DIR$/cli/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/client/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/client/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/conda/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/conda/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/core/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/core/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/deepl/src/main/java" charset="UTF-8" />
|
||||
@@ -27,8 +29,14 @@
|
||||
<file url="file://$PROJECT_DIR$/server/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/base/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/base/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/common/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/common/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/whisperx/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/whisper/whisperx/src/main/resources" charset="UTF-8" />
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/sonarlint.xml
generated
1
.idea/sonarlint.xml
generated
@@ -4,6 +4,7 @@
|
||||
<option name="moduleMapping">
|
||||
<map>
|
||||
<entry key="autosubtitle-gui-fx" value="autosubtitle-fx" />
|
||||
<entry key="whisper-base" value="whisperbase" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
/**
|
||||
* Translates texts and subtitles
|
||||
*/
|
||||
public interface Translator {
|
||||
public interface Translator<T extends Subtitle> {
|
||||
|
||||
/**
|
||||
* Guesses the language of the given text
|
||||
@@ -42,7 +42,7 @@ public interface Translator {
|
||||
* @param to The target language
|
||||
* @return The translated subtitle
|
||||
*/
|
||||
Subtitle translate(Subtitle subtitle, Language to);
|
||||
T translate(Subtitle subtitle, Language to);
|
||||
|
||||
/**
|
||||
* Translates the given subtitles collection to the given language
|
||||
@@ -51,5 +51,5 @@ public interface Translator {
|
||||
* @param to The target language
|
||||
* @return The translated subtitles collection
|
||||
*/
|
||||
SubtitleCollection translate(SubtitleCollection collection, Language to);
|
||||
SubtitleCollection<T> translate(SubtitleCollection<?> collection, Language to);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public interface VideoConverter {
|
||||
* @return The modified video
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
Video addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles) throws IOException;
|
||||
Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles) throws IOException;
|
||||
|
||||
/**
|
||||
* Adds soft subtitles to the given video
|
||||
@@ -29,7 +29,7 @@ public interface VideoConverter {
|
||||
* @param path The output path
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
void addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles, final Path path) throws IOException;
|
||||
void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final Path path) throws IOException;
|
||||
|
||||
/**
|
||||
* Adds hard subtitles to the given video
|
||||
@@ -39,7 +39,7 @@ public interface VideoConverter {
|
||||
* @return The modified video
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
Video addHardSubtitles(final Video video, final SubtitleCollection subtitles) throws IOException;
|
||||
Video addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles) throws IOException;
|
||||
|
||||
/**
|
||||
* Adds hard subtitles to the given video
|
||||
@@ -49,7 +49,7 @@ public interface VideoConverter {
|
||||
* @param path The output path
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
void addHardSubtitles(final Video video, final SubtitleCollection subtitles, final Path path) throws IOException;
|
||||
void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final Path path) throws IOException;
|
||||
|
||||
/**
|
||||
* Extracts the audio from the given video
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.github.gtache.autosubtitle.archive;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Compresses and uncompresses files
|
||||
*/
|
||||
public interface Archiver {
|
||||
|
||||
/**
|
||||
* Zips multiple files to the given destination
|
||||
*
|
||||
* @param files The files to zip
|
||||
* @param destination The zipped file
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
void compress(final List<Path> files, final Path destination) throws IOException;
|
||||
|
||||
/**
|
||||
* Unzips an archive to the given destination
|
||||
*
|
||||
* @param archive The archive
|
||||
* @param destination The destination folder
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
void decompress(final Path archive, final Path destination) throws IOException;
|
||||
|
||||
/**
|
||||
* Checks whether the given archive file is supported by the compresser
|
||||
*
|
||||
* @param path The file path
|
||||
* @return True if the file is supported
|
||||
*/
|
||||
default boolean isPathSupported(final Path path) {
|
||||
return path.toString().endsWith("." + archiveExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The zipped archive extension
|
||||
*/
|
||||
String archiveExtension();
|
||||
}
|
||||
@@ -22,10 +22,11 @@ public interface ProcessListener {
|
||||
String readLine() throws IOException;
|
||||
|
||||
/**
|
||||
* Waits for the process to finish
|
||||
* Waits for the process to finish. Automatically reads the process output to ensure it doesn't get stuck.
|
||||
*
|
||||
* @param duration The maximum time to wait
|
||||
* @return The process result
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
ProcessResult join(final Duration duration) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.process;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -10,24 +11,25 @@ import java.util.List;
|
||||
public interface ProcessRunner {
|
||||
|
||||
/**
|
||||
* Runs a command
|
||||
* Runs a command and waits max 1 hour for the process to run
|
||||
*
|
||||
* @param args the command
|
||||
* @return the result
|
||||
* @throws IOException if something goes wrong
|
||||
*/
|
||||
default ProcessResult run(final String... args) throws IOException {
|
||||
return run(Arrays.asList(args));
|
||||
return run(Arrays.asList(args), Duration.ofHours(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a command
|
||||
*
|
||||
* @param args the command
|
||||
* @param args the command
|
||||
* @param duration The maximum duration to wait for
|
||||
* @return the result
|
||||
* @throws IOException if something goes wrong
|
||||
*/
|
||||
ProcessResult run(final List<String> args) throws IOException;
|
||||
ProcessResult run(final List<String> args, final Duration duration) throws IOException;
|
||||
|
||||
/**
|
||||
* Starts a process
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Collection;
|
||||
/**
|
||||
* Represents a collection of {@link Subtitle}
|
||||
*/
|
||||
public interface SubtitleCollection {
|
||||
public interface SubtitleCollection<T extends Subtitle> {
|
||||
|
||||
/**
|
||||
* @return The whole text of the subtitles
|
||||
@@ -17,7 +17,7 @@ public interface SubtitleCollection {
|
||||
/**
|
||||
* @return The subtitles
|
||||
*/
|
||||
Collection<? extends Subtitle> subtitles();
|
||||
Collection<T> subtitles();
|
||||
|
||||
/**
|
||||
* @return The language of the subtitles
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.github.gtache.autosubtitle.subtitle;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Imports and exports subtitles
|
||||
*/
|
||||
public interface SubtitleImporterExporter<T extends Subtitle> {
|
||||
|
||||
/**
|
||||
* Imports subtitles from a file
|
||||
*
|
||||
* @param file The path to the file
|
||||
* @return A mapping of langauge to collection
|
||||
* @throws IOException If an error occurred
|
||||
* @throws ParseException If an error occurred while parsing a subtitle
|
||||
*/
|
||||
Map<Language, SubtitleCollection<T>> importSubtitles(final Path file) throws IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Exports multiple collections to a file
|
||||
*
|
||||
* @param collections The subtitle collections
|
||||
* @param file The path to the file
|
||||
* @throws IOException If an error occurred
|
||||
*/
|
||||
void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final Path file) throws IOException;
|
||||
|
||||
/**
|
||||
* Exports a single collection to a file
|
||||
*
|
||||
* @param collection The subtitle collection
|
||||
* @param file The path to the file
|
||||
* @throws IOException If an error occurred
|
||||
*/
|
||||
default void exportSubtitles(final SubtitleCollection<?> collection, final Path file) throws IOException {
|
||||
exportSubtitles(List.of(collection), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The supported file extensions for multiple collection exports
|
||||
*/
|
||||
Collection<String> supportedArchiveExtensions();
|
||||
|
||||
/**
|
||||
* @return The supported file extensions for single collection exports
|
||||
*/
|
||||
Collection<String> supportedSingleFileExtensions();
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter;
|
||||
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -9,7 +10,7 @@ import java.nio.file.Path;
|
||||
/**
|
||||
* Converts subtitles to a specific format (e.g. srt, ssa, ass, ...) and vice-versa
|
||||
*/
|
||||
public interface SubtitleConverter {
|
||||
public interface SubtitleConverter<T extends Subtitle> {
|
||||
|
||||
/**
|
||||
* Converts the subtitle collection
|
||||
@@ -17,7 +18,7 @@ public interface SubtitleConverter {
|
||||
* @param collection The collection
|
||||
* @return The converted subtitles as the content of a file
|
||||
*/
|
||||
String format(final SubtitleCollection collection);
|
||||
String format(final SubtitleCollection<?> collection);
|
||||
|
||||
/**
|
||||
* Parses a subtitle collection
|
||||
@@ -26,7 +27,7 @@ public interface SubtitleConverter {
|
||||
* @return The subtitle collection
|
||||
* @throws ParseException If an error occurred
|
||||
*/
|
||||
default SubtitleCollection parse(final Path file) throws ParseException {
|
||||
default SubtitleCollection<T> parse(final Path file) throws ParseException {
|
||||
try {
|
||||
final var content = Files.readString(file);
|
||||
return parse(content);
|
||||
@@ -42,7 +43,7 @@ public interface SubtitleConverter {
|
||||
* @return The subtitle collection
|
||||
* @throws ParseException If an error occurred
|
||||
*/
|
||||
SubtitleCollection parse(String content) throws ParseException;
|
||||
SubtitleCollection<T> parse(String content) throws ParseException;
|
||||
|
||||
/**
|
||||
* Check if the parser can parse the given file
|
||||
|
||||
@@ -3,12 +3,13 @@ package com.github.gtache.autosubtitle.subtitle.extractor;
|
||||
import com.github.gtache.autosubtitle.Audio;
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
|
||||
/**
|
||||
* Extracts subtitles from a video or audio
|
||||
*/
|
||||
public interface SubtitleExtractor {
|
||||
public interface SubtitleExtractor<T extends Subtitle> {
|
||||
|
||||
/**
|
||||
* Adds a listener
|
||||
@@ -37,7 +38,7 @@ public interface SubtitleExtractor {
|
||||
* @return The extracted subtitle collection
|
||||
* @throws ExtractException If an error occurs
|
||||
*/
|
||||
default SubtitleCollection extract(final Video video, final ExtractionModel model) throws ExtractException {
|
||||
default SubtitleCollection<T> extract(final Video video, final ExtractionModel model) throws ExtractException {
|
||||
return extract(video, Language.AUTO, model);
|
||||
}
|
||||
|
||||
@@ -50,7 +51,7 @@ public interface SubtitleExtractor {
|
||||
* @return The extracted subtitle collection
|
||||
* @throws ExtractException If an error occurs
|
||||
*/
|
||||
SubtitleCollection extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException;
|
||||
SubtitleCollection<T> extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException;
|
||||
|
||||
/**
|
||||
* Extracts the subtitles from an audio
|
||||
@@ -60,7 +61,7 @@ public interface SubtitleExtractor {
|
||||
* @return The extracted subtitle collection
|
||||
* @throws ExtractException If an error occurs
|
||||
*/
|
||||
default SubtitleCollection extract(final Audio audio, final ExtractionModel model) throws ExtractException {
|
||||
default SubtitleCollection<T> extract(final Audio audio, final ExtractionModel model) throws ExtractException {
|
||||
return extract(audio, Language.AUTO, model);
|
||||
}
|
||||
|
||||
@@ -73,5 +74,5 @@ public interface SubtitleExtractor {
|
||||
* @return The extracted subtitle collection
|
||||
* @throws ExtractException If an error occurs
|
||||
*/
|
||||
SubtitleCollection extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException;
|
||||
SubtitleCollection<T> extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
module com.github.gtache.autosubtitle.api {
|
||||
exports com.github.gtache.autosubtitle;
|
||||
exports com.github.gtache.autosubtitle.archive;
|
||||
exports com.github.gtache.autosubtitle.process;
|
||||
exports com.github.gtache.autosubtitle.setup;
|
||||
exports com.github.gtache.autosubtitle.subtitle;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.com.github.gtache.autosubtitle.subtitle.converter;
|
||||
|
||||
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.SubtitleConverter;
|
||||
@@ -23,10 +24,10 @@ import static org.mockito.Mockito.when;
|
||||
class TestSubtitleConverter {
|
||||
|
||||
private final SubtitleConverter subtitleConverter;
|
||||
private final SubtitleCollection subtitleCollection;
|
||||
private final SubtitleCollection<Subtitle> subtitleCollection;
|
||||
|
||||
TestSubtitleConverter(@Mock final SubtitleConverter subtitleConverter,
|
||||
@Mock final SubtitleCollection subtitleCollection) {
|
||||
@Mock final SubtitleCollection<Subtitle> subtitleCollection) {
|
||||
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
||||
this.subtitleCollection = Objects.requireNonNull(subtitleCollection);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -32,7 +33,7 @@ class TestProcessRunner {
|
||||
|
||||
@Test
|
||||
void testRunVarargs() throws IOException {
|
||||
when(processRunner.run(List.of("arg1", "arg2"))).thenReturn(result);
|
||||
when(processRunner.run(List.of("arg1", "arg2"), Duration.ofHours(1))).thenReturn(result);
|
||||
when(processRunner.run(any(String[].class))).thenCallRealMethod();
|
||||
|
||||
assertEquals(result, processRunner.run("arg1", "arg2"));
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||
<artifactId>autosubtitle-whisper</artifactId>
|
||||
<artifactId>autosubtitle-whisperx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
|
||||
@@ -3,14 +3,14 @@ package com.github.gtache.autosubtitle.modules.cli;
|
||||
import com.github.gtache.autosubtitle.modules.deepl.DeepLModule;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegModule;
|
||||
import com.github.gtache.autosubtitle.modules.impl.CoreModule;
|
||||
import com.github.gtache.autosubtitle.modules.subtitle.extractor.whisper.WhisperExtractorModule;
|
||||
import com.github.gtache.autosubtitle.modules.whisperx.WhisperXModule;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import dagger.Component;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
|
||||
@Component(modules = {CoreModule.class, DeepLModule.class, FFmpegModule.class, WhisperExtractorModule.class})
|
||||
@Component(modules = {CoreModule.class, DeepLModule.class, FFmpegModule.class, WhisperXModule.class})
|
||||
@Singleton
|
||||
public interface CliComponent {
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
module com.github.gtache.autosubtitle.cli {
|
||||
requires com.github.gtache.autosubtitle.deepl;
|
||||
requires com.github.gtache.autosubtitle.ffmpeg;
|
||||
requires com.github.gtache.autosubtitle.whisper;
|
||||
requires com.github.gtache.autosubtitle.whisperx;
|
||||
requires info.picocli;
|
||||
}
|
||||
20
conda/pom.xml
Normal file
20
conda/pom.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||
<artifactId>autosubtitle</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>autosubtitle-conda</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||
<artifactId>autosubtitle-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.setup.whisper.CondaSetupConfiguration;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.CacheRoot;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.ToolsRoot;
|
||||
import com.github.gtache.autosubtitle.setup.conda.CondaSetupConfiguration;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@@ -56,13 +58,13 @@ public final class CondaSetupModule {
|
||||
|
||||
@Provides
|
||||
@CondaRootPath
|
||||
static Path providesCondaRootPath(@WhisperBundledRoot final Path root, final OS os) {
|
||||
static Path providesCondaRootPath(@ToolsRoot final Path root) {
|
||||
return root.resolve(MINICONDA3);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@CondaInstallerPath
|
||||
static Path providesCondaInstallerPath(@WhisperBundledRoot final Path root, final OS os) {
|
||||
return root.resolve("cache").resolve("conda-install" + (os == OS.WINDOWS ? ".exe" : ".sh"));
|
||||
static Path providesCondaInstallerPath(@CacheRoot final Path root, final OS os) {
|
||||
return root.resolve("conda-install" + (os == OS.WINDOWS ? ".exe" : ".sh"));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.setup.conda;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.setup.conda;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
@@ -13,7 +13,9 @@ import java.io.IOException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@@ -67,8 +69,8 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
logger.info("Conda downloaded");
|
||||
}
|
||||
switch (configuration.os()) {
|
||||
case WINDOWS -> installWindows();
|
||||
case MAC, LINUX -> installLinux();
|
||||
case OS.WINDOWS -> installWindows();
|
||||
case OS.MAC, OS.LINUX -> installLinux();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +79,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
final var installerPath = configuration.condaInstallerPath();
|
||||
final var rootPath = configuration.condaRootPath();
|
||||
logger.info("Installing conda using {}", installerPath);
|
||||
final var result = run("bash", installerPath.toString(), "-b", "-p", rootPath.toString());
|
||||
final var result = run(List.of("bash", installerPath.toString(), "-b", "-p", rootPath.toString()), Duration.ofMinutes(15));
|
||||
if (result.exitCode() == 0) {
|
||||
logger.info("Installed conda to {}", rootPath);
|
||||
} else {
|
||||
@@ -93,7 +95,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
final var installerPath = configuration.condaInstallerPath();
|
||||
final var rootPath = configuration.condaRootPath();
|
||||
logger.info("Installing conda using {}", installerPath);
|
||||
final var result = run(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + rootPath.toString());
|
||||
final var result = run(List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + rootPath.toString()), Duration.ofMinutes(15));
|
||||
if (result.exitCode() == 0) {
|
||||
logger.info("Installed conda to {}", rootPath);
|
||||
} else {
|
||||
@@ -106,9 +108,9 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
|
||||
private void downloadConda() throws SetupException {
|
||||
switch (configuration.os()) {
|
||||
case WINDOWS -> downloadCondaWindows();
|
||||
case MAC -> downloadCondaMac();
|
||||
case LINUX -> downloadCondaLinux();
|
||||
case OS.WINDOWS -> downloadCondaWindows();
|
||||
case OS.MAC -> downloadCondaMac();
|
||||
case OS.LINUX -> downloadCondaLinux();
|
||||
}
|
||||
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());
|
||||
}
|
||||
@@ -152,7 +154,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
@Override
|
||||
public void update() throws SetupException {
|
||||
try {
|
||||
final var result = run(getCondaPath().toString(), "update", "-y", "conda");
|
||||
final var result = run(List.of(getCondaPath().toString(), "update", "-y", "conda"), Duration.ofMinutes(15));
|
||||
if (result.exitCode() == 0) {
|
||||
logger.info("Conda updated");
|
||||
} else {
|
||||
@@ -167,7 +169,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
final var args = Stream.concat(Stream.of(getCondaPath().toString(), "create", "-y", "-p", path.toString(), "python=" + pythonVersion), Arrays.stream(packages)).toList();
|
||||
try {
|
||||
logger.info("Creating venv {}", path);
|
||||
final var result = run(args);
|
||||
final var result = run(args, Duration.ofMinutes(15));
|
||||
if (result.exitCode() == 0) {
|
||||
logger.info("Created venv {}", path);
|
||||
} else {
|
||||
@@ -200,7 +202,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
|
||||
private boolean isSystemCondaInstalled() throws SetupException {
|
||||
try {
|
||||
final var result = run(configuration.condaSystemPath().toString(), "--version");
|
||||
final var result = run(List.of(configuration.condaSystemPath().toString(), "--version"), Duration.ofSeconds(5));
|
||||
if (result.exitCode() == 0) {
|
||||
final var output = result.output().getFirst();
|
||||
final var versionString = output.substring(output.indexOf(' ') + 1);
|
||||
10
conda/src/main/java/module-info.java
Normal file
10
conda/src/main/java/module-info.java
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Module for conda
|
||||
*/
|
||||
module com.github.gtache.autosubtitle.conda {
|
||||
requires transitive com.github.gtache.autosubtitle.core;
|
||||
requires org.apache.logging.log4j;
|
||||
|
||||
exports com.github.gtache.autosubtitle.setup.conda;
|
||||
exports com.github.gtache.autosubtitle.modules.setup.conda;
|
||||
}
|
||||
@@ -24,5 +24,10 @@
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,16 +1,31 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.archive.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* Zip implementation of {@link Decompresser}
|
||||
* Zip implementation of {@link Archiver}
|
||||
*/
|
||||
public class ZipDecompresser implements Decompresser {
|
||||
public class ZipDecompresser implements Archiver {
|
||||
|
||||
@Inject
|
||||
ZipDecompresser() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compress(final List<Path> files, final Path destination) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
@@ -49,9 +64,9 @@ public class ZipDecompresser implements Decompresser {
|
||||
}
|
||||
return destPath;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPathSupported(final Path path) {
|
||||
return path.getFileName().toString().endsWith(".zip");
|
||||
public String archiveExtension() {
|
||||
return "zip";
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,33 @@
|
||||
package com.github.gtache.autosubtitle.modules.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.impl.ZipDecompresser;
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.DaggerException;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.SetupModule;
|
||||
import com.github.gtache.autosubtitle.modules.subtitle.impl.SubtitleModule;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
/**
|
||||
* Dagger module for Core
|
||||
*/
|
||||
@Module(includes = {SetupModule.class, SubtitleModule.class})
|
||||
public final class CoreModule {
|
||||
public abstract class CoreModule {
|
||||
|
||||
private CoreModule() {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
@StringKey("zip")
|
||||
@IntoMap
|
||||
abstract Archiver bindsZipDecompresser(final ZipDecompresser decompresser);
|
||||
|
||||
@Provides
|
||||
static OS providesOS() {
|
||||
final var os = OS.getOS();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.whisper;
|
||||
package com.github.gtache.autosubtitle.modules.setup.impl;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -12,5 +12,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface WhisperVersion {
|
||||
public @interface CacheRoot {
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Dagger core module for setup
|
||||
@@ -14,6 +16,18 @@ public final class SetupModule {
|
||||
private SetupModule() {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@CacheRoot
|
||||
static Path providesCacheRoot() {
|
||||
return Paths.get("cache");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ToolsRoot
|
||||
static Path providesToolsRoot() {
|
||||
return Paths.get("tools");
|
||||
}
|
||||
|
||||
@Provides
|
||||
static HttpClient providesHttpClient() {
|
||||
return HttpClient.newHttpClient();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.impl;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface ToolsRoot {
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.github.gtache.autosubtitle.modules.subtitle.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleImporterExporter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.impl.SRTSubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImporterExporterImpl;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.multibindings.IntoMap;
|
||||
@@ -20,4 +22,7 @@ public abstract class SubtitleModule {
|
||||
@IntoMap
|
||||
@StringKey("srt")
|
||||
abstract SubtitleConverter bindsSubtitleConverter(final SRTSubtitleConverter converter);
|
||||
|
||||
@Binds
|
||||
abstract SubtitleImporterExporter bindsSubtitleImporterExporter(final SubtitleImporterExporterImpl impl);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.apache.logging.log4j.Logger;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link ProcessRunner}
|
||||
@@ -19,19 +18,9 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
||||
private static final Logger logger = LogManager.getLogger(AbstractProcessRunner.class);
|
||||
|
||||
@Override
|
||||
public ProcessResult run(final List<String> args) throws IOException {
|
||||
public ProcessResult run(final List<String> args, final Duration duration) throws IOException {
|
||||
final var listener = startListen(args);
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
var line = listener.readLine();
|
||||
while (line != null) {
|
||||
line = listener.readLine();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
logger.error("Error listening to process output of {}", args, e);
|
||||
}
|
||||
});
|
||||
return listener.join(Duration.ofHours(1));
|
||||
return listener.join(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,11 +40,12 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
||||
/**
|
||||
* Runs a process and writes the output to the log
|
||||
*
|
||||
* @param args the command
|
||||
* @param args the command
|
||||
* @param duration The maximum duration to wait for
|
||||
* @return the result
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
protected ProcessResult runListen(final List<String> args) throws IOException {
|
||||
protected ProcessResult runListen(final List<String> args, final Duration duration) throws IOException {
|
||||
final var listener = startListen(args);
|
||||
var line = listener.readLine();
|
||||
final var processName = args.getFirst();
|
||||
@@ -63,6 +53,6 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
||||
logger.info("[{}]: {}", processName, line);
|
||||
line = listener.readLine();
|
||||
}
|
||||
return listener.join(Duration.ofHours(1));
|
||||
return listener.join(duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,25 @@ package com.github.gtache.autosubtitle.process.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.process.ProcessListener;
|
||||
import com.github.gtache.autosubtitle.process.ProcessResult;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ProcessListener}
|
||||
*/
|
||||
public class ProcessListenerImpl implements ProcessListener {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(ProcessListenerImpl.class);
|
||||
private final Process process;
|
||||
private final BufferedReader reader;
|
||||
private final List<String> output;
|
||||
@@ -30,7 +33,7 @@ public class ProcessListenerImpl implements ProcessListener {
|
||||
public ProcessListenerImpl(final Process process) {
|
||||
this.process = Objects.requireNonNull(process);
|
||||
this.reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
|
||||
this.output = new ArrayList<>();
|
||||
this.output = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,7 +51,24 @@ public class ProcessListenerImpl implements ProcessListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessResult join(final Duration duration) throws IOException {
|
||||
public ProcessResult join(final Duration duration) {
|
||||
//Read to ensure process doesn't get stuck
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
var line = readLine();
|
||||
while (line != null) {
|
||||
line = readLine();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
logger.error("Error listening to process output of {}", process, e);
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (final IOException e) {
|
||||
logger.warn("Error closing reader of {}", process, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
process.waitFor(duration.getSeconds(), TimeUnit.SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
@@ -58,11 +78,6 @@ public class ProcessListenerImpl implements ProcessListener {
|
||||
if (process.isAlive()) {
|
||||
process.destroyForcibly();
|
||||
}
|
||||
//Reads lines to output
|
||||
while (readLine() != null) {
|
||||
//Do nothing
|
||||
}
|
||||
reader.close();
|
||||
return new ProcessResultImpl(process.exitValue(), output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -20,17 +19,17 @@ import static java.util.Objects.requireNonNull;
|
||||
/**
|
||||
* Converts subtitles to SRT format
|
||||
*/
|
||||
@Singleton
|
||||
public class SRTSubtitleConverter implements SubtitleConverter {
|
||||
public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
|
||||
private final Translator translator;
|
||||
private final Translator<?> translator;
|
||||
|
||||
@Inject
|
||||
SRTSubtitleConverter(final Translator translator) {
|
||||
this.translator = requireNonNull(translator);
|
||||
}
|
||||
|
||||
public String format(final SubtitleCollection collection) {
|
||||
@Override
|
||||
public String format(final SubtitleCollection<?> collection) {
|
||||
final var subtitles = collection.subtitles().stream().sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
||||
return IntStream.range(0, subtitles.size()).mapToObj(i -> {
|
||||
final var subtitle = subtitles.get(i);
|
||||
@@ -51,7 +50,7 @@ public class SRTSubtitleConverter implements SubtitleConverter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleCollection parse(final String content) throws ParseException {
|
||||
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
|
||||
try {
|
||||
final var elements = content.split("\n\n");
|
||||
final var subtitles = Arrays.stream(elements).filter(element -> !element.isBlank()).map(element -> {
|
||||
@@ -66,7 +65,7 @@ public class SRTSubtitleConverter implements SubtitleConverter {
|
||||
return new SubtitleImpl(text, start, end, null, null);
|
||||
}).toList();
|
||||
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining(" "));
|
||||
return new SubtitleCollectionImpl(text, subtitles, translator.getLanguage(text));
|
||||
return new SubtitleCollectionImpl<>(text, subtitles, translator.getLanguage(text));
|
||||
} catch (final Exception e) {
|
||||
throw new ParseException(e);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import static java.util.Objects.requireNonNull;
|
||||
/**
|
||||
* Implementation of {@link SubtitleCollection}
|
||||
*/
|
||||
public record SubtitleCollectionImpl(String text, Collection<? extends Subtitle> subtitles,
|
||||
Language language) implements SubtitleCollection {
|
||||
public record SubtitleCollectionImpl<T extends Subtitle>(String text, Collection<T> subtitles,
|
||||
Language language) implements SubtitleCollection<T> {
|
||||
|
||||
public SubtitleCollectionImpl {
|
||||
Objects.requireNonNull(text);
|
||||
|
||||
@@ -22,4 +22,8 @@ public record SubtitleImpl(String content, long start, long end, Font font, Boun
|
||||
throw new IllegalArgumentException("start must be <= end : " + start + " > " + end);
|
||||
}
|
||||
}
|
||||
|
||||
public SubtitleImpl(final Subtitle subtitle) {
|
||||
this(subtitle.content(), subtitle.start(), subtitle.end(), subtitle.font(), subtitle.bounds());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleImporterExporter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementation of {@link SubtitleImporterExporter}
|
||||
*/
|
||||
public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<SubtitleImpl> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(SubtitleImporterExporterImpl.class);
|
||||
private final Map<String, Archiver> archiverMap;
|
||||
private final Map<String, SubtitleConverter<?>> converterMap;
|
||||
|
||||
@Inject
|
||||
SubtitleImporterExporterImpl(final Map<String, Archiver> archiverMap, final Map<String, SubtitleConverter> converterMap) {
|
||||
this.archiverMap = Map.copyOf(archiverMap);
|
||||
this.converterMap = converterMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Language, SubtitleCollection<SubtitleImpl>> importSubtitles(final Path file) throws IOException, ParseException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
if (archiverMap.containsKey(extension)) {
|
||||
return loadArchive(file);
|
||||
} else {
|
||||
final var loaded = loadSingleFile(file);
|
||||
logger.info("Loaded {}", file);
|
||||
return Map.of(loaded.language(), loaded);
|
||||
}
|
||||
}
|
||||
|
||||
private SubtitleCollection<SubtitleImpl> loadSingleFile(final Path file) throws ParseException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var parser = converterMap.get(extension);
|
||||
if (parser == null) {
|
||||
throw new ParseException("No converter found for " + file);
|
||||
} else {
|
||||
final var parsed = parser.parse(file);
|
||||
return new SubtitleCollectionImpl<>(parsed.text(), parsed.subtitles().stream().map(SubtitleImpl::new).toList(), parsed.language());
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Language, SubtitleCollection<SubtitleImpl>> loadArchive(final Path file) throws IOException, ParseException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var archiver = archiverMap.get(extension);
|
||||
final var tempDirectory = Files.createTempDirectory("autosubtitle");
|
||||
archiver.decompress(file, tempDirectory);
|
||||
final var files = new ArrayList<Path>();
|
||||
try (final var stream = Files.list(tempDirectory)) {
|
||||
stream.forEach(files::add);
|
||||
}
|
||||
final var map = new EnumMap<Language, SubtitleCollection<SubtitleImpl>>(Language.class);
|
||||
for (final var path : files) {
|
||||
final var loaded = loadSingleFile(path);
|
||||
map.put(loaded.language(), loaded);
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
Files.deleteIfExists(tempDirectory);
|
||||
logger.info("Loaded {}", file);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final Path file) throws IOException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
if (archiverMap.containsKey(extension)) {
|
||||
saveArchive(file, collections);
|
||||
} else if (collections.size() == 1) {
|
||||
saveSingleFile(file, collections.iterator().next());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot export multiple collections to a non-archive file : " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveArchive(final Path file, final Iterable<? extends SubtitleCollection<?>> collections) throws IOException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var archiver = archiverMap.get(extension);
|
||||
final var singleExporter = converterMap.getOrDefault("json", converterMap.values().iterator().next());
|
||||
final var tempDir = Files.createTempDirectory("autosubtitle");
|
||||
for (final var collection : collections) {
|
||||
final var subtitleFile = tempDir.resolve(collection.language() + "." + singleExporter.formatName());
|
||||
saveSingleFile(subtitleFile, collection);
|
||||
}
|
||||
final var files = new ArrayList<Path>();
|
||||
try (final var stream = Files.list(tempDir)) {
|
||||
stream.forEach(files::add);
|
||||
}
|
||||
archiver.compress(files, file);
|
||||
for (final var path : files) {
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
Files.deleteIfExists(tempDir);
|
||||
logger.info("Saved {}", file);
|
||||
}
|
||||
|
||||
private void saveSingleFile(final Path file, final SubtitleCollection<?> collection) throws IOException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var converter = converterMap.get(extension);
|
||||
if (converter == null) {
|
||||
throw new IOException("No converter found for " + file);
|
||||
} else {
|
||||
final var string = converter.format(collection);
|
||||
Files.writeString(file, string);
|
||||
logger.info("Saved {}", file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<String> supportedArchiveExtensions() {
|
||||
return archiverMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> supportedSingleFileExtensions() {
|
||||
return converterMap.keySet();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ module com.github.gtache.autosubtitle.core {
|
||||
requires org.apache.logging.log4j;
|
||||
|
||||
exports com.github.gtache.autosubtitle.impl;
|
||||
exports com.github.gtache.autosubtitle.archive.impl;
|
||||
exports com.github.gtache.autosubtitle.process.impl;
|
||||
exports com.github.gtache.autosubtitle.setup.impl;
|
||||
exports com.github.gtache.autosubtitle.subtitle.impl;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.github.gtache.autosubtitle.archive.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TestZipDecompresser {
|
||||
|
||||
private final ZipDecompresser zipDecompresser;
|
||||
|
||||
TestZipDecompresser() {
|
||||
this.zipDecompresser = new ZipDecompresser();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsPathSupported() {
|
||||
assertTrue(zipDecompresser.isPathSupported(Path.of("test.zip")));
|
||||
assertFalse(zipDecompresser.isPathSupported(Path.of("test")));
|
||||
assertFalse(zipDecompresser.isPathSupported(Path.of("test.txt")));
|
||||
assertFalse(zipDecompresser.isPathSupported(Path.of("test.zip2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecompress(@TempDir final Path tempDir) throws IOException {
|
||||
final var file = tempDir.resolve("test.zip");
|
||||
try (final var in = getClass().getResourceAsStream("in.zip")) {
|
||||
Files.copy(in, file);
|
||||
}
|
||||
zipDecompresser.decompress(file, tempDir);
|
||||
final var inTxt = tempDir.resolve("in.txt");
|
||||
final var bin = tempDir.resolve("bin");
|
||||
final var binTxt = bin.resolve("bin.txt");
|
||||
final var lib = tempDir.resolve("lib");
|
||||
final var libTxt = lib.resolve("lib.txt");
|
||||
|
||||
assertTrue(Files.exists(inTxt));
|
||||
assertEquals("in", Files.readString(inTxt));
|
||||
assertTrue(Files.exists(bin));
|
||||
assertTrue(Files.exists(binTxt));
|
||||
assertEquals("bin", Files.readString(binTxt));
|
||||
assertTrue(Files.exists(lib));
|
||||
assertTrue(Files.exists(libTxt));
|
||||
assertEquals("lib", Files.readString(libTxt));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(IllegalArgumentException.class, () -> zipDecompresser.decompress(Paths.get("file.txt"), Paths.get("target")));
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class TestAbstractProcessRunner {
|
||||
@Test
|
||||
void testRun() throws IOException {
|
||||
final var expected = new ProcessResultImpl(0, List.of("1", "2", "3"));
|
||||
final var actual = dummyProcessRunner.run(ARGS);
|
||||
final var actual = dummyProcessRunner.run(ARGS, Duration.ofSeconds(5));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class TestAbstractProcessRunner {
|
||||
|
||||
@Test
|
||||
void testRunListen() throws IOException {
|
||||
final var result = dummyProcessRunner.runListen(ARGS);
|
||||
final var result = dummyProcessRunner.runListen(ARGS, Duration.ofSeconds(5));
|
||||
assertEquals(0, result.exitCode());
|
||||
assertEquals(List.of("1", "2", "3"), result.output());
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class TestSRTSubtitleConverter {
|
||||
final var end2 = 12L * 60 * 60 * 1000 + 23 * 60 * 1000 + 34 * 1000 + 457;
|
||||
final var subtitle1 = new SubtitleImpl("test5 test6\ntest7 test8", start1, end1, null, null);
|
||||
final var subtitle2 = new SubtitleImpl("test1 test2\ntest3 test4", start2, end2, null, null);
|
||||
final var subtitles = new SubtitleCollectionImpl(subtitle1.content() + " " + subtitle2.content(), Arrays.asList(subtitle1, subtitle2), language);
|
||||
final var subtitles = new SubtitleCollectionImpl<>(subtitle1.content() + " " + subtitle2.content(), Arrays.asList(subtitle1, subtitle2), language);
|
||||
final var converter = new SRTSubtitleConverter(translator);
|
||||
assertEquals(subtitles, converter.parse(in));
|
||||
assertEquals(in, converter.format(subtitles));
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.subtitle.extractor.impl;
|
||||
import com.github.gtache.autosubtitle.Audio;
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
|
||||
@@ -56,12 +57,12 @@ class TestAbstractSubtitleExtractor {
|
||||
private static final class DummySubtitleExtractor extends AbstractSubtitleExtractor {
|
||||
|
||||
@Override
|
||||
public SubtitleCollection extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException {
|
||||
public SubtitleCollection<Subtitle> extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleCollection extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException {
|
||||
public SubtitleCollection<Subtitle> extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
in
|
||||
Binary file not shown.
13
core/src/test/resources/log4j2-test.xml
Normal file
13
core/src/test/resources/log4j2-test.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="INFO">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -10,7 +10,7 @@ import javax.inject.Inject;
|
||||
/**
|
||||
* DeepL implementation of {@link Translator}
|
||||
*/
|
||||
public class DeepLTranslator implements Translator {
|
||||
public class DeepLTranslator implements Translator<Subtitle> {
|
||||
|
||||
@Inject
|
||||
DeepLTranslator() {
|
||||
@@ -32,7 +32,7 @@ public class DeepLTranslator implements Translator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleCollection translate(final SubtitleCollection collection, final Language to) {
|
||||
public SubtitleCollection<Subtitle> translate(final SubtitleCollection<?> collection, final Language to) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
<groupId>org.tukaani</groupId>
|
||||
<artifactId>xz</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,17 +1,31 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.archive.ffmpeg;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tar implementation of {@link Decompresser}
|
||||
* Tar implementation of {@link Archiver}
|
||||
*/
|
||||
public class TarDecompresser implements Decompresser {
|
||||
public class TarArchiver implements Archiver {
|
||||
|
||||
@Inject
|
||||
TarArchiver() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compress(final List<Path> files, final Path destination) throws IOException {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
@@ -38,7 +52,7 @@ public class TarDecompresser implements Decompresser {
|
||||
}
|
||||
}
|
||||
|
||||
private static Path newFile(final Path destinationDir, final TarArchiveEntry entry) throws IOException {
|
||||
private static Path newFile(final Path destinationDir, final ArchiveEntry entry) throws IOException {
|
||||
final var destPath = destinationDir.resolve(entry.getName());
|
||||
|
||||
final var destDirPath = destinationDir.toAbsolutePath().toString();
|
||||
@@ -51,7 +65,7 @@ public class TarDecompresser implements Decompresser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathSupported(final Path path) {
|
||||
return path.getFileName().toString().endsWith(".tar");
|
||||
public String archiveExtension() {
|
||||
return "tar";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.github.gtache.autosubtitle.archive.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import org.tukaani.xz.XZInputStream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* XZ implementation of {@link Archiver}
|
||||
*/
|
||||
public class XZArchiver implements Archiver {
|
||||
|
||||
@Inject
|
||||
XZArchiver() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compress(final List<Path> files, final Path destination) throws IOException {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
throw new IllegalArgumentException("Unsupported path : " + archive);
|
||||
}
|
||||
final var archiveName = archive.getFileName().toString();
|
||||
Files.createDirectories(destination);
|
||||
final var destinationFile = destination.resolve(archiveName.substring(0, archiveName.lastIndexOf(".xz")));
|
||||
try (final var xzIn = new XZInputStream(Files.newInputStream(archive));
|
||||
final var out = Files.newOutputStream(destinationFile)) {
|
||||
xzIn.transferTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String archiveExtension() {
|
||||
return "xz";
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,17 @@ 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.ffmpeg.FFmpegBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -32,30 +32,29 @@ import static java.util.Objects.requireNonNull;
|
||||
/**
|
||||
* FFmpeg implementation of {@link VideoConverter}
|
||||
*/
|
||||
@Singleton
|
||||
public class FFmpegVideoConverter extends AbstractProcessRunner implements VideoConverter {
|
||||
|
||||
private static final String TEMP_FILE_PREFIX = "autosubtitle";
|
||||
private final Path bundledPath;
|
||||
private final Path systemPath;
|
||||
private final SubtitleConverter subtitleConverter;
|
||||
private final SubtitleConverter<?> subtitleConverter;
|
||||
|
||||
@Inject
|
||||
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath, final Map<String, SubtitleConverter> subtitleConverters) {
|
||||
this.bundledPath = requireNonNull(bundledPath);
|
||||
this.systemPath = requireNonNull(systemPath);
|
||||
this.subtitleConverter = requireNonNull(subtitleConverters.get("srt"));
|
||||
this.subtitleConverter = subtitleConverters.get("srt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Video addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles) throws IOException {
|
||||
final var out = getTempFile("mkv"); //Soft ass subtitles are only supported by mkv apparently
|
||||
public Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles) throws IOException {
|
||||
final var out = getTempFile(video.info().videoFormat());
|
||||
addSoftSubtitles(video, subtitles, out);
|
||||
return new FileVideoImpl(out, new VideoInfoImpl("mkv", video.info().width(), video.info().height(), video.info().duration()));
|
||||
return new FileVideoImpl(out, new VideoInfoImpl(video.info().videoFormat(), video.info().width(), video.info().height(), video.info().duration()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles, final Path path) throws IOException {
|
||||
public void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final Path path) throws IOException {
|
||||
final var videoPath = getPath(video);
|
||||
final var collectionMap = dumpCollections(subtitles);
|
||||
final var args = new ArrayList<String>();
|
||||
@@ -96,18 +95,18 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
args.add("language=" + c.language().iso3());
|
||||
});
|
||||
args.add(path.toString());
|
||||
runListen(args);
|
||||
runListen(args, Duration.ofHours(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Video addHardSubtitles(final Video video, final SubtitleCollection subtitles) throws IOException {
|
||||
public Video addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles) throws IOException {
|
||||
final var out = getTempFile(video.info().videoFormat());
|
||||
addHardSubtitles(video, subtitles, out);
|
||||
return new FileVideoImpl(out, video.info());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHardSubtitles(final Video video, final SubtitleCollection subtitles, final Path path) throws IOException {
|
||||
public void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final Path path) throws IOException {
|
||||
final var videoPath = getPath(video);
|
||||
final var subtitlesPath = dumpSubtitles(subtitles);
|
||||
final var escapedPath = escapeVF(subtitlesPath.toString());
|
||||
@@ -120,7 +119,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
subtitleArg,
|
||||
path.toString()
|
||||
);
|
||||
runListen(args);
|
||||
runListen(args, Duration.ofHours(1));
|
||||
}
|
||||
|
||||
private static String escapeVF(final String path) {
|
||||
@@ -145,7 +144,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
"0:v",
|
||||
dumpVideoPath.toString()
|
||||
);
|
||||
runListen(args);
|
||||
runListen(args, Duration.ofHours(1));
|
||||
Files.deleteIfExists(dumpVideoPath);
|
||||
return new FileAudioImpl(audioPath, new AudioInfoImpl("wav", video.info().duration()));
|
||||
}
|
||||
@@ -166,15 +165,15 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
return path;
|
||||
}
|
||||
|
||||
private SequencedMap<SubtitleCollection, Path> dumpCollections(final Collection<SubtitleCollection> collections) throws IOException {
|
||||
final var ret = LinkedHashMap.<SubtitleCollection, Path>newLinkedHashMap(collections.size());
|
||||
private <T extends SubtitleCollection<?>> SequencedMap<T, Path> dumpCollections(final Collection<T> collections) throws IOException {
|
||||
final var ret = LinkedHashMap.<T, Path>newLinkedHashMap(collections.size());
|
||||
for (final var subtitles : collections) {
|
||||
ret.put(subtitles, dumpSubtitles(subtitles));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Path dumpSubtitles(final SubtitleCollection subtitles) throws IOException {
|
||||
private Path dumpSubtitles(final SubtitleCollection<?> subtitles) throws IOException {
|
||||
final var path = getTempFile("srt");
|
||||
Files.writeString(path, subtitleConverter.format(subtitles));
|
||||
return path;
|
||||
|
||||
@@ -4,22 +4,22 @@ import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.VideoLoader;
|
||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeSystemPath;
|
||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* FFprobe implementation of {@link VideoLoader}
|
||||
*/
|
||||
@Singleton
|
||||
public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLoader {
|
||||
|
||||
private final Path bundledPath;
|
||||
@@ -33,7 +33,7 @@ public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLo
|
||||
|
||||
@Override
|
||||
public Video loadVideo(final Path path) throws IOException {
|
||||
final var result = run(getFFprobePath(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", path.toString());
|
||||
final var result = 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]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,24 +1,17 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.ffmpeg.TarArchiver;
|
||||
import com.github.gtache.autosubtitle.archive.ffmpeg.XZArchiver;
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFBundledRoot;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFProbeInstallerPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegInstallerPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegVersion;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.impl.ExecutableExtension;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.CacheRoot;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.ToolsRoot;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.VideoConverterSetup;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.Decompresser;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.FFmpegSetupConfiguration;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.FFmpegSetupManager;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.TarDecompresser;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.XZDecompresser;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.ZipDecompresser;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -41,20 +34,16 @@ public abstract class FFmpegSetupModule {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
@StringKey("zip")
|
||||
@IntoMap
|
||||
abstract Decompresser bindsZipDecompresser(final ZipDecompresser decompresser);
|
||||
|
||||
@Binds
|
||||
@StringKey("tar")
|
||||
@IntoMap
|
||||
abstract Decompresser bindsTarDecompresser(final TarDecompresser decompresser);
|
||||
abstract Archiver bindsTarDecompresser(final TarArchiver decompresser);
|
||||
|
||||
@Binds
|
||||
@StringKey("xz")
|
||||
@IntoMap
|
||||
abstract Decompresser bindsXzDecompresser(final XZDecompresser decompresser);
|
||||
abstract Archiver bindsXZDecompresser(final XZArchiver decompresser);
|
||||
|
||||
@Binds
|
||||
@VideoConverterSetup
|
||||
@@ -69,19 +58,19 @@ public abstract class FFmpegSetupModule {
|
||||
|
||||
@Provides
|
||||
@FFmpegInstallerPath
|
||||
static Path providesFFmpegInstallerPath(@FFBundledRoot final Path root, final OS os) {
|
||||
return root.resolve("cache").resolve("ffmpeg-installer" + getInstallerExtension(os));
|
||||
static Path providesFFmpegInstallerPath(@CacheRoot final Path root, final OS os) {
|
||||
return root.resolve(FFMPEG + "-installer" + getInstallerExtension(os));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FFProbeInstallerPath
|
||||
static Path providesFFProbeInstallerPath(@FFBundledRoot final Path root, final OS os) {
|
||||
return root.resolve("cache").resolve("ffprobe-installer" + getInstallerExtension(os));
|
||||
static Path providesFFProbeInstallerPath(@CacheRoot final Path root, final OS os) {
|
||||
return root.resolve(FFPROBE + "-installer" + getInstallerExtension(os));
|
||||
}
|
||||
|
||||
private static String getInstallerExtension(final OS os) {
|
||||
if (os == OS.LINUX) {
|
||||
return ".tar.gz";
|
||||
return ".tar.xz";
|
||||
} else {
|
||||
return ".zip";
|
||||
}
|
||||
@@ -89,8 +78,8 @@ public abstract class FFmpegSetupModule {
|
||||
|
||||
@Provides
|
||||
@FFBundledRoot
|
||||
static Path providesFFBundledRoot() {
|
||||
return Paths.get("tools", FFMPEG);
|
||||
static Path providesFFBundledRoot(@ToolsRoot final Path root) {
|
||||
return root.resolve(FFMPEG);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Unzips files
|
||||
*/
|
||||
public interface Decompresser {
|
||||
|
||||
/**
|
||||
* Unzips an archive to the given destination
|
||||
*
|
||||
* @param archive The archive
|
||||
* @param destination The destination folder
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
void decompress(final Path archive, final Path destination) throws IOException;
|
||||
|
||||
/**
|
||||
* Checks whether the given file is supported by the decompresser
|
||||
*
|
||||
* @param path The file path
|
||||
* @return True if the file is supported
|
||||
*/
|
||||
boolean isPathSupported(final Path path);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
@@ -13,6 +14,8 @@ import java.io.IOException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.github.gtache.autosubtitle.impl.Architecture.ARMEL;
|
||||
@@ -26,14 +29,14 @@ import static java.util.Objects.requireNonNull;
|
||||
public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
private static final Logger logger = LogManager.getLogger(FFmpegSetupManager.class);
|
||||
private final FFmpegSetupConfiguration configuration;
|
||||
private final Map<String, Decompresser> decompressers;
|
||||
private final Map<String, Archiver> archivers;
|
||||
|
||||
@Inject
|
||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final Map<String, Decompresser> decompressers,
|
||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final Map<String, Archiver> archivers,
|
||||
final HttpClient httpClient) {
|
||||
super(httpClient);
|
||||
this.configuration = requireNonNull(configuration);
|
||||
this.decompressers = Map.copyOf(decompressers);
|
||||
this.archivers = Map.copyOf(archivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,7 +135,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
try {
|
||||
final var filename = from.getFileName().toString();
|
||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
decompressers.get(extension).decompress(from, to);
|
||||
archivers.get(extension).decompress(from, to);
|
||||
} catch (final IOException e) {
|
||||
throw new SetupException(e);
|
||||
}
|
||||
@@ -188,7 +191,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
}
|
||||
|
||||
private boolean checkSystemFFmpeg() throws IOException {
|
||||
final var result = run(configuration.systemFFmpegPath().toString(), "-version");
|
||||
final var result = run(List.of(configuration.systemFFmpegPath().toString(), "-version"), Duration.ofSeconds(5));
|
||||
return result.exitCode() == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import org.tukaani.xz.XZInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* XZ implementation of {@link Decompresser}
|
||||
*/
|
||||
public class XZDecompresser implements Decompresser {
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
throw new IllegalArgumentException("Unsupported path : " + archive);
|
||||
}
|
||||
try (final var xzIn = new XZInputStream(Files.newInputStream(archive));
|
||||
final var out = Files.newOutputStream(destination)) {
|
||||
xzIn.transferTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathSupported(final Path path) {
|
||||
return path.getFileName().toString().endsWith(".xz");
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,5 @@ module com.github.gtache.autosubtitle.ffmpeg {
|
||||
|
||||
exports com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
exports com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
exports com.github.gtache.autosubtitle.archive.ffmpeg;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.github.gtache.autosubtitle.archive.ffmpeg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TestTarArchiver {
|
||||
|
||||
private final TarArchiver tarArchiver;
|
||||
|
||||
TestTarArchiver() {
|
||||
this.tarArchiver = new TarArchiver();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsPathSupported() {
|
||||
assertTrue(tarArchiver.isPathSupported(Path.of("test.tar")));
|
||||
assertFalse(tarArchiver.isPathSupported(Path.of("test")));
|
||||
assertFalse(tarArchiver.isPathSupported(Path.of("test.txt")));
|
||||
assertFalse(tarArchiver.isPathSupported(Path.of("test.tar2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecompress(@TempDir final Path tempDir) throws IOException {
|
||||
final var file = tempDir.resolve("test.tar");
|
||||
try (final var in = getClass().getResourceAsStream("in.tar")) {
|
||||
Files.copy(in, file);
|
||||
}
|
||||
tarArchiver.decompress(file, tempDir);
|
||||
final var inTxt = tempDir.resolve("in.txt");
|
||||
final var bin = tempDir.resolve("bin");
|
||||
final var binTxt = bin.resolve("bin.txt");
|
||||
final var lib = tempDir.resolve("lib");
|
||||
final var libTxt = lib.resolve("lib.txt");
|
||||
|
||||
assertTrue(Files.exists(inTxt));
|
||||
assertEquals("in", Files.readString(inTxt));
|
||||
assertTrue(Files.exists(bin));
|
||||
assertTrue(Files.exists(binTxt));
|
||||
assertEquals("bin", Files.readString(binTxt));
|
||||
assertTrue(Files.exists(lib));
|
||||
assertTrue(Files.exists(libTxt));
|
||||
assertEquals("lib", Files.readString(libTxt));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(IllegalArgumentException.class, () -> tarArchiver.decompress(Paths.get("file.txt"), Paths.get("target")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.github.gtache.autosubtitle.archive.ffmpeg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TestXZArchiver {
|
||||
|
||||
private final XZArchiver xzArchiver;
|
||||
|
||||
TestXZArchiver() {
|
||||
this.xzArchiver = new XZArchiver();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsPathSupported() {
|
||||
assertTrue(xzArchiver.isPathSupported(Path.of("test.xz")));
|
||||
assertFalse(xzArchiver.isPathSupported(Path.of("test")));
|
||||
assertFalse(xzArchiver.isPathSupported(Path.of("test.txt")));
|
||||
assertFalse(xzArchiver.isPathSupported(Path.of("test.xz2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecompress(@TempDir final Path tempDir) throws IOException {
|
||||
final var file = tempDir.resolve("in.txt.xz");
|
||||
try (final var in = getClass().getResourceAsStream("in.txt.xz")) {
|
||||
Files.copy(in, file);
|
||||
}
|
||||
xzArchiver.decompress(file, tempDir);
|
||||
final var inTxt = tempDir.resolve("in.txt");
|
||||
|
||||
assertTrue(Files.isRegularFile(inTxt));
|
||||
assertEquals("in", Files.readString(inTxt));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(IllegalArgumentException.class, () -> xzArchiver.decompress(Paths.get("file.txt"), Paths.get("target")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.github.gtache.autosubtitle.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestFFmpegVideoConverter {
|
||||
|
||||
private final FFmpegVideoConverter converter;
|
||||
private final SubtitleConverter subtitleConverter;
|
||||
private final Video video;
|
||||
private final VideoInfo videoInfo;
|
||||
private final Path tmpFile;
|
||||
private final Path outputPath;
|
||||
private final SubtitleCollection<?> collection;
|
||||
|
||||
TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @Mock final Video video,
|
||||
@Mock final VideoInfo videoInfo, @Mock final SubtitleCollection<?> collection) 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.videoInfo = Objects.requireNonNull(videoInfo);
|
||||
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.converter = new FFmpegVideoConverter(tmpFile, tmpFile, Map.of("srt", subtitleConverter));
|
||||
this.collection = Objects.requireNonNull(collection);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() throws IOException {
|
||||
Files.deleteIfExists(tmpFile);
|
||||
Files.deleteIfExists(outputPath);
|
||||
}
|
||||
|
||||
//TODO tests
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
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 org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
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 static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class TestFFmpegVideoLoader {
|
||||
|
||||
private final FFprobeVideoLoader loader;
|
||||
private final Path tmpFile;
|
||||
private final Path outputPath;
|
||||
|
||||
TestFFmpegVideoLoader() 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)) {
|
||||
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
this.outputPath = Path.of(output, "test-ffprobe-output.txt");
|
||||
this.loader = new FFprobeVideoLoader(tmpFile, tmpFile);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() throws IOException {
|
||||
Files.deleteIfExists(tmpFile);
|
||||
Files.deleteIfExists(outputPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Doesn't work")
|
||||
void testLoadVideo() throws IOException {
|
||||
final var in = Paths.get("in.mp4");
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.FFmpegSetupConfiguration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static com.github.gtache.autosubtitle.impl.OS.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
class TestFFmpegSetupModule {
|
||||
|
||||
private static final String FFMPEG = "ffmpeg";
|
||||
private static final String FFPROBE = "ffprobe";
|
||||
|
||||
private final Path root;
|
||||
private final String extension;
|
||||
|
||||
TestFFmpegSetupModule() {
|
||||
this.root = Paths.get("root");
|
||||
this.extension = "ext";
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFFmpegSetupConfiguration() {
|
||||
final var bundledPath = mock(Path.class);
|
||||
final var systemPath = mock(Path.class);
|
||||
final var ffmpegInstallerPath = mock(Path.class);
|
||||
final var ffprobeInstallerPath = mock(Path.class);
|
||||
final var os = mock(OS.class);
|
||||
final var architecture = mock(Architecture.class);
|
||||
final var expected = new FFmpegSetupConfiguration(root, bundledPath, systemPath, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture);
|
||||
assertEquals(expected, FFmpegSetupModule.providesFFmpegSetupConfiguration(root, bundledPath, systemPath, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvidesFFmpegInstallerPath() {
|
||||
assertEquals(root.resolve(FFMPEG + "-installer.tar.xz"), FFmpegSetupModule.providesFFmpegInstallerPath(root, LINUX));
|
||||
assertEquals(root.resolve(FFMPEG + "-installer.zip"), FFmpegSetupModule.providesFFmpegInstallerPath(root, WINDOWS));
|
||||
assertEquals(root.resolve(FFMPEG + "-installer.zip"), FFmpegSetupModule.providesFFmpegInstallerPath(root, MAC));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvidesFFProbeInstallerPath() {
|
||||
assertEquals(root.resolve(FFPROBE + "-installer.tar.xz"), FFmpegSetupModule.providesFFProbeInstallerPath(root, LINUX));
|
||||
assertEquals(root.resolve(FFPROBE + "-installer.zip"), FFmpegSetupModule.providesFFProbeInstallerPath(root, WINDOWS));
|
||||
assertEquals(root.resolve(FFPROBE + "-installer.zip"), FFmpegSetupModule.providesFFProbeInstallerPath(root, MAC));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvidesBundledRoot() {
|
||||
assertEquals(root.resolve(FFMPEG), FFmpegSetupModule.providesFFBundledRoot(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFFprobeBundledPath() {
|
||||
assertEquals(root.resolve(FFPROBE + extension), FFmpegSetupModule.providesFFProbeBundledPath(root, extension));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFFprobeSystemPath() {
|
||||
assertEquals(Paths.get(FFPROBE + extension), FFmpegSetupModule.providesFFProbeSystemPath(extension));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testFFmpegBundledPath() {
|
||||
assertEquals(root.resolve(FFMPEG + extension), FFmpegSetupModule.providesFFmpegBundledPath(root, extension));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFFmpegSystemPath() {
|
||||
assertEquals(Paths.get(FFMPEG + extension), FFmpegSetupModule.providesFFmpegSystemPath(extension));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersion() {
|
||||
assertEquals("7.0.1", FFmpegSetupModule.providesFFmpegVersion());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
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.nio.file.Path;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestFFmpegSetupConfiguration {
|
||||
|
||||
private final Path root;
|
||||
private final Path bundledFFmpegPath;
|
||||
private final Path systemFFmpegPath;
|
||||
private final Path ffmpegInstallerPath;
|
||||
private final Path ffprobeInstallerPath;
|
||||
private final OS os;
|
||||
private final Architecture architecture;
|
||||
|
||||
TestFFmpegSetupConfiguration(@Mock final Path root, @Mock final Path bundledFFmpegPath,
|
||||
@Mock final Path systemFFmpegPath, @Mock final Path ffmpegInstallerPath,
|
||||
@Mock final Path ffprobeInstallerPath, @Mock final OS os,
|
||||
@Mock final Architecture architecture) {
|
||||
this.root = requireNonNull(root);
|
||||
this.bundledFFmpegPath = requireNonNull(bundledFFmpegPath);
|
||||
this.systemFFmpegPath = requireNonNull(systemFFmpegPath);
|
||||
this.ffmpegInstallerPath = requireNonNull(ffmpegInstallerPath);
|
||||
this.ffprobeInstallerPath = requireNonNull(ffprobeInstallerPath);
|
||||
this.os = requireNonNull(os);
|
||||
this.architecture = requireNonNull(architecture);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetters() {
|
||||
final var config = new FFmpegSetupConfiguration(root, bundledFFmpegPath, systemFFmpegPath, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture);
|
||||
assertEquals(root, config.root());
|
||||
assertEquals(bundledFFmpegPath, config.bundledFFmpegPath());
|
||||
assertEquals(systemFFmpegPath, config.systemFFmpegPath());
|
||||
assertEquals(ffmpegInstallerPath, config.ffmpegInstallerPath());
|
||||
assertEquals(ffprobeInstallerPath, config.ffprobeInstallerPath());
|
||||
assertEquals(os, config.os());
|
||||
assertEquals(architecture, config.architecture());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(null, bundledFFmpegPath, systemFFmpegPath, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture));
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(root, null, systemFFmpegPath, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture));
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(root, bundledFFmpegPath, null, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture));
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(root, bundledFFmpegPath, systemFFmpegPath, null, ffprobeInstallerPath, os, architecture));
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(root, bundledFFmpegPath, systemFFmpegPath, ffmpegInstallerPath, null, os, architecture));
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(root, bundledFFmpegPath, systemFFmpegPath, ffmpegInstallerPath, ffprobeInstallerPath, null, architecture));
|
||||
assertThrows(NullPointerException.class, () -> new FFmpegSetupConfiguration(root, bundledFFmpegPath, systemFFmpegPath, ffmpegInstallerPath, ffprobeInstallerPath, os, null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
class TestFFmpegSetupManager {
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
bin
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
in
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
lib
|
||||
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
$TempDir = [System.IO.Path]::GetTempPath()
|
||||
$Output = "$TempDir\test-ffmpeg-output.txt"
|
||||
|
||||
Write-Output "$args" > $Output
|
||||
exit
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -o errexit -o noclobber -o pipefail -o nounset
|
||||
|
||||
output=/tmp/test-ffmpeg-output.txt
|
||||
|
||||
echo "$@" >| $output
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
$TempDir = [System.IO.Path]::GetTempPath()
|
||||
$Output = "$TempDir\test-ffprobe-output.txt"
|
||||
|
||||
Write-Output "$args" > $Output
|
||||
Write-Host "1920,1080,25"
|
||||
exit
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -o errexit -o noclobber -o pipefail -o nounset
|
||||
|
||||
output=/tmp/test-ffprobe-output.txt
|
||||
|
||||
echo "$@" >| $output
|
||||
echo "1920,1080,25"
|
||||
13
ffmpeg/src/test/resources/log4j2-test.xml
Normal file
13
ffmpeg/src/test/resources/log4j2-test.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="INFO">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -13,5 +13,5 @@ public interface MainModel {
|
||||
/**
|
||||
* @param index The index of the tab to select
|
||||
*/
|
||||
void selectTab(int index);
|
||||
void setSelectedTab(int index);
|
||||
}
|
||||
|
||||
@@ -19,20 +19,6 @@ public interface SetupModel {
|
||||
*/
|
||||
void setSubtitleExtractorStatus(SetupStatus status);
|
||||
|
||||
/**
|
||||
* @return whether the subtitle extractor is installed
|
||||
*/
|
||||
default boolean isSubtitleExtractorInstalled() {
|
||||
return subtitleExtractorStatus().isInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether an update is available for the subtitle extractor
|
||||
*/
|
||||
default boolean isSubtitleExtractorUpdateAvailable() {
|
||||
return subtitleExtractorStatus() == SetupStatus.UPDATE_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the progress of the subtitle extractor setup
|
||||
*/
|
||||
@@ -69,20 +55,6 @@ public interface SetupModel {
|
||||
*/
|
||||
void setVideoConverterStatus(SetupStatus status);
|
||||
|
||||
/**
|
||||
* @return whether the video converter is installed
|
||||
*/
|
||||
default boolean isVideoConverterInstalled() {
|
||||
return videoConverterStatus().isInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether an update is available for the video converter
|
||||
*/
|
||||
default boolean isVideoConverterUpdateAvailable() {
|
||||
return videoConverterStatus() == SetupStatus.UPDATE_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the progress of the video converter setup
|
||||
*/
|
||||
@@ -119,20 +91,6 @@ public interface SetupModel {
|
||||
*/
|
||||
void setTranslatorStatus(SetupStatus status);
|
||||
|
||||
/**
|
||||
* @return whether the translator is installed
|
||||
*/
|
||||
default boolean isTranslatorInstalled() {
|
||||
return translatorStatus().isInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether an update is available for the translator
|
||||
*/
|
||||
default boolean isTranslatorUpdateAvailable() {
|
||||
return translatorStatus() == SetupStatus.UPDATE_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the progress of the translator setup
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.github.gtache.autosubtitle.gui;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Controller for the subtitles view
|
||||
*/
|
||||
public interface SubtitlesController {
|
||||
|
||||
/**
|
||||
* Selects the given language for edition
|
||||
*
|
||||
* @param language The language
|
||||
*/
|
||||
void selectLanguage(final Language language);
|
||||
|
||||
/**
|
||||
* Deletes a language
|
||||
*
|
||||
* @param language The language
|
||||
*/
|
||||
void deleteLanguage(final Language language);
|
||||
|
||||
/**
|
||||
* Saves the subtitles to the given path
|
||||
*
|
||||
* @param file The output path
|
||||
*/
|
||||
void saveSubtitles(final Path file);
|
||||
|
||||
/**
|
||||
* Loads a subtitles file
|
||||
*
|
||||
* @param file The path to the file
|
||||
*/
|
||||
void loadSubtitles(final Path file);
|
||||
|
||||
/**
|
||||
* @return the model
|
||||
*/
|
||||
SubtitlesModel model();
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.github.gtache.autosubtitle.gui;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Model for the subtitles view
|
||||
*
|
||||
* @param <T> The type of subtitle
|
||||
* @param <U> The type of subtitle collection
|
||||
*/
|
||||
public interface SubtitlesModel<T extends Subtitle, U extends SubtitleCollection<T>> {
|
||||
|
||||
/**
|
||||
* @return The list of available video languages
|
||||
*/
|
||||
List<Language> availableVideoLanguages();
|
||||
|
||||
/**
|
||||
* @return the video language
|
||||
*/
|
||||
Language videoLanguage();
|
||||
|
||||
/**
|
||||
* Sets the video language
|
||||
*
|
||||
* @param language The new language
|
||||
*/
|
||||
void setVideoLanguage(Language language);
|
||||
|
||||
/**
|
||||
* @return The list of available translations languages
|
||||
*/
|
||||
List<Language> availableTranslationsLanguage();
|
||||
|
||||
/**
|
||||
* @return The list of selected translations languages
|
||||
*/
|
||||
List<Language> selectedTranslationsLanguages();
|
||||
|
||||
/**
|
||||
* @return The currently selected language
|
||||
*/
|
||||
Language selectedLanguage();
|
||||
|
||||
/**
|
||||
* @param language The new selected language
|
||||
*/
|
||||
void setSelectedLanguage(Language language);
|
||||
|
||||
/**
|
||||
* @return The mapping of language to subtitles
|
||||
*/
|
||||
Map<Language, U> collections();
|
||||
|
||||
/**
|
||||
* @return The currently selected collection
|
||||
*/
|
||||
U selectedCollection();
|
||||
|
||||
/**
|
||||
* @param collection The new selected collection
|
||||
*/
|
||||
void setSelectedCollection(U collection);
|
||||
|
||||
/**
|
||||
* @return The mapping of language to subtitles
|
||||
*/
|
||||
Map<Language, U> originalCollections();
|
||||
|
||||
/**
|
||||
* @return The list of selected subtitles
|
||||
*/
|
||||
List<T> selectedSubtitles();
|
||||
|
||||
/**
|
||||
* @return The currently selected subtitle
|
||||
*/
|
||||
T selectedSubtitle();
|
||||
|
||||
/**
|
||||
* @param subtitle The new selected subtitle
|
||||
*/
|
||||
void setSelectedSubtitle(T subtitle);
|
||||
|
||||
/**
|
||||
* @return Whether the user can load subtitles
|
||||
*/
|
||||
boolean canLoadSubtitles();
|
||||
|
||||
/**
|
||||
* @param canLoadSubtitles Whether the user can load subtitles
|
||||
*/
|
||||
void setCanLoadSubtitles(boolean canLoadSubtitles);
|
||||
|
||||
/**
|
||||
* @return Whether the user can add subtitles
|
||||
*/
|
||||
boolean canAddSubtitle();
|
||||
|
||||
/**
|
||||
* @param canAddSubtitle Whether the user can add subtitles
|
||||
*/
|
||||
void setCanAddSubtitle(boolean canAddSubtitle);
|
||||
|
||||
/**
|
||||
* @return Whether the user can reset subtitles
|
||||
*/
|
||||
boolean canResetSubtitles();
|
||||
|
||||
/**
|
||||
* @param canResetSubtitles Whether the user can reset subtitles
|
||||
*/
|
||||
void setCanResetSubtitles(boolean canResetSubtitles);
|
||||
|
||||
/**
|
||||
* @return Whether the user can save subtitles
|
||||
*/
|
||||
boolean canSaveSubtitles();
|
||||
|
||||
/**
|
||||
* @return Whether subtitles are currently being translated
|
||||
*/
|
||||
boolean isTranslating();
|
||||
|
||||
/**
|
||||
* @param translating Whether subtitles are currently being translated
|
||||
*/
|
||||
void setTranslating(boolean translating);
|
||||
|
||||
/**
|
||||
* @return Whether the user can edit the table
|
||||
*/
|
||||
boolean canEditTable();
|
||||
|
||||
/**
|
||||
* Sets whether the user can edit the table
|
||||
*
|
||||
* @param canEditTable Whether the user can edit the table
|
||||
*/
|
||||
void setCanEditTable(boolean canEditTable);
|
||||
}
|
||||
@@ -18,21 +18,7 @@ public interface WorkController {
|
||||
* @param file The path to the video
|
||||
*/
|
||||
void loadVideo(final Path file);
|
||||
|
||||
/**
|
||||
* Saves the subtitles to the given path
|
||||
*
|
||||
* @param file The output path
|
||||
*/
|
||||
void saveSubtitles(final Path file);
|
||||
|
||||
/**
|
||||
* Loads a subtitles file
|
||||
*
|
||||
* @param file The path to the file
|
||||
*/
|
||||
void loadSubtitles(final Path file);
|
||||
|
||||
|
||||
/**
|
||||
* @return The model
|
||||
*/
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package com.github.gtache.autosubtitle.gui;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.subtitle.EditableSubtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Model for the main view
|
||||
*/
|
||||
@@ -18,6 +14,11 @@ public interface WorkModel {
|
||||
*/
|
||||
Video video();
|
||||
|
||||
/**
|
||||
* @param video The new video
|
||||
*/
|
||||
void setVideo(Video video);
|
||||
|
||||
/**
|
||||
* @return The current extraction model
|
||||
*/
|
||||
@@ -28,56 +29,6 @@ public interface WorkModel {
|
||||
*/
|
||||
void setExtractionModel(ExtractionModel model);
|
||||
|
||||
/**
|
||||
* @return The current subtitle collection
|
||||
*/
|
||||
SubtitleCollection subtitleCollection();
|
||||
|
||||
/**
|
||||
* @return The current list of subtitles
|
||||
*/
|
||||
List<EditableSubtitle> subtitles();
|
||||
|
||||
/**
|
||||
* @return The current text
|
||||
*/
|
||||
String text();
|
||||
|
||||
/**
|
||||
* @return The original extracted subtitles (used to reset)
|
||||
*/
|
||||
List<EditableSubtitle> originalSubtitles();
|
||||
|
||||
/**
|
||||
* @return The currently selected subtitle
|
||||
*/
|
||||
EditableSubtitle selectedSubtitle();
|
||||
|
||||
/**
|
||||
* @return The list of available video languages
|
||||
*/
|
||||
List<Language> availableVideoLanguages();
|
||||
|
||||
/**
|
||||
* @return The list of available translations languages
|
||||
*/
|
||||
List<Language> availableTranslationsLanguage();
|
||||
|
||||
/**
|
||||
* @return The video language
|
||||
*/
|
||||
Language videoLanguage();
|
||||
|
||||
/**
|
||||
* @param language The video language
|
||||
*/
|
||||
void setVideoLanguage(Language language);
|
||||
|
||||
/**
|
||||
* @return The list of selected translations
|
||||
*/
|
||||
List<Language> translations();
|
||||
|
||||
/**
|
||||
* @return The current status
|
||||
*/
|
||||
@@ -97,4 +48,36 @@ public interface WorkModel {
|
||||
* @param progress The new progress
|
||||
*/
|
||||
void setProgress(double progress);
|
||||
|
||||
/**
|
||||
* @return Whether the user can extract subtitles
|
||||
*/
|
||||
boolean canExtract();
|
||||
|
||||
/**
|
||||
* @return Whether the user can export subtitles
|
||||
*/
|
||||
boolean canExport();
|
||||
|
||||
/**
|
||||
* @param canExport Whether the user can export subtitles
|
||||
*/
|
||||
void setCanExport(boolean canExport);
|
||||
|
||||
/**
|
||||
* @return Whether the progress bar and label are currently visible
|
||||
*/
|
||||
boolean isProgressVisible();
|
||||
|
||||
/**
|
||||
* @return The last extracted collection
|
||||
*/
|
||||
SubtitleCollection<?> extractedCollection();
|
||||
|
||||
/**
|
||||
* Sets the last extracted collection
|
||||
*
|
||||
* @param collection The last extracted collection
|
||||
*/
|
||||
void setExtractedCollection(SubtitleCollection<?> collection);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,52 @@
|
||||
package com.github.gtache.autosubtitle.gui.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Combines multiple resource bundles
|
||||
*/
|
||||
public class CombinedResourceBundle extends ResourceBundle {
|
||||
private static final Logger logger = LogManager.getLogger(CombinedResourceBundle.class);
|
||||
|
||||
private final Map<String, String> resources;
|
||||
private final Locale locale;
|
||||
|
||||
public CombinedResourceBundle(final ResourceBundle... bundles) {
|
||||
this(Arrays.asList(bundles));
|
||||
public CombinedResourceBundle(final ResourceBundle... resourceBundles) {
|
||||
this(Arrays.asList(resourceBundles));
|
||||
}
|
||||
|
||||
public CombinedResourceBundle(final Iterable<ResourceBundle> bundles) {
|
||||
public CombinedResourceBundle(final List<ResourceBundle> resourceBundles) {
|
||||
final var filteredBundles = resourceBundles.stream().filter(Objects::nonNull).toList();
|
||||
if (filteredBundles.size() != resourceBundles.size()) {
|
||||
logger.warn("There was one or more null bundles in the inner bundles");
|
||||
}
|
||||
if (filteredBundles.isEmpty()) {
|
||||
throw new IllegalArgumentException("The bundle should contain at least one bundle");
|
||||
}
|
||||
this.resources = new HashMap<>();
|
||||
bundles.forEach(rb -> rb.getKeys().asIterator().forEachRemaining(key -> resources.put(key, rb.getString(key))));
|
||||
filteredBundles.forEach(r -> r.keySet().forEach(s -> resources.put(s, r.getString(s))));
|
||||
this.locale = filteredBundles.getFirst().getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleGetObject(final String key) {
|
||||
return resources.get(key);
|
||||
public Object handleGetObject(final String key) {
|
||||
if (resources.containsKey(key)) {
|
||||
return resources.get(key);
|
||||
} else {
|
||||
throw new MissingResourceException(key + " not found", "CombinedResourceBundle", key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getKeys() {
|
||||
return Collections.enumeration(resources.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.gtache.autosubtitle.gui.impl.spi;
|
||||
|
||||
import java.util.spi.ResourceBundleProvider;
|
||||
|
||||
/**
|
||||
* Provider for SubtitlesBundle
|
||||
*/
|
||||
public interface SubtitlesBundleProvider extends ResourceBundleProvider {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.gtache.autosubtitle.gui.impl.spi;
|
||||
|
||||
import java.util.spi.AbstractResourceBundleProvider;
|
||||
|
||||
/**
|
||||
* Implementation of {@link SubtitlesBundleProvider}
|
||||
*/
|
||||
public class SubtitlesBundleProviderImpl extends AbstractResourceBundleProvider implements SubtitlesBundleProvider {
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public final class GuiCoreModule {
|
||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.SetupBundle"),
|
||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.WorkBundle"),
|
||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.ParametersBundle"),
|
||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.SubtitlesBundle"),
|
||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.MediaBundle"));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.github.gtache.autosubtitle.gui.impl.spi.WorkBundleProviderImpl;
|
||||
module com.github.gtache.autosubtitle.gui.core {
|
||||
requires transitive com.github.gtache.autosubtitle.gui.api;
|
||||
requires transitive com.github.gtache.autosubtitle.core;
|
||||
requires org.apache.logging.log4j;
|
||||
exports com.github.gtache.autosubtitle.gui.impl;
|
||||
exports com.github.gtache.autosubtitle.gui.impl.spi;
|
||||
exports com.github.gtache.autosubtitle.modules.gui.impl;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
subtitles.button.load.label=Load subtitles...
|
||||
subtitles.button.reset.label=Reset subtitles
|
||||
subtitles.button.subtitles.save.label=Save subtitles...
|
||||
subtitles.export.error.label=Error during the export : {0}
|
||||
subtitles.export.error.title=Error exporting
|
||||
subtitles.language.label=Video language
|
||||
subtitles.load.error.label=Error loading subtitles : {0}
|
||||
subtitles.load.error.title=Error loading
|
||||
subtitles.save.error.label=Error saving subtitles : {0}
|
||||
subtitles.save.error.title=Error saving
|
||||
subtitles.table.column.from.label=From
|
||||
subtitles.table.column.text.label=Text
|
||||
subtitles.table.column.to.label=To
|
||||
subtitles.translate.label=Automatic translations
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user