Allows setting max lines and max line length, enables DeepL, adds user setup bridge
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,3 +39,4 @@ build/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
tools
|
tools
|
||||||
|
.secrets
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
package com.github.gtache.autosubtitle;
|
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates texts and subtitles
|
|
||||||
*/
|
|
||||||
public interface Translator<T extends Subtitle> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Guesses the language of the given text
|
|
||||||
*
|
|
||||||
* @param text The text
|
|
||||||
* @return The guessed language
|
|
||||||
*/
|
|
||||||
Language getLanguage(final String text);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Guesses the language of the given subtitle
|
|
||||||
*
|
|
||||||
* @param subtitle The subtitle
|
|
||||||
* @return The guessed language
|
|
||||||
*/
|
|
||||||
default Language getLanguage(final Subtitle subtitle) {
|
|
||||||
return getLanguage(subtitle.content());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates the given text to the given language
|
|
||||||
*
|
|
||||||
* @param text The text to translate
|
|
||||||
* @param to The target language
|
|
||||||
* @return The translated text
|
|
||||||
*/
|
|
||||||
String translate(String text, Language to);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates the given subtitle to the given language
|
|
||||||
*
|
|
||||||
* @param subtitle The subtitle to translate
|
|
||||||
* @param to The target language
|
|
||||||
* @return The translated subtitle
|
|
||||||
*/
|
|
||||||
T translate(Subtitle subtitle, Language to);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates the given subtitles collection to the given language
|
|
||||||
*
|
|
||||||
* @param collection The subtitles collection to translate
|
|
||||||
* @param to The target language
|
|
||||||
* @return The translated subtitles collection
|
|
||||||
*/
|
|
||||||
SubtitleCollection<T> translate(SubtitleCollection<?> collection, Language to);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.github.gtache.autosubtitle.setup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bridge between setup manager and the user
|
||||||
|
*/
|
||||||
|
public interface SetupUserBridge {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks for user input
|
||||||
|
*
|
||||||
|
* @param question the question to display to the user
|
||||||
|
* @return the user input
|
||||||
|
*/
|
||||||
|
String askForUserInput(String question);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks for user choice
|
||||||
|
*
|
||||||
|
* @param question the question to display to the user
|
||||||
|
* @param choices the possible choices
|
||||||
|
* @param <T> the type of the choices
|
||||||
|
* @return the user choice
|
||||||
|
*/
|
||||||
|
<T> T askForUserChoice(String question, List<T> choices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks for user confirmation
|
||||||
|
*
|
||||||
|
* @param question the question to display to the user
|
||||||
|
* @return whether the user confirmed
|
||||||
|
*/
|
||||||
|
boolean askForUserConfirmation(String question);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.github.gtache.autosubtitle.translation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when an error occurs during translation
|
||||||
|
*/
|
||||||
|
public class TranslationException extends Exception {
|
||||||
|
|
||||||
|
public TranslationException(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranslationException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranslationException(final Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.github.gtache.autosubtitle.translation;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.Language;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates texts and subtitles
|
||||||
|
*/
|
||||||
|
public interface Translator<T extends Subtitle> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guesses the language of the given text
|
||||||
|
*
|
||||||
|
* @param text The text
|
||||||
|
* @return The guessed language
|
||||||
|
*/
|
||||||
|
Language getLanguage(final String text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guesses the language of the given subtitle
|
||||||
|
*
|
||||||
|
* @param subtitle The subtitle
|
||||||
|
* @return The guessed language
|
||||||
|
*/
|
||||||
|
default Language getLanguage(final Subtitle subtitle) {
|
||||||
|
return getLanguage(subtitle.content());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given text to the given language
|
||||||
|
*
|
||||||
|
* @param text The text to translate
|
||||||
|
* @param to The target language
|
||||||
|
* @return The translated text
|
||||||
|
* @throws TranslationException if an error occurred during the translation
|
||||||
|
*/
|
||||||
|
default String translate(final String text, final Language to) throws TranslationException {
|
||||||
|
return translate(text, getLanguage(text), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given text to the given language
|
||||||
|
*
|
||||||
|
* @param text The text to translate
|
||||||
|
* @param from The source language
|
||||||
|
* @param to The target language
|
||||||
|
* @return The translated text
|
||||||
|
* @throws TranslationException if an error occurred during the translation
|
||||||
|
*/
|
||||||
|
String translate(String text, Language from, Language to) throws TranslationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given subtitle to the given language
|
||||||
|
*
|
||||||
|
* @param subtitle The subtitle to translate
|
||||||
|
* @param to The target language
|
||||||
|
* @return The translated subtitle
|
||||||
|
* @throws TranslationException if an error occurred during the translation
|
||||||
|
*/
|
||||||
|
default T translate(final Subtitle subtitle, final Language to) throws TranslationException {
|
||||||
|
return translate(subtitle, getLanguage(subtitle), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given subtitle to the given language
|
||||||
|
*
|
||||||
|
* @param subtitle The subtitle to translate
|
||||||
|
* @param from The source language
|
||||||
|
* @param to The target language
|
||||||
|
* @return The translated subtitle
|
||||||
|
* @throws TranslationException if an error occurred during the translation
|
||||||
|
*/
|
||||||
|
T translate(Subtitle subtitle, Language from, Language to) throws TranslationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given subtitles collection to the given language
|
||||||
|
*
|
||||||
|
* @param collection The subtitles collection to translate
|
||||||
|
* @param to The target language
|
||||||
|
* @return The translated subtitles collection
|
||||||
|
* @throws TranslationException if an error occurred during the translation
|
||||||
|
*/
|
||||||
|
default SubtitleCollection<T> translate(final SubtitleCollection<?> collection, final Language to) throws TranslationException {
|
||||||
|
return translate(collection, getLanguage(collection.text()), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given subtitles collection to the given language
|
||||||
|
*
|
||||||
|
* @param collection The subtitles collection to translate
|
||||||
|
* @param from The source language
|
||||||
|
* @param to The target language
|
||||||
|
* @return The translated subtitles collection
|
||||||
|
* @throws TranslationException if an error occurred during the translation
|
||||||
|
*/
|
||||||
|
SubtitleCollection<T> translate(SubtitleCollection<?> collection, Language from, Language to) throws TranslationException;
|
||||||
|
}
|
||||||
@@ -9,4 +9,5 @@ module com.github.gtache.autosubtitle.api {
|
|||||||
exports com.github.gtache.autosubtitle.subtitle;
|
exports com.github.gtache.autosubtitle.subtitle;
|
||||||
exports com.github.gtache.autosubtitle.subtitle.extractor;
|
exports com.github.gtache.autosubtitle.subtitle.extractor;
|
||||||
exports com.github.gtache.autosubtitle.subtitle.converter;
|
exports com.github.gtache.autosubtitle.subtitle.converter;
|
||||||
|
exports com.github.gtache.autosubtitle.translation;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.gtache.autosubtitle;
|
package com.github.gtache.autosubtitle;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||||
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import dagger.Provides;
|
|||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
import dagger.multibindings.StringKey;
|
import dagger.multibindings.StringKey;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dagger module for Core
|
* Dagger module for Core
|
||||||
*/
|
*/
|
||||||
@@ -53,4 +56,22 @@ public abstract class CoreModule {
|
|||||||
static String providesExecutableExtension(final OS os) {
|
static String providesExecutableExtension(final OS os) {
|
||||||
return os == OS.WINDOWS ? ".exe" : "";
|
return os == OS.WINDOWS ? ".exe" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static Preferences providesPreferences() {
|
||||||
|
return Preferences.userRoot().node("/com/github/gtache/autosubtitle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@MaxLineLength
|
||||||
|
static int providesMaxLineLength() {
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@MaxLines
|
||||||
|
static int providesMaxLines() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.github.gtache.autosubtitle.modules.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 MaxLineLength {
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.github.gtache.autosubtitle.modules.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 MaxLines {
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
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;
|
||||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
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 com.github.gtache.autosubtitle.translation.Translator;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module com.github.gtache.autosubtitle.core {
|
|||||||
requires transitive java.net.http;
|
requires transitive java.net.http;
|
||||||
requires transitive javax.inject;
|
requires transitive javax.inject;
|
||||||
requires org.apache.logging.log4j;
|
requires org.apache.logging.log4j;
|
||||||
|
requires java.prefs;
|
||||||
|
|
||||||
exports com.github.gtache.autosubtitle.impl;
|
exports com.github.gtache.autosubtitle.impl;
|
||||||
exports com.github.gtache.autosubtitle.archive.impl;
|
exports com.github.gtache.autosubtitle.archive.impl;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
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 com.github.gtache.autosubtitle.translation.Translator;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
|||||||
@@ -11,11 +11,26 @@
|
|||||||
|
|
||||||
<artifactId>autosubtitle-deepl</artifactId>
|
<artifactId>autosubtitle-deepl</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<deepl.version>1.5.0</deepl.version>
|
||||||
|
<lingua.version>1.2.2</lingua.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||||
<artifactId>autosubtitle-core</artifactId>
|
<artifactId>autosubtitle-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.deepl.api</groupId>
|
||||||
|
<artifactId>deepl-java</artifactId>
|
||||||
|
<version>${deepl.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.pemistahl</groupId>
|
||||||
|
<artifactId>lingua</artifactId>
|
||||||
|
<version>${lingua.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package com.github.gtache.autosubtitle.deepl;
|
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Language;
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DeepL implementation of {@link Translator}
|
|
||||||
*/
|
|
||||||
public class DeepLTranslator implements Translator<Subtitle> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
DeepLTranslator() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Language getLanguage(final String text) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String translate(final String text, final Language to) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Subtitle translate(final Subtitle subtitle, final Language to) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubtitleCollection<Subtitle> translate(final SubtitleCollection<?> collection, final Language to) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,28 @@
|
|||||||
package com.github.gtache.autosubtitle.modules.deepl;
|
package com.github.gtache.autosubtitle.modules.deepl;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
import com.github.gtache.autosubtitle.modules.setup.deepl.DeepLSetupModule;
|
||||||
import com.github.gtache.autosubtitle.deepl.DeepLTranslator;
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
|
import com.github.gtache.autosubtitle.translation.deepl.DeepLTranslator;
|
||||||
|
import com.github.pemistahl.lingua.api.LanguageDetector;
|
||||||
|
import com.github.pemistahl.lingua.api.LanguageDetectorBuilder;
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dagger module for DeepL
|
* Dagger module for DeepL
|
||||||
*/
|
*/
|
||||||
@Module
|
@Module(includes = DeepLSetupModule.class)
|
||||||
public interface DeepLModule {
|
public abstract class DeepLModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
Translator bindsTranslator(final DeepLTranslator translator);
|
abstract Translator bindsTranslator(final DeepLTranslator translator);
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static LanguageDetector providesLanguageDetector() {
|
||||||
|
return LanguageDetectorBuilder.fromAllSpokenLanguages().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.github.gtache.autosubtitle.modules.setup.deepl;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.modules.setup.impl.TranslatorSetup;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||||
|
import com.github.gtache.autosubtitle.setup.deepl.DeepLSetupManager;
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dagger module for DeepL setup
|
||||||
|
*/
|
||||||
|
@Module
|
||||||
|
public abstract class DeepLSetupModule {
|
||||||
|
private DeepLSetupModule() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@TranslatorSetup
|
||||||
|
abstract SetupManager bindsSetupManager(final DeepLSetupManager setupManager);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.github.gtache.autosubtitle.setup.deepl;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupUserBridge;
|
||||||
|
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.prefs.BackingStoreException;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SetupManager} for DeepL
|
||||||
|
*/
|
||||||
|
public class DeepLSetupManager extends AbstractSetupManager {
|
||||||
|
|
||||||
|
private static final String DEEPL_API_KEY = "deepl.api.key";
|
||||||
|
|
||||||
|
private final SetupUserBridge userBridge;
|
||||||
|
private final Preferences preferences;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DeepLSetupManager(final SetupUserBridge userBridge, final Preferences preferences) {
|
||||||
|
this.userBridge = Objects.requireNonNull(userBridge);
|
||||||
|
this.preferences = Objects.requireNonNull(preferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SetupStatus getStatus() throws SetupException {
|
||||||
|
final var key = preferences.get(DEEPL_API_KEY, null);
|
||||||
|
return key == null ? SetupStatus.NOT_INSTALLED : SetupStatus.BUNDLE_INSTALLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return "DeepL";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void install() throws SetupException {
|
||||||
|
final var key = userBridge.askForUserInput("Please enter your DeepL API key - https://www.deepl.com/pro-api? (It will be stored unencrypted)");
|
||||||
|
if (key == null) {
|
||||||
|
throw new SetupException("API key cannot be null");
|
||||||
|
} else {
|
||||||
|
preferences.put(DEEPL_API_KEY, key);
|
||||||
|
try {
|
||||||
|
preferences.flush();
|
||||||
|
} catch (final BackingStoreException e) {
|
||||||
|
throw new SetupException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstall() throws SetupException {
|
||||||
|
preferences.remove(DEEPL_API_KEY);
|
||||||
|
try {
|
||||||
|
preferences.flush();
|
||||||
|
} catch (final BackingStoreException e) {
|
||||||
|
throw new SetupException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() throws SetupException {
|
||||||
|
//No need to update
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.github.gtache.autosubtitle.translation.deepl;
|
||||||
|
|
||||||
|
import com.deepl.api.DeepLException;
|
||||||
|
import com.github.gtache.autosubtitle.Language;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
||||||
|
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||||
|
import com.github.gtache.autosubtitle.translation.TranslationException;
|
||||||
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
|
import com.github.pemistahl.lingua.api.LanguageDetector;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeepL implementation of {@link Translator}
|
||||||
|
*/
|
||||||
|
public class DeepLTranslator implements Translator<Subtitle> {
|
||||||
|
|
||||||
|
private final Preferences preferences;
|
||||||
|
private final LanguageDetector languageDetector;
|
||||||
|
private com.deepl.api.Translator translator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DeepLTranslator(final Preferences preferences, final LanguageDetector languageDetector) {
|
||||||
|
this.preferences = Objects.requireNonNull(preferences);
|
||||||
|
this.languageDetector = Objects.requireNonNull(languageDetector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Language getLanguage(final String text) {
|
||||||
|
return Language.getLanguage(languageDetector.detectLanguageOf(text).getIsoCode639_1().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translate(final String text, final Language from, final Language to) throws TranslationException {
|
||||||
|
final var currentTranslator = getTranslator();
|
||||||
|
if (currentTranslator == null) {
|
||||||
|
throw new TranslationException("DeepL API key is not set");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final var translated = currentTranslator.translateText(text, from.iso2(), to.iso2());
|
||||||
|
return translated.getText();
|
||||||
|
} catch (final DeepLException e) {
|
||||||
|
throw new TranslationException(e);
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new TranslationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Subtitle translate(final Subtitle subtitle, final Language from, final Language to) throws TranslationException {
|
||||||
|
final var translated = translate(subtitle.content(), from, to);
|
||||||
|
return new SubtitleImpl(translated, subtitle.start(), subtitle.end(), subtitle.font(), subtitle.bounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SubtitleCollection<Subtitle> translate(final SubtitleCollection<?> collection, final Language from, final Language to) throws TranslationException {
|
||||||
|
final var subtitles = new ArrayList<Subtitle>(collection.subtitles().size());
|
||||||
|
for (final var subtitle : collection.subtitles()) {
|
||||||
|
subtitles.add(translate(subtitle, from, to));
|
||||||
|
}
|
||||||
|
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining(""));
|
||||||
|
return new SubtitleCollectionImpl<>(text, subtitles, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
private com.deepl.api.Translator getTranslator() {
|
||||||
|
if (translator == null && preferences.get("deepl.api.key", null) != null) {
|
||||||
|
translator = new com.deepl.api.Translator(preferences.get("deepl.api.key", null));
|
||||||
|
}
|
||||||
|
return translator;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
module com.github.gtache.autosubtitle.deepl {
|
module com.github.gtache.autosubtitle.deepl {
|
||||||
requires transitive com.github.gtache.autosubtitle.core;
|
requires transitive com.github.gtache.autosubtitle.core;
|
||||||
exports com.github.gtache.autosubtitle.deepl;
|
requires transitive deepl.java;
|
||||||
|
requires transitive com.github.pemistahl.lingua;
|
||||||
|
requires transitive java.prefs;
|
||||||
exports com.github.gtache.autosubtitle.modules.deepl;
|
exports com.github.gtache.autosubtitle.modules.deepl;
|
||||||
|
exports com.github.gtache.autosubtitle.translation.deepl;
|
||||||
|
exports com.github.gtache.autosubtitle.setup.deepl;
|
||||||
}
|
}
|
||||||
@@ -64,4 +64,12 @@ public interface ParametersModel {
|
|||||||
* @param fontSize The new font size
|
* @param fontSize The new font size
|
||||||
*/
|
*/
|
||||||
void setFontSize(int fontSize);
|
void setFontSize(int fontSize);
|
||||||
|
|
||||||
|
int maxLineLength();
|
||||||
|
|
||||||
|
void setMaxLineLength(int maxLineLength);
|
||||||
|
|
||||||
|
int maxLines();
|
||||||
|
|
||||||
|
void setMaxLines(int maxLines);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ parameters.button.save.label=Save
|
|||||||
parameters.extraction.model.label=Model to use for subtitle extraction
|
parameters.extraction.model.label=Model to use for subtitle extraction
|
||||||
parameters.subtitles.font.family=Default subtitle font name
|
parameters.subtitles.font.family=Default subtitle font name
|
||||||
parameters.subtitles.font.size=Default subtitle font size
|
parameters.subtitles.font.size=Default subtitle font size
|
||||||
|
parameters.subtitles.max.length.label=Max length of a subtitles line (characters)
|
||||||
|
parameters.subtitles.max.lines.label=Max subtitles lines
|
||||||
parameters.subtitles.output.format=Output format for the subtitles
|
parameters.subtitles.output.format=Output format for the subtitles
|
||||||
@@ -3,4 +3,5 @@ parameters.button.save.label=Sauvegarder
|
|||||||
parameters.extraction.model.label=Mod\u00E8le utilis\u00E9 pour l'extraction des sous-titres
|
parameters.extraction.model.label=Mod\u00E8le utilis\u00E9 pour l'extraction des sous-titres
|
||||||
parameters.subtitles.font.family=Police par d\u00E9faut pour les sous-titres
|
parameters.subtitles.font.family=Police par d\u00E9faut pour les sous-titres
|
||||||
parameters.subtitles.font.size=Taille de la police par d\u00E9faut pour les sous-titres
|
parameters.subtitles.font.size=Taille de la police par d\u00E9faut pour les sous-titres
|
||||||
|
parameters.subtitles.max.length.label=Taille maximale d'une ligne de sous-titres (caract\u00E8res)
|
||||||
parameters.subtitles.output.format=Format de sortie pour les sous-titres
|
parameters.subtitles.output.format=Format de sortie pour les sous-titres
|
||||||
@@ -12,7 +12,7 @@ public abstract class AbstractFXController {
|
|||||||
/**
|
/**
|
||||||
* @return the current window
|
* @return the current window
|
||||||
*/
|
*/
|
||||||
protected abstract Window window();
|
public abstract Window window();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show an error dialog
|
* Show an error dialog
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class FXMainController extends AbstractFXController implements MainContro
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Window window() {
|
public Window window() {
|
||||||
return tabPane.getScene().getWindow();
|
return tabPane.getScene().getWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ public class FXParametersController extends AbstractFXController implements Para
|
|||||||
private PrefixSelectionComboBox<String> fontFamilyCombobox;
|
private PrefixSelectionComboBox<String> fontFamilyCombobox;
|
||||||
@FXML
|
@FXML
|
||||||
private TextField fontSizeField;
|
private TextField fontSizeField;
|
||||||
|
@FXML
|
||||||
|
private TextField maxLengthField;
|
||||||
|
@FXML
|
||||||
|
private TextField maxLinesField;
|
||||||
|
|
||||||
private final FXParametersModel model;
|
private final FXParametersModel model;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
@@ -68,8 +72,12 @@ public class FXParametersController extends AbstractFXController implements Para
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
fontSizeField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), 0, integerFilter));
|
fontSizeField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), 0, integerFilter));
|
||||||
|
maxLengthField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), 0, integerFilter));
|
||||||
|
maxLinesField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), 0, integerFilter));
|
||||||
|
|
||||||
fontSizeField.textProperty().bindBidirectional(model.fontSizeProperty(), new NumberStringConverter());
|
fontSizeField.textProperty().bindBidirectional(model.fontSizeProperty(), new NumberStringConverter());
|
||||||
|
maxLengthField.textProperty().bindBidirectional(model.maxLineLengthProperty(), new NumberStringConverter());
|
||||||
|
maxLinesField.textProperty().bindBidirectional(model.maxLinesProperty(), new NumberStringConverter());
|
||||||
|
|
||||||
loadPreferences();
|
loadPreferences();
|
||||||
}
|
}
|
||||||
@@ -79,11 +87,15 @@ public class FXParametersController extends AbstractFXController implements Para
|
|||||||
final var outputFormat = preferences.get("outputFormat", model.outputFormat().name());
|
final var outputFormat = preferences.get("outputFormat", model.outputFormat().name());
|
||||||
final var fontFamily = preferences.get("fontFamily", model.fontFamily());
|
final var fontFamily = preferences.get("fontFamily", model.fontFamily());
|
||||||
final var fontSize = preferences.getInt("fontSize", model.fontSize());
|
final var fontSize = preferences.getInt("fontSize", model.fontSize());
|
||||||
|
final var maxLineLength = preferences.getInt("maxLineLength", model.maxLineLength());
|
||||||
|
final var maxLines = preferences.getInt("maxLines", model.maxLines());
|
||||||
|
|
||||||
model.setExtractionModel(extractionModelProvider.getExtractionModel(extractionModel));
|
model.setExtractionModel(extractionModelProvider.getExtractionModel(extractionModel));
|
||||||
model.setOutputFormat(OutputFormat.valueOf(outputFormat));
|
model.setOutputFormat(OutputFormat.valueOf(outputFormat));
|
||||||
model.setFontFamily(fontFamily);
|
model.setFontFamily(fontFamily);
|
||||||
model.setFontSize(fontSize);
|
model.setFontSize(fontSize);
|
||||||
|
model.setMaxLineLength(maxLineLength);
|
||||||
|
model.setMaxLines(maxLines);
|
||||||
logger.info("Loaded preferences");
|
logger.info("Loaded preferences");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +107,8 @@ public class FXParametersController extends AbstractFXController implements Para
|
|||||||
preferences.put("outputFormat", model.outputFormat().name());
|
preferences.put("outputFormat", model.outputFormat().name());
|
||||||
preferences.put("fontFamily", model.fontFamily());
|
preferences.put("fontFamily", model.fontFamily());
|
||||||
preferences.putInt("fontSize", model.fontSize());
|
preferences.putInt("fontSize", model.fontSize());
|
||||||
|
preferences.putInt("maxLineLength", model.maxLineLength());
|
||||||
|
preferences.putInt("maxLines", model.maxLines());
|
||||||
try {
|
try {
|
||||||
preferences.flush();
|
preferences.flush();
|
||||||
logger.info("Preferences saved");
|
logger.info("Preferences saved");
|
||||||
@@ -124,7 +138,7 @@ public class FXParametersController extends AbstractFXController implements Para
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Window window() {
|
public Window window() {
|
||||||
return extractionModelCombobox.getScene().getWindow();
|
return extractionModelCombobox.getScene().getWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.github.gtache.autosubtitle.gui.fx;
|
|||||||
import com.github.gtache.autosubtitle.gui.ParametersModel;
|
import com.github.gtache.autosubtitle.gui.ParametersModel;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.impl.FontFamily;
|
import com.github.gtache.autosubtitle.modules.gui.impl.FontFamily;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.impl.FontSize;
|
import com.github.gtache.autosubtitle.modules.gui.impl.FontSize;
|
||||||
|
import com.github.gtache.autosubtitle.modules.impl.MaxLineLength;
|
||||||
|
import com.github.gtache.autosubtitle.modules.impl.MaxLines;
|
||||||
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
|
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModelProvider;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModelProvider;
|
||||||
@@ -31,9 +33,12 @@ public class FXParametersModel implements ParametersModel {
|
|||||||
private final ObservableList<String> availableFontFamilies;
|
private final ObservableList<String> availableFontFamilies;
|
||||||
private final StringProperty fontFamily;
|
private final StringProperty fontFamily;
|
||||||
private final IntegerProperty fontSize;
|
private final IntegerProperty fontSize;
|
||||||
|
private final IntegerProperty maxLineLength;
|
||||||
|
private final IntegerProperty maxLines;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FXParametersModel(final ExtractionModelProvider extractionModelProvider, @FontFamily final String defaultFontFamily, @FontSize final int defaultFontSize) {
|
FXParametersModel(final ExtractionModelProvider extractionModelProvider, @FontFamily final String defaultFontFamily,
|
||||||
|
@FontSize final int defaultFontSize, @MaxLineLength final int defaultMaxLineLength, @MaxLines final int defaultMaxLines) {
|
||||||
this.availableExtractionModels = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(extractionModelProvider.getAvailableExtractionModels()));
|
this.availableExtractionModels = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(extractionModelProvider.getAvailableExtractionModels()));
|
||||||
this.extractionModel = new SimpleObjectProperty<>(extractionModelProvider.getDefaultExtractionModel());
|
this.extractionModel = new SimpleObjectProperty<>(extractionModelProvider.getDefaultExtractionModel());
|
||||||
this.availableOutputFormats = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(OutputFormat.SRT));
|
this.availableOutputFormats = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(OutputFormat.SRT));
|
||||||
@@ -41,6 +46,8 @@ public class FXParametersModel implements ParametersModel {
|
|||||||
this.availableFontFamilies = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList("Arial"));
|
this.availableFontFamilies = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList("Arial"));
|
||||||
this.fontFamily = new SimpleStringProperty(defaultFontFamily);
|
this.fontFamily = new SimpleStringProperty(defaultFontFamily);
|
||||||
this.fontSize = new SimpleIntegerProperty(defaultFontSize);
|
this.fontSize = new SimpleIntegerProperty(defaultFontSize);
|
||||||
|
this.maxLineLength = new SimpleIntegerProperty(defaultMaxLineLength);
|
||||||
|
this.maxLines = new SimpleIntegerProperty(defaultMaxLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,4 +120,32 @@ public class FXParametersModel implements ParametersModel {
|
|||||||
IntegerProperty fontSizeProperty() {
|
IntegerProperty fontSizeProperty() {
|
||||||
return fontSize;
|
return fontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int maxLineLength() {
|
||||||
|
return maxLineLength.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMaxLineLength(final int maxLineLength) {
|
||||||
|
this.maxLineLength.set(maxLineLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
IntegerProperty maxLineLengthProperty() {
|
||||||
|
return maxLineLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int maxLines() {
|
||||||
|
return maxLines.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMaxLines(final int maxLines) {
|
||||||
|
this.maxLines.set(maxLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
IntegerProperty maxLinesProperty() {
|
||||||
|
return maxLines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ public class FXSetupController extends AbstractFXController implements SetupCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Window window() {
|
public Window window() {
|
||||||
return converterNameLabel.getScene().getWindow();
|
return converterNameLabel.getScene().getWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.gui.fx;
|
package com.github.gtache.autosubtitle.gui.fx;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.gui.SubtitlesController;
|
import com.github.gtache.autosubtitle.gui.SubtitlesController;
|
||||||
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
@@ -9,6 +8,8 @@ import com.github.gtache.autosubtitle.subtitle.SubtitleImporterExporter;
|
|||||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||||
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
|
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
|
||||||
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
|
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
|
||||||
|
import com.github.gtache.autosubtitle.translation.TranslationException;
|
||||||
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
@@ -39,6 +40,7 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@@ -129,19 +131,30 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
translationsCombobox.valueProperty().addListener((observable, oldValue, newValue) -> {
|
translationsCombobox.setOnAction(e -> {
|
||||||
if (newValue != null && !model.collections().containsKey(newValue)) {
|
final var value = translationsCombobox.getValue();
|
||||||
|
if (value != null && !model.collections().containsKey(value)) {
|
||||||
model.setTranslating(true);
|
model.setTranslating(true);
|
||||||
CompletableFuture.supplyAsync(() -> translator.translate(model.collections().get(model.videoLanguage()), newValue))
|
CompletableFuture.supplyAsync(() -> {
|
||||||
.whenCompleteAsync((r, t) -> {
|
final var mainCollection = model.collections().get(model.videoLanguage());
|
||||||
if (t == null) {
|
try {
|
||||||
loadCollection(r);
|
if (mainCollection == null) {
|
||||||
model.setSelectedCollection(model.collections().get(newValue));
|
return translator.translate(model.selectedCollection(), value);
|
||||||
} else {
|
} else {
|
||||||
logger.error("Error while translating to {}", newValue, t);
|
return translator.translate(mainCollection, value);
|
||||||
}
|
}
|
||||||
model.setTranslating(false);
|
} catch (final TranslationException ex) {
|
||||||
}, Platform::runLater);
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
|
}).whenCompleteAsync((r, t) -> {
|
||||||
|
if (t == null) {
|
||||||
|
loadCollection(r);
|
||||||
|
model.setSelectedCollection(model.collections().get(value));
|
||||||
|
} else {
|
||||||
|
logger.error("Error while translating to {}", value, t);
|
||||||
|
}
|
||||||
|
model.setTranslating(false);
|
||||||
|
}, Platform::runLater);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
binder.createBindings();
|
binder.createBindings();
|
||||||
@@ -197,7 +210,7 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
final var toRemove = new ArrayList<Tab>();
|
final var toRemove = new ArrayList<Tab>();
|
||||||
final var toAdd = new ArrayList<Tab>();
|
final var toAdd = new ArrayList<Tab>();
|
||||||
tabPane.getTabs().forEach(tab -> {
|
tabPane.getTabs().forEach(tab -> {
|
||||||
if (!model.collections().containsKey(Language.getLanguage(tab.getText()))) {
|
if (tab != mainSubtitlesTab && !model.collections().containsKey(Language.getLanguage(tab.getText()))) {
|
||||||
toRemove.add(tab);
|
toRemove.add(tab);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -237,7 +250,9 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
filePicker.getExtensionFilters().addAll(archiveFilter, allSupportedFilter);
|
filePicker.getExtensionFilters().addAll(archiveFilter, allSupportedFilter);
|
||||||
filePicker.setSelectedExtensionFilter(allSupportedFilter);
|
filePicker.setSelectedExtensionFilter(allSupportedFilter);
|
||||||
final var file = filePicker.showOpenDialog(window());
|
final var file = filePicker.showOpenDialog(window());
|
||||||
loadSubtitles(file.toPath());
|
if (file != null) {
|
||||||
|
loadSubtitles(file.toPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -283,10 +298,11 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
public void loadSubtitles(final Path file) {
|
public void loadSubtitles(final Path file) {
|
||||||
try {
|
try {
|
||||||
final var map = importerExporter.importSubtitles(file);
|
final var map = importerExporter.importSubtitles(file);
|
||||||
map.values().forEach(this::loadCollection);
|
|
||||||
if (model.videoLanguage() == Language.AUTO) {
|
if (model.videoLanguage() == Language.AUTO) {
|
||||||
model.setVideoLanguage(map.keySet().stream().findFirst().orElse(Language.AUTO));
|
model.setVideoLanguage(map.keySet().stream().findFirst().orElse(Language.AUTO));
|
||||||
}
|
}
|
||||||
|
map.values().forEach(this::loadCollection);
|
||||||
|
model.setSelectedCollection(model.collections().get(map.keySet().iterator().next()));
|
||||||
} catch (final IOException | ParseException e) {
|
} catch (final IOException | ParseException e) {
|
||||||
logger.error("Error loading subtitles {}", file, e);
|
logger.error("Error loading subtitles {}", file, e);
|
||||||
showErrorDialog(resources.getString("subtitles.load.error.title"), MessageFormat.format(resources.getString("subtitles.load.error.label"), file));
|
showErrorDialog(resources.getString("subtitles.load.error.title"), MessageFormat.format(resources.getString("subtitles.load.error.label"), file));
|
||||||
@@ -316,7 +332,7 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Window window() {
|
public Window window() {
|
||||||
return saveButton.getScene().getWindow();
|
return saveButton.getScene().getWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.github.gtache.autosubtitle.gui.fx.FXSubtitlesController;
|
|||||||
import com.github.gtache.autosubtitle.gui.fx.FXWorkController;
|
import com.github.gtache.autosubtitle.gui.fx.FXWorkController;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.impl.Pause;
|
import com.github.gtache.autosubtitle.modules.gui.impl.Pause;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.impl.Play;
|
import com.github.gtache.autosubtitle.modules.gui.impl.Play;
|
||||||
|
import com.github.gtache.autosubtitle.modules.setup.gui.fx.FXSetupModule;
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -19,12 +20,11 @@ import javafx.scene.image.Image;
|
|||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.prefs.Preferences;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dagger module for FX
|
* Dagger module for FX
|
||||||
*/
|
*/
|
||||||
@Module
|
@Module(includes = FXSetupModule.class)
|
||||||
public abstract class FXModule {
|
public abstract class FXModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@@ -69,10 +69,4 @@ public abstract class FXModule {
|
|||||||
static Image providesPauseImage(@Pause final byte[] pauseImage) {
|
static Image providesPauseImage(@Pause final byte[] pauseImage) {
|
||||||
return new Image(new ByteArrayInputStream(pauseImage));
|
return new Image(new ByteArrayInputStream(pauseImage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
static Preferences providesPreferences() {
|
|
||||||
return Preferences.userNodeForPackage(FXParametersController.class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.github.gtache.autosubtitle.modules.setup.gui.fx;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupUserBridge;
|
||||||
|
import com.github.gtache.autosubtitle.setup.gui.fx.FXSetupUserBridge;
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dagger module for FX setup
|
||||||
|
*/
|
||||||
|
@Module
|
||||||
|
public abstract class FXSetupModule {
|
||||||
|
|
||||||
|
private FXSetupModule() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract SetupUserBridge bindsSetupUserBridge(final FXSetupUserBridge setupUserBridge);
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.github.gtache.autosubtitle.setup.gui.fx;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.gui.fx.FXMainController;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupUserBridge;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.ChoiceDialog;
|
||||||
|
import javafx.scene.control.TextInputDialog;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FX implementation of {@link SetupUserBridge}
|
||||||
|
*/
|
||||||
|
public class FXSetupUserBridge implements SetupUserBridge {
|
||||||
|
|
||||||
|
private final FXMainController controller;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
FXSetupUserBridge(final FXMainController mainController) {
|
||||||
|
this.controller = Objects.requireNonNull(mainController);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean askForUserConfirmation(final String question) {
|
||||||
|
return showConfirmationDialog("Confirmation", question);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T askForUserChoice(final String question, final List<T> choices) {
|
||||||
|
return showChoiceDialog("Choice", question, choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String askForUserInput(final String question) {
|
||||||
|
return showInputDialog("Input", question);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T showChoiceDialog(final String title, final String message, final List<T> choices) {
|
||||||
|
final var dialog = new ChoiceDialog<>(choices.getFirst(), choices);
|
||||||
|
dialog.initOwner(controller.window());
|
||||||
|
dialog.setHeaderText(message);
|
||||||
|
dialog.setTitle(title);
|
||||||
|
return dialog.showAndWait().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String showInputDialog(final String title, final String message) {
|
||||||
|
final var dialog = new TextInputDialog();
|
||||||
|
dialog.initOwner(controller.window());
|
||||||
|
dialog.setHeaderText(message);
|
||||||
|
dialog.setTitle(title);
|
||||||
|
return dialog.showAndWait().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean showConfirmationDialog(final String title, final String message) {
|
||||||
|
final var alert = new Alert(Alert.AlertType.CONFIRMATION, message, ButtonType.YES, ButtonType.NO);
|
||||||
|
alert.initOwner(controller.window());
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setTitle(title);
|
||||||
|
return alert.showAndWait().map(bt -> bt == ButtonType.YES).orElse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,8 @@ module com.github.gtache.autosubtitle.gui.fx {
|
|||||||
requires transitive java.prefs;
|
requires transitive java.prefs;
|
||||||
|
|
||||||
exports com.github.gtache.autosubtitle.gui.fx;
|
exports com.github.gtache.autosubtitle.gui.fx;
|
||||||
exports com.github.gtache.autosubtitle.modules.gui.fx;
|
exports com.github.gtache.autosubtitle.setup.gui.fx;
|
||||||
exports com.github.gtache.autosubtitle.subtitle.gui.fx;
|
exports com.github.gtache.autosubtitle.subtitle.gui.fx;
|
||||||
|
exports com.github.gtache.autosubtitle.modules.gui.fx;
|
||||||
opens com.github.gtache.autosubtitle.gui.fx to javafx.fxml;
|
opens com.github.gtache.autosubtitle.gui.fx to javafx.fxml;
|
||||||
}
|
}
|
||||||
@@ -4,37 +4,42 @@
|
|||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
<?import org.controlsfx.control.PrefixSelectionComboBox?>
|
<?import org.controlsfx.control.PrefixSelectionComboBox?>
|
||||||
<GridPane hgap="10.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" vgap="10.0"
|
|
||||||
xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
|
<GridPane hgap="10.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" vgap="10.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.github.gtache.autosubtitle.gui.fx.FXParametersController">
|
||||||
fx:controller="com.github.gtache.autosubtitle.gui.fx.FXParametersController">
|
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
<ColumnConstraints hgrow="SOMETIMES" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
<ColumnConstraints hgrow="SOMETIMES" />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints vgrow="SOMETIMES"/>
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints vgrow="SOMETIMES"/>
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints vgrow="SOMETIMES"/>
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints vgrow="SOMETIMES"/>
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints vgrow="SOMETIMES"/>
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Label text="%parameters.extraction.model.label"/>
|
<Label text="%parameters.extraction.model.label" />
|
||||||
<PrefixSelectionComboBox fx:id="extractionModelCombobox" GridPane.columnIndex="1"/>
|
<PrefixSelectionComboBox fx:id="extractionModelCombobox" GridPane.columnIndex="1" />
|
||||||
<PrefixSelectionComboBox fx:id="extractionOutputFormat" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
<PrefixSelectionComboBox fx:id="extractionOutputFormat" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
<Label text="%parameters.subtitles.output.format" GridPane.rowIndex="1"/>
|
<Label text="%parameters.subtitles.output.format" GridPane.rowIndex="1" />
|
||||||
<Label text="%parameters.subtitles.font.size" GridPane.rowIndex="3"/>
|
<Label text="%parameters.subtitles.font.size" GridPane.rowIndex="3" />
|
||||||
<Label text="%parameters.subtitles.font.family" GridPane.rowIndex="2"/>
|
<Label text="%parameters.subtitles.font.family" GridPane.rowIndex="2" />
|
||||||
<PrefixSelectionComboBox fx:id="fontFamilyCombobox" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
<PrefixSelectionComboBox fx:id="fontFamilyCombobox" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
<TextField fx:id="fontSizeField" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
<TextField fx:id="fontSizeField" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
<Button mnemonicParsing="false" onAction="#savePressed" text="%parameters.button.save.label"
|
<Button mnemonicParsing="false" onAction="#savePressed" text="%parameters.button.save.label" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
<Button mnemonicParsing="false" onAction="#resetPressed" text="%parameters.button.reset.label" GridPane.rowIndex="6" />
|
||||||
<Button mnemonicParsing="false" onAction="#resetPressed" text="%parameters.button.reset.label"
|
<Label text="%parameters.subtitles.max.length.label" GridPane.rowIndex="4" />
|
||||||
GridPane.rowIndex="4"/>
|
<Label text="%parameters.subtitles.max.lines.label" GridPane.rowIndex="5" />
|
||||||
|
<TextField fx:id="maxLengthField" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<TextField fx:id="maxLinesField" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
</padding>
|
</padding>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.github.gtache.autosubtitle.gui.fx;
|
package com.github.gtache.autosubtitle.gui.fx;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.VideoConverter;
|
import com.github.gtache.autosubtitle.VideoConverter;
|
||||||
import com.github.gtache.autosubtitle.VideoLoader;
|
import com.github.gtache.autosubtitle.VideoLoader;
|
||||||
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
||||||
@@ -8,6 +7,7 @@ import com.github.gtache.autosubtitle.modules.gui.fx.DaggerResourceComponent;
|
|||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModelProvider;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModelProvider;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||||
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
@@ -51,7 +51,7 @@ class TestFXParametersController extends FxRobot {
|
|||||||
this.videoConverter = requireNonNull(videoConverter);
|
this.videoConverter = requireNonNull(videoConverter);
|
||||||
this.translator = requireNonNull(translator);
|
this.translator = requireNonNull(translator);
|
||||||
this.timeFormatter = requireNonNull(timeFormatter);
|
this.timeFormatter = requireNonNull(timeFormatter);
|
||||||
this.model = spy(new FXParametersModel(mock(ExtractionModelProvider.class), "Arial", 12));
|
this.model = spy(new FXParametersModel(mock(ExtractionModelProvider.class), "Arial", 12, 40, 1));
|
||||||
this.binder = requireNonNull(binder);
|
this.binder = requireNonNull(binder);
|
||||||
this.window = FxToolkit.registerPrimaryStage();
|
this.window = FxToolkit.registerPrimaryStage();
|
||||||
this.controller = spy(FXParametersController.class);
|
this.controller = spy(FXParametersController.class);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ class TestFXParametersModel {
|
|||||||
|
|
||||||
private static final String DEFAULT_FONT_FAMILY = "Arial";
|
private static final String DEFAULT_FONT_FAMILY = "Arial";
|
||||||
private static final int DEFAULT_FONT_SIZE = 12;
|
private static final int DEFAULT_FONT_SIZE = 12;
|
||||||
|
private static final int DEFAULT_MAX_LINE_LENGTH = 40;
|
||||||
|
private static final int DEFAULT_MAX_LINES = 2;
|
||||||
private final List<ExtractionModel> availableExtractionModels;
|
private final List<ExtractionModel> availableExtractionModels;
|
||||||
private final ExtractionModel defaultExtractionModel;
|
private final ExtractionModel defaultExtractionModel;
|
||||||
private final ExtractionModelProvider provider;
|
private final ExtractionModelProvider provider;
|
||||||
@@ -34,7 +36,7 @@ class TestFXParametersModel {
|
|||||||
when(provider.getDefaultExtractionModel()).thenReturn(defaultExtractionModel);
|
when(provider.getDefaultExtractionModel()).thenReturn(defaultExtractionModel);
|
||||||
this.availableExtractionModels = List.of(defaultExtractionModel, extractionModel);
|
this.availableExtractionModels = List.of(defaultExtractionModel, extractionModel);
|
||||||
when(provider.getAvailableExtractionModels()).thenReturn(availableExtractionModels);
|
when(provider.getAvailableExtractionModels()).thenReturn(availableExtractionModels);
|
||||||
model = new FXParametersModel(extractionModelProvider, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE);
|
model = new FXParametersModel(extractionModelProvider, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_MAX_LINE_LENGTH, DEFAULT_MAX_LINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.github.gtache.autosubtitle.gui.fx;
|
package com.github.gtache.autosubtitle.gui.fx;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.VideoConverter;
|
import com.github.gtache.autosubtitle.VideoConverter;
|
||||||
import com.github.gtache.autosubtitle.VideoLoader;
|
import com.github.gtache.autosubtitle.VideoLoader;
|
||||||
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.fx.DaggerResourceComponent;
|
import com.github.gtache.autosubtitle.modules.gui.fx.DaggerResourceComponent;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||||
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.github.gtache.autosubtitle.gui.fx;
|
package com.github.gtache.autosubtitle.gui.fx;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.VideoConverter;
|
import com.github.gtache.autosubtitle.VideoConverter;
|
||||||
import com.github.gtache.autosubtitle.VideoLoader;
|
import com.github.gtache.autosubtitle.VideoLoader;
|
||||||
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
import com.github.gtache.autosubtitle.gui.TimeFormatter;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.fx.DaggerResourceComponent;
|
import com.github.gtache.autosubtitle.modules.gui.fx.DaggerResourceComponent;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||||
|
import com.github.gtache.autosubtitle.translation.Translator;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class TestFXWorkBinder {
|
|||||||
@Test
|
@Test
|
||||||
void testBindings() {
|
void testBindings() {
|
||||||
final var workModel = new FXWorkModel();
|
final var workModel = new FXWorkModel();
|
||||||
final var parametersModel = new FXParametersModel(mock(ExtractionModelProvider.class), "Arial", 12);
|
final var parametersModel = new FXParametersModel(mock(ExtractionModelProvider.class), "Arial", 12, 40, 1);
|
||||||
final var binder = new FXWorkBinder(workModel, parametersModel);
|
final var binder = new FXWorkBinder(workModel, parametersModel);
|
||||||
binder.createBindings();
|
binder.createBindings();
|
||||||
assertNull(workModel.extractionModel());
|
assertNull(workModel.extractionModel());
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import javafx.scene.image.Image;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.prefs.Preferences;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -36,8 +35,4 @@ class TestFXModule {
|
|||||||
assertInstanceOf(Image.class, FXModule.providesPauseImage(IMAGE));
|
assertInstanceOf(Image.class, FXModule.providesPauseImage(IMAGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testPreferences() {
|
|
||||||
assertInstanceOf(Preferences.class, FXModule.providesPreferences());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
<artifactId>autosubtitle-gui-run</artifactId>
|
<artifactId>autosubtitle-gui-run</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||||
|
<artifactId>autosubtitle-deepl</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||||
<artifactId>autosubtitle-ffmpeg</artifactId>
|
<artifactId>autosubtitle-ffmpeg</artifactId>
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package com.github.gtache.autosubtitle.modules.gui.run;
|
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Language;
|
|
||||||
import com.github.gtache.autosubtitle.Translator;
|
|
||||||
import com.github.gtache.autosubtitle.modules.setup.impl.TranslatorSetup;
|
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module for missing components
|
|
||||||
*/
|
|
||||||
@Module
|
|
||||||
public abstract class MissingComponentsModule {
|
|
||||||
|
|
||||||
private MissingComponentsModule() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
static Translator providesTranslator() {
|
|
||||||
return new Translator<>() {
|
|
||||||
@Override
|
|
||||||
public Language getLanguage(final String text) {
|
|
||||||
return Language.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String translate(final String text, final Language to) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Subtitle translate(final Subtitle subtitle, final Language to) {
|
|
||||||
return subtitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubtitleCollection<Subtitle> translate(final SubtitleCollection<?> collection, final Language to) {
|
|
||||||
return (SubtitleCollection<Subtitle>) collection;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@TranslatorSetup
|
|
||||||
static SetupManager providesTranslatorSetupManager() {
|
|
||||||
return new NoOpSetupManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package com.github.gtache.autosubtitle.modules.gui.run;
|
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
|
||||||
import com.github.gtache.autosubtitle.setup.SetupListener;
|
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
|
||||||
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
|
||||||
|
|
||||||
class NoOpSetupManager implements SetupManager {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String name() {
|
|
||||||
return "NoOpSetupManager";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SetupStatus status() {
|
|
||||||
return SetupStatus.NOT_INSTALLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInstalled() throws SetupException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void install() throws SetupException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void uninstall() throws SetupException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reinstall() throws SetupException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUpdateAvailable() throws SetupException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update() throws SetupException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addListener(final SetupListener listener) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListener(final SetupListener listener) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListeners() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.modules.gui.run;
|
package com.github.gtache.autosubtitle.modules.gui.run;
|
||||||
|
|
||||||
|
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.gui.fx.FXModule;
|
import com.github.gtache.autosubtitle.modules.gui.fx.FXModule;
|
||||||
import com.github.gtache.autosubtitle.modules.gui.impl.GuiCoreModule;
|
import com.github.gtache.autosubtitle.modules.gui.impl.GuiCoreModule;
|
||||||
@@ -15,7 +16,7 @@ import javax.inject.Singleton;
|
|||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
@Component(modules = {CoreModule.class, GuiCoreModule.class, FXModule.class, FFmpegModule.class,
|
@Component(modules = {CoreModule.class, GuiCoreModule.class, FXModule.class, FFmpegModule.class,
|
||||||
WhisperXModule.class, MissingComponentsModule.class})
|
WhisperXModule.class, DeepLModule.class})
|
||||||
public interface RunComponent {
|
public interface RunComponent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Main module for auto-subtitle
|
* Main module for auto-subtitle
|
||||||
*/
|
*/
|
||||||
module com.github.gtache.autosubtitle.run {
|
module com.github.gtache.autosubtitle.gui.run {
|
||||||
|
requires com.github.gtache.autosubtitle.deepl;
|
||||||
requires com.github.gtache.autosubtitle.ffmpeg;
|
requires com.github.gtache.autosubtitle.ffmpeg;
|
||||||
requires com.github.gtache.autosubtitle.gui.fx;
|
requires com.github.gtache.autosubtitle.gui.fx;
|
||||||
requires com.github.gtache.autosubtitle.whisperx;
|
requires com.github.gtache.autosubtitle.whisperx;
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
|||||||
try {
|
try {
|
||||||
final var json = gson.fromJson(content, JSONSubtitles.class);
|
final var json = gson.fromJson(content, JSONSubtitles.class);
|
||||||
final var subtitles = json.segments().stream().map(s -> {
|
final var subtitles = json.segments().stream().map(s -> {
|
||||||
final var start = (long) s.start() * 1000L;
|
final var start = (long) (s.start() * 1000L);
|
||||||
final var end = (long) s.end() * 1000L;
|
final var end = (long) (s.end() * 1000L);
|
||||||
return new SubtitleImpl(s.text(), start, end, null, null);
|
return new SubtitleImpl(s.text(), start, end, null, null);
|
||||||
}).sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
}).sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
||||||
final var language = Language.getLanguage(json.language());
|
final var language = Language.getLanguage(json.language());
|
||||||
|
|||||||
@@ -55,10 +55,8 @@ public class WhisperXSubtitleExtractor extends AbstractWhisperSubtitleExtractor
|
|||||||
args.add("True");
|
args.add("True");
|
||||||
args.add("--compute_type");
|
args.add("--compute_type");
|
||||||
args.add("int8");
|
args.add("int8");
|
||||||
args.add("--max_line_count");
|
args.add("--threads");
|
||||||
args.add("2");
|
args.add(String.valueOf(Runtime.getRuntime().availableProcessors()));
|
||||||
args.add("--max_line_width");
|
|
||||||
args.add("30");
|
|
||||||
if (language != Language.AUTO) {
|
if (language != Language.AUTO) {
|
||||||
args.add("--language");
|
args.add("--language");
|
||||||
args.add(language.iso2());
|
args.add(language.iso2());
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.github.gtache.autosubtitle.subtitle.parser.json.whisperx;
|
package com.github.gtache.autosubtitle.subtitle.parser.json.whisperx;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
|
import com.github.gtache.autosubtitle.modules.impl.MaxLineLength;
|
||||||
|
import com.github.gtache.autosubtitle.modules.impl.MaxLines;
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
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;
|
||||||
@@ -12,10 +14,14 @@ import com.google.gson.Gson;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link SubtitleConverter} implementation for JSON files
|
* {@link SubtitleConverter} implementation for JSON files
|
||||||
@@ -23,11 +29,21 @@ import java.util.stream.Collectors;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||||
|
|
||||||
|
private static final String MAX_LINE_LENGTH = "maxLineLength";
|
||||||
|
private static final String MAX_LINES = "maxLines";
|
||||||
|
private static final Pattern SPLIT_PATTERN = Pattern.compile("[ \n]+");
|
||||||
|
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
|
private final Preferences preferences;
|
||||||
|
private final int defaultMaxLineLength;
|
||||||
|
private final int defaultMaxLines;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JSONSubtitleConverter(final Gson gson) {
|
JSONSubtitleConverter(final Gson gson, final Preferences preferences, @MaxLineLength final int defaultMaxLineLength, @MaxLines final int defaultMaxLines) {
|
||||||
this.gson = Objects.requireNonNull(gson);
|
this.gson = Objects.requireNonNull(gson);
|
||||||
|
this.preferences = Objects.requireNonNull(preferences);
|
||||||
|
this.defaultMaxLineLength = defaultMaxLineLength;
|
||||||
|
this.defaultMaxLines = defaultMaxLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -42,10 +58,15 @@ public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
|||||||
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
|
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
|
||||||
try {
|
try {
|
||||||
final var json = gson.fromJson(content, JSONSubtitles.class);
|
final var json = gson.fromJson(content, JSONSubtitles.class);
|
||||||
final var subtitles = json.segments().stream().map(s -> {
|
final var subtitles = json.segments().stream().flatMap(s -> {
|
||||||
final var start = (long) s.start() * 1000L;
|
final var start = (long) (s.start() * 1000L);
|
||||||
final var end = (long) s.end() * 1000L;
|
final var end = (long) (s.end() * 1000L);
|
||||||
return new SubtitleImpl(s.text(), start, end, null, null);
|
if (s.words().isEmpty()) {
|
||||||
|
return Stream.of(new SubtitleImpl(s.text(), start, end, null, null));
|
||||||
|
} else {
|
||||||
|
return splitSubtitle(s);
|
||||||
|
|
||||||
|
}
|
||||||
}).sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
}).sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
||||||
final var language = Language.getLanguage(json.language());
|
final var language = Language.getLanguage(json.language());
|
||||||
final var subtitlesText = subtitles.stream().map(Subtitle::content).collect(Collectors.joining(""));
|
final var subtitlesText = subtitles.stream().map(Subtitle::content).collect(Collectors.joining(""));
|
||||||
@@ -55,6 +76,79 @@ public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Stream<SubtitleImpl> splitSubtitle(final JSONSubtitleSegment segment) {
|
||||||
|
final var maxLineLength = preferences.getInt(MAX_LINE_LENGTH, defaultMaxLineLength);
|
||||||
|
final var maxLines = preferences.getInt(MAX_LINES, defaultMaxLines);
|
||||||
|
final var text = segment.text();
|
||||||
|
if (text.length() <= maxLineLength) {
|
||||||
|
final var start = (long) (segment.start() * 1000L);
|
||||||
|
final var end = (long) (segment.end() * 1000L);
|
||||||
|
return Stream.of(new SubtitleImpl(text.replace("\n", " "), start, end, null, null));
|
||||||
|
} else if (text.length() <= maxLines * maxLineLength) {
|
||||||
|
return splitSubtitleLines(segment);
|
||||||
|
} else {
|
||||||
|
return splitSubtitleWords(segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<SubtitleImpl> splitSubtitleLines(final JSONSubtitleSegment segment) {
|
||||||
|
final var maxLineLength = preferences.getInt(MAX_LINE_LENGTH, defaultMaxLineLength);
|
||||||
|
final var text = segment.text();
|
||||||
|
final var split = SPLIT_PATTERN.split(text);
|
||||||
|
final var builder = new StringBuilder(text.length());
|
||||||
|
for (final var s : split) {
|
||||||
|
final var newLength = builder.length() + s.length();
|
||||||
|
if (areDifferentLines(builder.length(), newLength, maxLineLength)) {
|
||||||
|
builder.append("\n").append(s);
|
||||||
|
} else {
|
||||||
|
builder.append(" ").append(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final var start = (long) (segment.start() * 1000L);
|
||||||
|
final var end = (long) (segment.end() * 1000L);
|
||||||
|
return Stream.of(new SubtitleImpl(builder.toString(), start, end, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean areDifferentLines(final int currentLength, final int newLength, final int maxLength) {
|
||||||
|
return currentLength / (maxLength + 1) < newLength / (maxLength + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<SubtitleImpl> splitSubtitleWords(final JSONSubtitleSegment segment) {
|
||||||
|
final var maxLineLength = preferences.getInt(MAX_LINE_LENGTH, defaultMaxLineLength);
|
||||||
|
final var maxLines = preferences.getInt(MAX_LINES, defaultMaxLines);
|
||||||
|
final var ret = new ArrayList<SubtitleImpl>(segment.text().length() / (maxLines * maxLineLength));
|
||||||
|
final var builder = new StringBuilder(maxLines * maxLineLength);
|
||||||
|
final var words = segment.words();
|
||||||
|
var currentStart = -1L;
|
||||||
|
var currentEnd = -1L;
|
||||||
|
for (final var word : words) {
|
||||||
|
final var text = word.word();
|
||||||
|
final var start = (long) (word.start() * 1000L);
|
||||||
|
final var end = (long) (word.end() * 1000L);
|
||||||
|
if (currentStart < 0) {
|
||||||
|
currentStart = start;
|
||||||
|
}
|
||||||
|
final var newLength = builder.length() + text.length();
|
||||||
|
if (newLength > maxLineLength * maxLines) {
|
||||||
|
final var newSubtitle = new SubtitleImpl(builder.toString(), currentStart, currentEnd, null, null);
|
||||||
|
ret.add(newSubtitle);
|
||||||
|
builder.delete(0, builder.length());
|
||||||
|
builder.append(text);
|
||||||
|
currentStart = start == 0 ? currentEnd : start;
|
||||||
|
} else if (areDifferentLines(builder.length(), newLength, maxLineLength)) {
|
||||||
|
builder.append("\n").append(text);
|
||||||
|
} else {
|
||||||
|
builder.append(" ").append(text);
|
||||||
|
}
|
||||||
|
currentEnd = end == 0 ? currentEnd : end;
|
||||||
|
}
|
||||||
|
if (!builder.isEmpty()) {
|
||||||
|
final var newSubtitle = new SubtitleImpl(builder.toString(), currentStart, currentEnd, null, null);
|
||||||
|
ret.add(newSubtitle);
|
||||||
|
}
|
||||||
|
return ret.stream();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canParse(final Path file) {
|
public boolean canParse(final Path file) {
|
||||||
return file.getFileName().toString().endsWith(".json");
|
return file.getFileName().toString().endsWith(".json");
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
module com.github.gtache.autosubtitle.whisperx {
|
module com.github.gtache.autosubtitle.whisperx {
|
||||||
requires transitive com.github.gtache.autosubtitle.whisper.common;
|
requires transitive com.github.gtache.autosubtitle.whisper.common;
|
||||||
requires org.apache.logging.log4j;
|
requires org.apache.logging.log4j;
|
||||||
|
requires java.prefs;
|
||||||
|
|
||||||
exports com.github.gtache.autosubtitle.whisperx;
|
exports com.github.gtache.autosubtitle.whisperx;
|
||||||
exports com.github.gtache.autosubtitle.setup.whisperx;
|
exports com.github.gtache.autosubtitle.setup.whisperx;
|
||||||
|
|||||||
Reference in New Issue
Block a user