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$/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/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/client/src/main/resources" 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/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/core/src/main/resources" 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" />
|
<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$/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/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/resources" 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/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/whisper/src/main/resources" 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" />
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
1
.idea/sonarlint.xml
generated
1
.idea/sonarlint.xml
generated
@@ -4,6 +4,7 @@
|
|||||||
<option name="moduleMapping">
|
<option name="moduleMapping">
|
||||||
<map>
|
<map>
|
||||||
<entry key="autosubtitle-gui-fx" value="autosubtitle-fx" />
|
<entry key="autosubtitle-gui-fx" value="autosubtitle-fx" />
|
||||||
|
<entry key="whisper-base" value="whisperbase" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
|||||||
/**
|
/**
|
||||||
* Translates texts and subtitles
|
* Translates texts and subtitles
|
||||||
*/
|
*/
|
||||||
public interface Translator {
|
public interface Translator<T extends Subtitle> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guesses the language of the given text
|
* Guesses the language of the given text
|
||||||
@@ -42,7 +42,7 @@ public interface Translator {
|
|||||||
* @param to The target language
|
* @param to The target language
|
||||||
* @return The translated subtitle
|
* @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
|
* Translates the given subtitles collection to the given language
|
||||||
@@ -51,5 +51,5 @@ public interface Translator {
|
|||||||
* @param to The target language
|
* @param to The target language
|
||||||
* @return The translated subtitles collection
|
* @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
|
* @return The modified video
|
||||||
* @throws IOException If an I/O error occurs
|
* @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
|
* Adds soft subtitles to the given video
|
||||||
@@ -29,7 +29,7 @@ public interface VideoConverter {
|
|||||||
* @param path The output path
|
* @param path The output path
|
||||||
* @throws IOException If an I/O error occurs
|
* @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
|
* Adds hard subtitles to the given video
|
||||||
@@ -39,7 +39,7 @@ public interface VideoConverter {
|
|||||||
* @return The modified video
|
* @return The modified video
|
||||||
* @throws IOException If an I/O error occurs
|
* @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
|
* Adds hard subtitles to the given video
|
||||||
@@ -49,7 +49,7 @@ public interface VideoConverter {
|
|||||||
* @param path The output path
|
* @param path The output path
|
||||||
* @throws IOException If an I/O error occurs
|
* @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
|
* 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;
|
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
|
* @param duration The maximum time to wait
|
||||||
* @return The process result
|
* @return The process result
|
||||||
|
* @throws IOException if an error occurs
|
||||||
*/
|
*/
|
||||||
ProcessResult join(final Duration duration) throws IOException;
|
ProcessResult join(final Duration duration) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.gtache.autosubtitle.process;
|
package com.github.gtache.autosubtitle.process;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -10,24 +11,25 @@ import java.util.List;
|
|||||||
public interface ProcessRunner {
|
public interface ProcessRunner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a command
|
* Runs a command and waits max 1 hour for the process to run
|
||||||
*
|
*
|
||||||
* @param args the command
|
* @param args the command
|
||||||
* @return the result
|
* @return the result
|
||||||
* @throws IOException if something goes wrong
|
* @throws IOException if something goes wrong
|
||||||
*/
|
*/
|
||||||
default ProcessResult run(final String... args) throws IOException {
|
default ProcessResult run(final String... args) throws IOException {
|
||||||
return run(Arrays.asList(args));
|
return run(Arrays.asList(args), Duration.ofHours(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a command
|
* Runs a command
|
||||||
*
|
*
|
||||||
* @param args the command
|
* @param args the command
|
||||||
|
* @param duration The maximum duration to wait for
|
||||||
* @return the result
|
* @return the result
|
||||||
* @throws IOException if something goes wrong
|
* @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
|
* Starts a process
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import java.util.Collection;
|
|||||||
/**
|
/**
|
||||||
* Represents a collection of {@link Subtitle}
|
* Represents a collection of {@link Subtitle}
|
||||||
*/
|
*/
|
||||||
public interface SubtitleCollection {
|
public interface SubtitleCollection<T extends Subtitle> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The whole text of the subtitles
|
* @return The whole text of the subtitles
|
||||||
@@ -17,7 +17,7 @@ public interface SubtitleCollection {
|
|||||||
/**
|
/**
|
||||||
* @return The subtitles
|
* @return The subtitles
|
||||||
*/
|
*/
|
||||||
Collection<? extends Subtitle> subtitles();
|
Collection<T> subtitles();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The language of the 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;
|
package 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.SubtitleCollection;
|
||||||
|
|
||||||
import java.io.IOException;
|
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
|
* 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
|
* Converts the subtitle collection
|
||||||
@@ -17,7 +18,7 @@ public interface SubtitleConverter {
|
|||||||
* @param collection The collection
|
* @param collection The collection
|
||||||
* @return The converted subtitles as the content of a file
|
* @return The converted subtitles as the content of a file
|
||||||
*/
|
*/
|
||||||
String format(final SubtitleCollection collection);
|
String format(final SubtitleCollection<?> collection);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a subtitle collection
|
* Parses a subtitle collection
|
||||||
@@ -26,7 +27,7 @@ public interface SubtitleConverter {
|
|||||||
* @return The subtitle collection
|
* @return The subtitle collection
|
||||||
* @throws ParseException If an error occurred
|
* @throws ParseException If an error occurred
|
||||||
*/
|
*/
|
||||||
default SubtitleCollection parse(final Path file) throws ParseException {
|
default SubtitleCollection<T> parse(final Path file) throws ParseException {
|
||||||
try {
|
try {
|
||||||
final var content = Files.readString(file);
|
final var content = Files.readString(file);
|
||||||
return parse(content);
|
return parse(content);
|
||||||
@@ -42,7 +43,7 @@ public interface SubtitleConverter {
|
|||||||
* @return The subtitle collection
|
* @return The subtitle collection
|
||||||
* @throws ParseException If an error occurred
|
* @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
|
* 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.Audio;
|
||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.Video;
|
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.SubtitleCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts subtitles from a video or audio
|
* Extracts subtitles from a video or audio
|
||||||
*/
|
*/
|
||||||
public interface SubtitleExtractor {
|
public interface SubtitleExtractor<T extends Subtitle> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener
|
* Adds a listener
|
||||||
@@ -37,7 +38,7 @@ public interface SubtitleExtractor {
|
|||||||
* @return The extracted subtitle collection
|
* @return The extracted subtitle collection
|
||||||
* @throws ExtractException If an error occurs
|
* @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);
|
return extract(video, Language.AUTO, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ public interface SubtitleExtractor {
|
|||||||
* @return The extracted subtitle collection
|
* @return The extracted subtitle collection
|
||||||
* @throws ExtractException If an error occurs
|
* @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
|
* Extracts the subtitles from an audio
|
||||||
@@ -60,7 +61,7 @@ public interface SubtitleExtractor {
|
|||||||
* @return The extracted subtitle collection
|
* @return The extracted subtitle collection
|
||||||
* @throws ExtractException If an error occurs
|
* @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);
|
return extract(audio, Language.AUTO, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,5 +74,5 @@ public interface SubtitleExtractor {
|
|||||||
* @return The extracted subtitle collection
|
* @return The extracted subtitle collection
|
||||||
* @throws ExtractException If an error occurs
|
* @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 {
|
module com.github.gtache.autosubtitle.api {
|
||||||
exports com.github.gtache.autosubtitle;
|
exports com.github.gtache.autosubtitle;
|
||||||
|
exports com.github.gtache.autosubtitle.archive;
|
||||||
exports com.github.gtache.autosubtitle.process;
|
exports com.github.gtache.autosubtitle.process;
|
||||||
exports com.github.gtache.autosubtitle.setup;
|
exports com.github.gtache.autosubtitle.setup;
|
||||||
exports com.github.gtache.autosubtitle.subtitle;
|
exports com.github.gtache.autosubtitle.subtitle;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.com.github.gtache.autosubtitle.subtitle.converter;
|
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.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
@@ -23,10 +24,10 @@ import static org.mockito.Mockito.when;
|
|||||||
class TestSubtitleConverter {
|
class TestSubtitleConverter {
|
||||||
|
|
||||||
private final SubtitleConverter subtitleConverter;
|
private final SubtitleConverter subtitleConverter;
|
||||||
private final SubtitleCollection subtitleCollection;
|
private final SubtitleCollection<Subtitle> subtitleCollection;
|
||||||
|
|
||||||
TestSubtitleConverter(@Mock final SubtitleConverter subtitleConverter,
|
TestSubtitleConverter(@Mock final SubtitleConverter subtitleConverter,
|
||||||
@Mock final SubtitleCollection subtitleCollection) {
|
@Mock final SubtitleCollection<Subtitle> subtitleCollection) {
|
||||||
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
||||||
this.subtitleCollection = Objects.requireNonNull(subtitleCollection);
|
this.subtitleCollection = Objects.requireNonNull(subtitleCollection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ class TestProcessRunner {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRunVarargs() throws IOException {
|
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();
|
when(processRunner.run(any(String[].class))).thenCallRealMethod();
|
||||||
|
|
||||||
assertEquals(result, processRunner.run("arg1", "arg2"));
|
assertEquals(result, processRunner.run("arg1", "arg2"));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||||
<artifactId>autosubtitle-whisper</artifactId>
|
<artifactId>autosubtitle-whisperx</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<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.deepl.DeepLModule;
|
||||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegModule;
|
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegModule;
|
||||||
import com.github.gtache.autosubtitle.modules.impl.CoreModule;
|
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 com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.util.Map;
|
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
|
@Singleton
|
||||||
public interface CliComponent {
|
public interface CliComponent {
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
module com.github.gtache.autosubtitle.cli {
|
module com.github.gtache.autosubtitle.cli {
|
||||||
requires com.github.gtache.autosubtitle.deepl;
|
requires com.github.gtache.autosubtitle.deepl;
|
||||||
requires com.github.gtache.autosubtitle.ffmpeg;
|
requires com.github.gtache.autosubtitle.ffmpeg;
|
||||||
requires com.github.gtache.autosubtitle.whisper;
|
requires com.github.gtache.autosubtitle.whisperx;
|
||||||
requires info.picocli;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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.Architecture;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
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.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
@@ -56,13 +58,13 @@ public final class CondaSetupModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@CondaRootPath
|
@CondaRootPath
|
||||||
static Path providesCondaRootPath(@WhisperBundledRoot final Path root, final OS os) {
|
static Path providesCondaRootPath(@ToolsRoot final Path root) {
|
||||||
return root.resolve(MINICONDA3);
|
return root.resolve(MINICONDA3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@CondaInstallerPath
|
@CondaInstallerPath
|
||||||
static Path providesCondaInstallerPath(@WhisperBundledRoot final Path root, final OS os) {
|
static Path providesCondaInstallerPath(@CacheRoot final Path root, final OS os) {
|
||||||
return root.resolve("cache").resolve("conda-install" + (os == OS.WINDOWS ? ".exe" : ".sh"));
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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.Architecture;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
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.impl.OS;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
@@ -13,7 +13,9 @@ import java.io.IOException;
|
|||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
@@ -67,8 +69,8 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
logger.info("Conda downloaded");
|
logger.info("Conda downloaded");
|
||||||
}
|
}
|
||||||
switch (configuration.os()) {
|
switch (configuration.os()) {
|
||||||
case WINDOWS -> installWindows();
|
case OS.WINDOWS -> installWindows();
|
||||||
case MAC, LINUX -> installLinux();
|
case OS.MAC, OS.LINUX -> installLinux();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
final var installerPath = configuration.condaInstallerPath();
|
final var installerPath = configuration.condaInstallerPath();
|
||||||
final var rootPath = configuration.condaRootPath();
|
final var rootPath = configuration.condaRootPath();
|
||||||
logger.info("Installing conda using {}", installerPath);
|
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) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Installed conda to {}", rootPath);
|
logger.info("Installed conda to {}", rootPath);
|
||||||
} else {
|
} else {
|
||||||
@@ -93,7 +95,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
final var installerPath = configuration.condaInstallerPath();
|
final var installerPath = configuration.condaInstallerPath();
|
||||||
final var rootPath = configuration.condaRootPath();
|
final var rootPath = configuration.condaRootPath();
|
||||||
logger.info("Installing conda using {}", installerPath);
|
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) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Installed conda to {}", rootPath);
|
logger.info("Installed conda to {}", rootPath);
|
||||||
} else {
|
} else {
|
||||||
@@ -106,9 +108,9 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
|
|
||||||
private void downloadConda() throws SetupException {
|
private void downloadConda() throws SetupException {
|
||||||
switch (configuration.os()) {
|
switch (configuration.os()) {
|
||||||
case WINDOWS -> downloadCondaWindows();
|
case OS.WINDOWS -> downloadCondaWindows();
|
||||||
case MAC -> downloadCondaMac();
|
case OS.MAC -> downloadCondaMac();
|
||||||
case LINUX -> downloadCondaLinux();
|
case OS.LINUX -> downloadCondaLinux();
|
||||||
}
|
}
|
||||||
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());
|
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());
|
||||||
}
|
}
|
||||||
@@ -152,7 +154,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
@Override
|
@Override
|
||||||
public void update() throws SetupException {
|
public void update() throws SetupException {
|
||||||
try {
|
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) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Conda updated");
|
logger.info("Conda updated");
|
||||||
} else {
|
} 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();
|
final var args = Stream.concat(Stream.of(getCondaPath().toString(), "create", "-y", "-p", path.toString(), "python=" + pythonVersion), Arrays.stream(packages)).toList();
|
||||||
try {
|
try {
|
||||||
logger.info("Creating venv {}", path);
|
logger.info("Creating venv {}", path);
|
||||||
final var result = run(args);
|
final var result = run(args, Duration.ofMinutes(15));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Created venv {}", path);
|
logger.info("Created venv {}", path);
|
||||||
} else {
|
} else {
|
||||||
@@ -200,7 +202,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
|
|
||||||
private boolean isSystemCondaInstalled() throws SetupException {
|
private boolean isSystemCondaInstalled() throws SetupException {
|
||||||
try {
|
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) {
|
if (result.exitCode() == 0) {
|
||||||
final var output = result.output().getFirst();
|
final var output = result.output().getFirst();
|
||||||
final var versionString = output.substring(output.indexOf(' ') + 1);
|
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>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
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
|
@Override
|
||||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||||
if (!isPathSupported(archive)) {
|
if (!isPathSupported(archive)) {
|
||||||
@@ -49,9 +64,9 @@ public class ZipDecompresser implements Decompresser {
|
|||||||
}
|
}
|
||||||
return destPath;
|
return destPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPathSupported(final Path path) {
|
public String archiveExtension() {
|
||||||
return path.getFileName().toString().endsWith(".zip");
|
return "zip";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,33 @@
|
|||||||
package com.github.gtache.autosubtitle.modules.impl;
|
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.Architecture;
|
||||||
import com.github.gtache.autosubtitle.impl.DaggerException;
|
import com.github.gtache.autosubtitle.impl.DaggerException;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.impl.SetupModule;
|
import com.github.gtache.autosubtitle.modules.setup.impl.SetupModule;
|
||||||
import com.github.gtache.autosubtitle.modules.subtitle.impl.SubtitleModule;
|
import com.github.gtache.autosubtitle.modules.subtitle.impl.SubtitleModule;
|
||||||
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
import dagger.multibindings.IntoMap;
|
||||||
|
import dagger.multibindings.StringKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dagger module for Core
|
* Dagger module for Core
|
||||||
*/
|
*/
|
||||||
@Module(includes = {SetupModule.class, SubtitleModule.class})
|
@Module(includes = {SetupModule.class, SubtitleModule.class})
|
||||||
public final class CoreModule {
|
public abstract class CoreModule {
|
||||||
|
|
||||||
private CoreModule() {
|
private CoreModule() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@StringKey("zip")
|
||||||
|
@IntoMap
|
||||||
|
abstract Archiver bindsZipDecompresser(final ZipDecompresser decompresser);
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
static OS providesOS() {
|
static OS providesOS() {
|
||||||
final var os = OS.getOS();
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
@@ -12,5 +12,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
@Documented
|
@Documented
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||||
public @interface WhisperVersion {
|
public @interface CacheRoot {
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dagger core module for setup
|
* Dagger core module for setup
|
||||||
@@ -14,6 +16,18 @@ public final class SetupModule {
|
|||||||
private SetupModule() {
|
private SetupModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@CacheRoot
|
||||||
|
static Path providesCacheRoot() {
|
||||||
|
return Paths.get("cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@ToolsRoot
|
||||||
|
static Path providesToolsRoot() {
|
||||||
|
return Paths.get("tools");
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
static HttpClient providesHttpClient() {
|
static HttpClient providesHttpClient() {
|
||||||
return HttpClient.newHttpClient();
|
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;
|
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.SubtitleConverter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.impl.SRTSubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.impl.SRTSubtitleConverter;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImporterExporterImpl;
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
@@ -20,4 +22,7 @@ public abstract class SubtitleModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@StringKey("srt")
|
@StringKey("srt")
|
||||||
abstract SubtitleConverter bindsSubtitleConverter(final SRTSubtitleConverter converter);
|
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.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation of {@link ProcessRunner}
|
* Base implementation of {@link ProcessRunner}
|
||||||
@@ -19,19 +18,9 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
|||||||
private static final Logger logger = LogManager.getLogger(AbstractProcessRunner.class);
|
private static final Logger logger = LogManager.getLogger(AbstractProcessRunner.class);
|
||||||
|
|
||||||
@Override
|
@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);
|
final var listener = startListen(args);
|
||||||
CompletableFuture.runAsync(() -> {
|
return listener.join(duration);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -51,11 +40,12 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
|||||||
/**
|
/**
|
||||||
* Runs a process and writes the output to the log
|
* 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
|
* @return the result
|
||||||
* @throws IOException if an error occurs
|
* @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);
|
final var listener = startListen(args);
|
||||||
var line = listener.readLine();
|
var line = listener.readLine();
|
||||||
final var processName = args.getFirst();
|
final var processName = args.getFirst();
|
||||||
@@ -63,6 +53,6 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
|||||||
logger.info("[{}]: {}", processName, line);
|
logger.info("[{}]: {}", processName, line);
|
||||||
line = listener.readLine();
|
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.ProcessListener;
|
||||||
import com.github.gtache.autosubtitle.process.ProcessResult;
|
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.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link ProcessListener}
|
* Implementation of {@link ProcessListener}
|
||||||
*/
|
*/
|
||||||
public class ProcessListenerImpl implements ProcessListener {
|
public class ProcessListenerImpl implements ProcessListener {
|
||||||
|
private static final Logger logger = LogManager.getLogger(ProcessListenerImpl.class);
|
||||||
private final Process process;
|
private final Process process;
|
||||||
private final BufferedReader reader;
|
private final BufferedReader reader;
|
||||||
private final List<String> output;
|
private final List<String> output;
|
||||||
@@ -30,7 +33,7 @@ public class ProcessListenerImpl implements ProcessListener {
|
|||||||
public ProcessListenerImpl(final Process process) {
|
public ProcessListenerImpl(final Process process) {
|
||||||
this.process = Objects.requireNonNull(process);
|
this.process = Objects.requireNonNull(process);
|
||||||
this.reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
|
this.reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
|
||||||
this.output = new ArrayList<>();
|
this.output = new CopyOnWriteArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,7 +51,24 @@ public class ProcessListenerImpl implements ProcessListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 {
|
try {
|
||||||
process.waitFor(duration.getSeconds(), TimeUnit.SECONDS);
|
process.waitFor(duration.getSeconds(), TimeUnit.SECONDS);
|
||||||
} catch (final InterruptedException e) {
|
} catch (final InterruptedException e) {
|
||||||
@@ -58,11 +78,6 @@ public class ProcessListenerImpl implements ProcessListener {
|
|||||||
if (process.isAlive()) {
|
if (process.isAlive()) {
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
}
|
}
|
||||||
//Reads lines to output
|
|
||||||
while (readLine() != null) {
|
|
||||||
//Do nothing
|
|
||||||
}
|
|
||||||
reader.close();
|
|
||||||
return new ProcessResultImpl(process.exitValue(), output);
|
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 com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -20,17 +19,17 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* Converts subtitles to SRT format
|
* Converts subtitles to SRT format
|
||||||
*/
|
*/
|
||||||
@Singleton
|
public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||||
public class SRTSubtitleConverter implements SubtitleConverter {
|
|
||||||
|
|
||||||
private final Translator translator;
|
private final Translator<?> translator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SRTSubtitleConverter(final Translator translator) {
|
SRTSubtitleConverter(final Translator translator) {
|
||||||
this.translator = requireNonNull(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();
|
final var subtitles = collection.subtitles().stream().sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
||||||
return IntStream.range(0, subtitles.size()).mapToObj(i -> {
|
return IntStream.range(0, subtitles.size()).mapToObj(i -> {
|
||||||
final var subtitle = subtitles.get(i);
|
final var subtitle = subtitles.get(i);
|
||||||
@@ -51,7 +50,7 @@ public class SRTSubtitleConverter implements SubtitleConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleCollection parse(final String content) throws ParseException {
|
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
|
||||||
try {
|
try {
|
||||||
final var elements = content.split("\n\n");
|
final var elements = content.split("\n\n");
|
||||||
final var subtitles = Arrays.stream(elements).filter(element -> !element.isBlank()).map(element -> {
|
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);
|
return new SubtitleImpl(text, start, end, null, null);
|
||||||
}).toList();
|
}).toList();
|
||||||
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining(" "));
|
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) {
|
} catch (final Exception e) {
|
||||||
throw new ParseException(e);
|
throw new ParseException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* Implementation of {@link SubtitleCollection}
|
* Implementation of {@link SubtitleCollection}
|
||||||
*/
|
*/
|
||||||
public record SubtitleCollectionImpl(String text, Collection<? extends Subtitle> subtitles,
|
public record SubtitleCollectionImpl<T extends Subtitle>(String text, Collection<T> subtitles,
|
||||||
Language language) implements SubtitleCollection {
|
Language language) implements SubtitleCollection<T> {
|
||||||
|
|
||||||
public SubtitleCollectionImpl {
|
public SubtitleCollectionImpl {
|
||||||
Objects.requireNonNull(text);
|
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);
|
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;
|
requires org.apache.logging.log4j;
|
||||||
|
|
||||||
exports com.github.gtache.autosubtitle.impl;
|
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.process.impl;
|
||||||
exports com.github.gtache.autosubtitle.setup.impl;
|
exports com.github.gtache.autosubtitle.setup.impl;
|
||||||
exports com.github.gtache.autosubtitle.subtitle.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
|
@Test
|
||||||
void testRun() throws IOException {
|
void testRun() throws IOException {
|
||||||
final var expected = new ProcessResultImpl(0, List.of("1", "2", "3"));
|
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);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class TestAbstractProcessRunner {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRunListen() throws IOException {
|
void testRunListen() throws IOException {
|
||||||
final var result = dummyProcessRunner.runListen(ARGS);
|
final var result = dummyProcessRunner.runListen(ARGS, Duration.ofSeconds(5));
|
||||||
assertEquals(0, result.exitCode());
|
assertEquals(0, result.exitCode());
|
||||||
assertEquals(List.of("1", "2", "3"), result.output());
|
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 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 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 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);
|
final var converter = new SRTSubtitleConverter(translator);
|
||||||
assertEquals(subtitles, converter.parse(in));
|
assertEquals(subtitles, converter.parse(in));
|
||||||
assertEquals(in, converter.format(subtitles));
|
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.Audio;
|
||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.Video;
|
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.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
|
||||||
@@ -56,12 +57,12 @@ class TestAbstractSubtitleExtractor {
|
|||||||
private static final class DummySubtitleExtractor extends AbstractSubtitleExtractor {
|
private static final class DummySubtitleExtractor extends AbstractSubtitleExtractor {
|
||||||
|
|
||||||
@Override
|
@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();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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();
|
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}
|
* DeepL implementation of {@link Translator}
|
||||||
*/
|
*/
|
||||||
public class DeepLTranslator implements Translator {
|
public class DeepLTranslator implements Translator<Subtitle> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DeepLTranslator() {
|
DeepLTranslator() {
|
||||||
@@ -32,7 +32,7 @@ public class DeepLTranslator implements Translator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleCollection translate(final SubtitleCollection collection, final Language to) {
|
public SubtitleCollection<Subtitle> translate(final SubtitleCollection<?> collection, final Language to) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,11 @@
|
|||||||
<groupId>org.tukaani</groupId>
|
<groupId>org.tukaani</groupId>
|
||||||
<artifactId>xz</artifactId>
|
<artifactId>xz</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</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 org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
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
|
@Override
|
||||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||||
if (!isPathSupported(archive)) {
|
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 destPath = destinationDir.resolve(entry.getName());
|
||||||
|
|
||||||
final var destDirPath = destinationDir.toAbsolutePath().toString();
|
final var destDirPath = destinationDir.toAbsolutePath().toString();
|
||||||
@@ -51,7 +65,7 @@ public class TarDecompresser implements Decompresser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPathSupported(final Path path) {
|
public String archiveExtension() {
|
||||||
return path.getFileName().toString().endsWith(".tar");
|
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.FileAudioImpl;
|
||||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegBundledPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegBundledPath;
|
||||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegSystemPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath;
|
||||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -32,30 +32,29 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* FFmpeg implementation of {@link VideoConverter}
|
* FFmpeg implementation of {@link VideoConverter}
|
||||||
*/
|
*/
|
||||||
@Singleton
|
|
||||||
public class FFmpegVideoConverter extends AbstractProcessRunner implements VideoConverter {
|
public class FFmpegVideoConverter extends AbstractProcessRunner implements VideoConverter {
|
||||||
|
|
||||||
private static final String TEMP_FILE_PREFIX = "autosubtitle";
|
private static final String TEMP_FILE_PREFIX = "autosubtitle";
|
||||||
private final Path bundledPath;
|
private final Path bundledPath;
|
||||||
private final Path systemPath;
|
private final Path systemPath;
|
||||||
private final SubtitleConverter subtitleConverter;
|
private final SubtitleConverter<?> subtitleConverter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath, final Map<String, SubtitleConverter> subtitleConverters) {
|
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath, final Map<String, SubtitleConverter> subtitleConverters) {
|
||||||
this.bundledPath = requireNonNull(bundledPath);
|
this.bundledPath = requireNonNull(bundledPath);
|
||||||
this.systemPath = requireNonNull(systemPath);
|
this.systemPath = requireNonNull(systemPath);
|
||||||
this.subtitleConverter = requireNonNull(subtitleConverters.get("srt"));
|
this.subtitleConverter = subtitleConverters.get("srt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Video addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles) throws IOException {
|
public Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles) throws IOException {
|
||||||
final var out = getTempFile("mkv"); //Soft ass subtitles are only supported by mkv apparently
|
final var out = getTempFile(video.info().videoFormat());
|
||||||
addSoftSubtitles(video, subtitles, out);
|
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
|
@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 videoPath = getPath(video);
|
||||||
final var collectionMap = dumpCollections(subtitles);
|
final var collectionMap = dumpCollections(subtitles);
|
||||||
final var args = new ArrayList<String>();
|
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("language=" + c.language().iso3());
|
||||||
});
|
});
|
||||||
args.add(path.toString());
|
args.add(path.toString());
|
||||||
runListen(args);
|
runListen(args, Duration.ofHours(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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());
|
final var out = getTempFile(video.info().videoFormat());
|
||||||
addHardSubtitles(video, subtitles, out);
|
addHardSubtitles(video, subtitles, out);
|
||||||
return new FileVideoImpl(out, video.info());
|
return new FileVideoImpl(out, video.info());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 videoPath = getPath(video);
|
||||||
final var subtitlesPath = dumpSubtitles(subtitles);
|
final var subtitlesPath = dumpSubtitles(subtitles);
|
||||||
final var escapedPath = escapeVF(subtitlesPath.toString());
|
final var escapedPath = escapeVF(subtitlesPath.toString());
|
||||||
@@ -120,7 +119,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
|||||||
subtitleArg,
|
subtitleArg,
|
||||||
path.toString()
|
path.toString()
|
||||||
);
|
);
|
||||||
runListen(args);
|
runListen(args, Duration.ofHours(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String escapeVF(final String path) {
|
private static String escapeVF(final String path) {
|
||||||
@@ -145,7 +144,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
|||||||
"0:v",
|
"0:v",
|
||||||
dumpVideoPath.toString()
|
dumpVideoPath.toString()
|
||||||
);
|
);
|
||||||
runListen(args);
|
runListen(args, Duration.ofHours(1));
|
||||||
Files.deleteIfExists(dumpVideoPath);
|
Files.deleteIfExists(dumpVideoPath);
|
||||||
return new FileAudioImpl(audioPath, new AudioInfoImpl("wav", video.info().duration()));
|
return new FileAudioImpl(audioPath, new AudioInfoImpl("wav", video.info().duration()));
|
||||||
}
|
}
|
||||||
@@ -166,15 +165,15 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SequencedMap<SubtitleCollection, Path> dumpCollections(final Collection<SubtitleCollection> collections) throws IOException {
|
private <T extends SubtitleCollection<?>> SequencedMap<T, Path> dumpCollections(final Collection<T> collections) throws IOException {
|
||||||
final var ret = LinkedHashMap.<SubtitleCollection, Path>newLinkedHashMap(collections.size());
|
final var ret = LinkedHashMap.<T, Path>newLinkedHashMap(collections.size());
|
||||||
for (final var subtitles : collections) {
|
for (final var subtitles : collections) {
|
||||||
ret.put(subtitles, dumpSubtitles(subtitles));
|
ret.put(subtitles, dumpSubtitles(subtitles));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path dumpSubtitles(final SubtitleCollection subtitles) throws IOException {
|
private Path dumpSubtitles(final SubtitleCollection<?> subtitles) throws IOException {
|
||||||
final var path = getTempFile("srt");
|
final var path = getTempFile("srt");
|
||||||
Files.writeString(path, subtitleConverter.format(subtitles));
|
Files.writeString(path, subtitleConverter.format(subtitles));
|
||||||
return path;
|
return path;
|
||||||
|
|||||||
@@ -4,22 +4,22 @@ import com.github.gtache.autosubtitle.Video;
|
|||||||
import com.github.gtache.autosubtitle.VideoLoader;
|
import com.github.gtache.autosubtitle.VideoLoader;
|
||||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeBundledPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeBundledPath;
|
||||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeSystemPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeSystemPath;
|
||||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FFprobe implementation of {@link VideoLoader}
|
* FFprobe implementation of {@link VideoLoader}
|
||||||
*/
|
*/
|
||||||
@Singleton
|
|
||||||
public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLoader {
|
public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLoader {
|
||||||
|
|
||||||
private final Path bundledPath;
|
private final Path bundledPath;
|
||||||
@@ -33,7 +33,7 @@ public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Video loadVideo(final Path path) throws IOException {
|
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 resolution = result.output().getLast();
|
||||||
final var split = resolution.split(",");
|
final var split = resolution.split(",");
|
||||||
final var width = Integer.parseInt(split[0]);
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
@@ -1,24 +1,17 @@
|
|||||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
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.Architecture;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
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.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.modules.setup.impl.VideoConverterSetup;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
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.FFmpegSetupConfiguration;
|
||||||
import com.github.gtache.autosubtitle.setup.ffmpeg.FFmpegSetupManager;
|
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.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -41,20 +34,16 @@ public abstract class FFmpegSetupModule {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
|
||||||
@StringKey("zip")
|
|
||||||
@IntoMap
|
|
||||||
abstract Decompresser bindsZipDecompresser(final ZipDecompresser decompresser);
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@StringKey("tar")
|
@StringKey("tar")
|
||||||
@IntoMap
|
@IntoMap
|
||||||
abstract Decompresser bindsTarDecompresser(final TarDecompresser decompresser);
|
abstract Archiver bindsTarDecompresser(final TarArchiver decompresser);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@StringKey("xz")
|
@StringKey("xz")
|
||||||
@IntoMap
|
@IntoMap
|
||||||
abstract Decompresser bindsXzDecompresser(final XZDecompresser decompresser);
|
abstract Archiver bindsXZDecompresser(final XZArchiver decompresser);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@VideoConverterSetup
|
@VideoConverterSetup
|
||||||
@@ -69,19 +58,19 @@ public abstract class FFmpegSetupModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FFmpegInstallerPath
|
@FFmpegInstallerPath
|
||||||
static Path providesFFmpegInstallerPath(@FFBundledRoot final Path root, final OS os) {
|
static Path providesFFmpegInstallerPath(@CacheRoot final Path root, final OS os) {
|
||||||
return root.resolve("cache").resolve("ffmpeg-installer" + getInstallerExtension(os));
|
return root.resolve(FFMPEG + "-installer" + getInstallerExtension(os));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FFProbeInstallerPath
|
@FFProbeInstallerPath
|
||||||
static Path providesFFProbeInstallerPath(@FFBundledRoot final Path root, final OS os) {
|
static Path providesFFProbeInstallerPath(@CacheRoot final Path root, final OS os) {
|
||||||
return root.resolve("cache").resolve("ffprobe-installer" + getInstallerExtension(os));
|
return root.resolve(FFPROBE + "-installer" + getInstallerExtension(os));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getInstallerExtension(final OS os) {
|
private static String getInstallerExtension(final OS os) {
|
||||||
if (os == OS.LINUX) {
|
if (os == OS.LINUX) {
|
||||||
return ".tar.gz";
|
return ".tar.xz";
|
||||||
} else {
|
} else {
|
||||||
return ".zip";
|
return ".zip";
|
||||||
}
|
}
|
||||||
@@ -89,8 +78,8 @@ public abstract class FFmpegSetupModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FFBundledRoot
|
@FFBundledRoot
|
||||||
static Path providesFFBundledRoot() {
|
static Path providesFFBundledRoot(@ToolsRoot final Path root) {
|
||||||
return Paths.get("tools", FFMPEG);
|
return root.resolve(FFMPEG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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 javax.inject.Qualifier;
|
||||||
import java.lang.annotation.Documented;
|
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;
|
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.impl.Architecture;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||||
@@ -13,6 +14,8 @@ import java.io.IOException;
|
|||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.github.gtache.autosubtitle.impl.Architecture.ARMEL;
|
import static com.github.gtache.autosubtitle.impl.Architecture.ARMEL;
|
||||||
@@ -26,14 +29,14 @@ import static java.util.Objects.requireNonNull;
|
|||||||
public class FFmpegSetupManager extends AbstractSetupManager {
|
public class FFmpegSetupManager extends AbstractSetupManager {
|
||||||
private static final Logger logger = LogManager.getLogger(FFmpegSetupManager.class);
|
private static final Logger logger = LogManager.getLogger(FFmpegSetupManager.class);
|
||||||
private final FFmpegSetupConfiguration configuration;
|
private final FFmpegSetupConfiguration configuration;
|
||||||
private final Map<String, Decompresser> decompressers;
|
private final Map<String, Archiver> archivers;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final Map<String, Decompresser> decompressers,
|
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final Map<String, Archiver> archivers,
|
||||||
final HttpClient httpClient) {
|
final HttpClient httpClient) {
|
||||||
super(httpClient);
|
super(httpClient);
|
||||||
this.configuration = requireNonNull(configuration);
|
this.configuration = requireNonNull(configuration);
|
||||||
this.decompressers = Map.copyOf(decompressers);
|
this.archivers = Map.copyOf(archivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,7 +135,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
|||||||
try {
|
try {
|
||||||
final var filename = from.getFileName().toString();
|
final var filename = from.getFileName().toString();
|
||||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||||
decompressers.get(extension).decompress(from, to);
|
archivers.get(extension).decompress(from, to);
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new SetupException(e);
|
throw new SetupException(e);
|
||||||
}
|
}
|
||||||
@@ -188,7 +191,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkSystemFFmpeg() throws IOException {
|
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;
|
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.ffmpeg;
|
||||||
exports com.github.gtache.autosubtitle.modules.setup.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
|
* @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);
|
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
|
* @return the progress of the subtitle extractor setup
|
||||||
*/
|
*/
|
||||||
@@ -69,20 +55,6 @@ public interface SetupModel {
|
|||||||
*/
|
*/
|
||||||
void setVideoConverterStatus(SetupStatus status);
|
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
|
* @return the progress of the video converter setup
|
||||||
*/
|
*/
|
||||||
@@ -119,20 +91,6 @@ public interface SetupModel {
|
|||||||
*/
|
*/
|
||||||
void setTranslatorStatus(SetupStatus status);
|
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
|
* @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
|
* @param file The path to the video
|
||||||
*/
|
*/
|
||||||
void loadVideo(final Path file);
|
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
|
* @return The model
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
package com.github.gtache.autosubtitle.gui;
|
package com.github.gtache.autosubtitle.gui;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Language;
|
|
||||||
import com.github.gtache.autosubtitle.Video;
|
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.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model for the main view
|
* Model for the main view
|
||||||
*/
|
*/
|
||||||
@@ -18,6 +14,11 @@ public interface WorkModel {
|
|||||||
*/
|
*/
|
||||||
Video video();
|
Video video();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param video The new video
|
||||||
|
*/
|
||||||
|
void setVideo(Video video);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The current extraction model
|
* @return The current extraction model
|
||||||
*/
|
*/
|
||||||
@@ -28,56 +29,6 @@ public interface WorkModel {
|
|||||||
*/
|
*/
|
||||||
void setExtractionModel(ExtractionModel model);
|
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
|
* @return The current status
|
||||||
*/
|
*/
|
||||||
@@ -97,4 +48,36 @@ public interface WorkModel {
|
|||||||
* @param progress The new progress
|
* @param progress The new progress
|
||||||
*/
|
*/
|
||||||
void setProgress(double 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;
|
package com.github.gtache.autosubtitle.gui.impl;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import java.util.Collections;
|
import org.apache.logging.log4j.Logger;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines multiple resource bundles
|
* Combines multiple resource bundles
|
||||||
*/
|
*/
|
||||||
public class CombinedResourceBundle extends ResourceBundle {
|
public class CombinedResourceBundle extends ResourceBundle {
|
||||||
|
private static final Logger logger = LogManager.getLogger(CombinedResourceBundle.class);
|
||||||
|
|
||||||
private final Map<String, String> resources;
|
private final Map<String, String> resources;
|
||||||
|
private final Locale locale;
|
||||||
|
|
||||||
public CombinedResourceBundle(final ResourceBundle... bundles) {
|
public CombinedResourceBundle(final ResourceBundle... resourceBundles) {
|
||||||
this(Arrays.asList(bundles));
|
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<>();
|
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
|
@Override
|
||||||
protected Object handleGetObject(final String key) {
|
public Object handleGetObject(final String key) {
|
||||||
return resources.get(key);
|
if (resources.containsKey(key)) {
|
||||||
|
return resources.get(key);
|
||||||
|
} else {
|
||||||
|
throw new MissingResourceException(key + " not found", "CombinedResourceBundle", key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enumeration<String> getKeys() {
|
public Enumeration<String> getKeys() {
|
||||||
return Collections.enumeration(resources.keySet());
|
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.SetupBundle"),
|
||||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.WorkBundle"),
|
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.ParametersBundle"),
|
||||||
|
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.SubtitlesBundle"),
|
||||||
ResourceBundle.getBundle("com.github.gtache.autosubtitle.gui.impl.MediaBundle"));
|
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 {
|
module com.github.gtache.autosubtitle.gui.core {
|
||||||
requires transitive com.github.gtache.autosubtitle.gui.api;
|
requires transitive com.github.gtache.autosubtitle.gui.api;
|
||||||
requires transitive com.github.gtache.autosubtitle.core;
|
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;
|
||||||
exports com.github.gtache.autosubtitle.gui.impl.spi;
|
exports com.github.gtache.autosubtitle.gui.impl.spi;
|
||||||
exports com.github.gtache.autosubtitle.modules.gui.impl;
|
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