Allows choosing and managing each tool

This commit is contained in:
Guillaume Tâche
2024-10-03 21:55:14 +02:00
parent 0a2f9e0c31
commit df58cf4585
117 changed files with 1547 additions and 1515 deletions

View File

@@ -1,69 +1,39 @@
package com.github.gtache.autosubtitle.gui.setup;
import com.github.gtache.autosubtitle.ToolType;
/**
* Controller for the setup view
*/
public interface SetupController {
/**
* Installs the video converter
* Installs the tool given by the tool type
*
* @param type The tool type
*/
void installVideoConverter();
void install(ToolType type);
/**
* Uninstalls the video converter
* Uninstalls the tool given by the tool type
*
* @param type The tool type
*/
void uninstallVideoConverter();
void uninstall(ToolType type);
/**
* Updates the video converter
* Updates the tool given by the tool type
*
* @param type The tool type
*/
void updateVideoConverter();
void update(ToolType type);
/**
* Reinstalls the video converter
* Reinstalls the tool given by the tool type
*
* @param type The tool type
*/
void reinstallVideoConverter();
/**
* Installs the subtitle extractor
*/
void installSubtitleExtractor();
/**
* Uninstalls the subtitle extractor
*/
void uninstallSubtitleExtractor();
/**
* Updates the subtitle extractor
*/
void updateSubtitleExtractor();
/**
* Reinstalls the subtitle extractor
*/
void reinstallSubtitleExtractor();
/**
* Installs the translator
*/
void installTranslator();
/**
* Uninstalls the translator
*/
void uninstallTranslator();
/**
* Updates the translator
*/
void updateTranslator();
/**
* Reinstalls the translator
*/
void reinstallTranslator();
void reinstall(ToolType type);
/**
* @return the model

View File

@@ -1,117 +1,101 @@
package com.github.gtache.autosubtitle.gui.setup;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.setup.SetupManager;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import java.util.List;
/**
* Model for the setup view
*/
public interface SetupModel {
/**
* @return the status of the subtitle extractor
* Returns the list of available setup managers for the given tool type
*
* @param type The tool type
* @return The list of managers
*/
SetupStatus subtitleExtractorStatus();
List<SetupManager> availableSetupManagers(ToolType type);
/**
* Sets the status of the subtitle extractor
* Returns the selected setup manager for the given tool type
*
* @param type The tool type
* @return The manager
*/
SetupManager selectedSetupManager(ToolType type);
/**
* Sets the selected setup manager for the given tool type
*
* @param type The tool type
* @param manager the new subtitle extractor
*/
void setSelectedSetupManager(ToolType type, SetupManager manager);
/**
* Returns the selected tool for the given tool type
*
* @param type The tool type
* @return the selected tool
*/
String selectedTool(ToolType type);
/**
* Returns the status of the setup manager for the given tool type
*
* @param type The tool type
* @return the status of the setup manager
*/
SetupStatus managerStatus(ToolType type);
/**
* Sets the status of the setup manager for the given tool type
*
* @param type The tool type
* @param status the new status
*/
void setSubtitleExtractorStatus(SetupStatus status);
void setManagerStatus(ToolType type, SetupStatus status);
/**
* @return the progress of the subtitle extractor setup
*/
double subtitleExtractorSetupProgress();
/**
* Sets the progress of the subtitle extractor setup
* Returns the progress of the tool setup for the given tool type
*
* @param type The tool type
* @return the progress of the tool setup
*/
double setupProgress(ToolType type);
/**
* Sets the progress of the tool setup for the given tool type
*
* @param type The tool type
* @param progress the new progress
*/
void setSubtitleExtractorSetupProgress(double progress);
void setSetupProgress(ToolType type, double progress);
/**
* @return the text of the subtitle extractor setup progress
*/
String subtitleExtractorSetupProgressLabel();
/**
* Sets the text of the subtitle extractor setup progress
* Returns the text of the setup progress for the given tool type
*
* @param type The tool type
* @return the text of the setup progress label
*/
String setupProgressLabel(ToolType type);
/**
* Sets the text of the setup progress for the given tool type
*
* @param type The tool type
* @param label the new text
*/
void setSubtitleExtractorSetupProgressLabel(String label);
void setSetupProgressLabel(ToolType type, String label);
/**
* @return the status of the video converter
*/
SetupStatus videoConverterStatus();
/**
* Sets the status of the video converter
* Returns true if the setup is editable
*
* @param status the new status
* @param type The tool type
* @return true if the setup is editable
*/
void setVideoConverterStatus(SetupStatus status);
/**
* @return the progress of the video converter setup
*/
double videoConverterSetupProgress();
/**
* Sets the progress of the video converter setup
*
* @param progress the new progress
*/
void setVideoConverterSetupProgress(double progress);
/**
* @return the text of the video converter setup progress
*/
String videoConverterSetupProgressLabel();
/**
* Sets the text of the video converter setup progress
*
* @param label the new text
*/
void setVideoConverterSetupProgressLabel(String label);
/**
* @return the status of the translator
*/
SetupStatus translatorStatus();
/**
* Sets the status of the translator
*
* @param status the new status
*/
void setTranslatorStatus(SetupStatus status);
/**
* @return the progress of the translator setup
*/
double translatorSetupProgress();
/**
* Sets the progress of the translator setup
*
* @param progress the new progress
*/
void setTranslatorSetupProgress(double progress);
/**
* @return the text of the translator setup progress
*/
String translatorSetupProgressLabel();
/**
* Sets the text of the translator setup progress
*
* @param label the new text
*/
void setTranslatorSetupProgressLabel(String label);
boolean isSetupInProgress(ToolType type);
}

View File

@@ -26,7 +26,10 @@ setup.status.installed.label=is installed.
setup.status.not_installed.label=is not installed.
setup.status.system_installed.label=is installed (system).
setup.status.update_available.label=has an available update.
setup.subtitle_extractor.choice.label=Subtitle extractor
setup.subtitle_translator.choice.label=Subtitle translator
setup.uninstall.error.label=An error occurred while uninstalling : {0}
setup.uninstall.error.title=Error uninstalling
setup.update.error.label=An error occurred while updating : {0}
setup.update.error.title=Error updating
setup.update.error.title=Error updating
setup.video_converter.choice.label=Video Converter

View File

@@ -1,3 +1,4 @@
setup.converter.choice.label=Convertisseur vid\u00E9o
setup.description.label=Statut des outils utilis\u00E9s par l'application
setup.event.check.end.label=Contr\u00F4le de {0} termin\u00E9
setup.event.check.start.label=Contr\u00F4le de {0} en cours
@@ -11,6 +12,7 @@ setup.event.uninstall.end.label=D\u00E9sinstallation de {0} termin\u00E9e
setup.event.uninstall.start.label=D\u00E9sinstallation de {0} en cours
setup.event.update.end.label=Mise \u00E0 jour de {0} termin\u00E9e
setup.event.update.start.label=Mise \u00E0 jour de {0} en cours
setup.extractor.choice.label=Extracteur de sous-titres
setup.install.error.label=Une erreur s''est produite lors de l''installation: {0}
setup.install.error.title=Erreur d'installation
setup.menu.install.label=Installer
@@ -26,7 +28,11 @@ setup.status.installed.label=est install\u00E9.
setup.status.not_installed.label=n'est pas install\u00E9.
setup.status.system_installed.label=est install\u00E9 (syst\u00E8me).
setup.status.update_available.label=a une mise \u00E0 jour disponible.
setup.subtitle_extractor.choice.label=Extracteur de sous-titres
setup.subtitle_translator.choice.label=Traducteur de sous-titres
setup.translator.choice.label=Traducteur de sous-titres
setup.uninstall.error.label=Une erreur s''est produite lors de la d\u00E9sinstallation : {0}
setup.uninstall.error.title=Erreur de d\u00E9sinstallation
setup.update.error.label=Une erreur s''est produite lors de la mise \u00E0 jour : {0}
setup.update.error.title=Erreur de mise \u00E0 jour
setup.update.error.title=Erreur de mise \u00E0 jour
setup.video_converter.choice.label=Convertisseur de vid\u00E9o

View File

@@ -13,7 +13,7 @@
<properties>
<controlsfx.version>11.2.1</controlsfx.version>
<javafx.version>22.0.2</javafx.version>
<javafx.version>23</javafx.version>
<testfx.version>4.0.18</testfx.version>
</properties>

View File

@@ -60,7 +60,6 @@ public class FXMediaController implements MediaController {
private Label volumeValueLabel;
private final FXMediaModel model;
private final FXMediaBinder binder;
private final TimeFormatter timeFormatter;
private final Image playImage;
private final Image pauseImage;
@@ -71,10 +70,10 @@ public class FXMediaController implements MediaController {
FXMediaController(final FXMediaModel model, final FXMediaBinder binder, final TimeFormatter timeFormatter,
@Play final Image playImage, @Pause final Image pauseImage) {
this.model = requireNonNull(model);
this.binder = requireNonNull(binder);
this.timeFormatter = requireNonNull(timeFormatter);
this.playImage = requireNonNull(playImage);
this.pauseImage = requireNonNull(pauseImage);
binder.createBindings();
}
@FXML
@@ -116,7 +115,6 @@ public class FXMediaController implements MediaController {
stackPane.widthProperty().addListener((observable, oldValue, newValue) -> resizeMediaView());
stackPane.heightProperty().addListener((observable, oldValue, newValue) -> resizeMediaView());
binder.createBindings();
}
private void resizeMediaView() {

View File

@@ -0,0 +1,35 @@
package com.github.gtache.autosubtitle.gui.parameters.fx;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.gui.fx.FXBinder;
import com.github.gtache.autosubtitle.gui.setup.fx.FXSetupModel;
import javax.inject.Inject;
import javax.inject.Singleton;
import static java.util.Objects.requireNonNull;
/**
* Binds the subtitles model
*/
@Singleton
public class FXParametersBinder implements FXBinder {
private final FXParametersModel parametersModel;
private final FXSetupModel setupModel;
@Inject
FXParametersBinder(final FXParametersModel parametersModel, final FXSetupModel setupModel) {
this.parametersModel = requireNonNull(parametersModel);
this.setupModel = requireNonNull(setupModel);
}
@Override
public void createBindings() {
setupModel.selectedToolProperty(ToolType.SUBTITLE_EXTRACTOR).addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
parametersModel.extractionModelProviderProperty().set(parametersModel.availableExtractionModelProviders().get(newValue));
}
});
}
}

View File

@@ -4,7 +4,7 @@ import com.github.gtache.autosubtitle.gui.fx.AbstractFXController;
import com.github.gtache.autosubtitle.gui.parameters.ParametersController;
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModelProvider;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
@@ -47,13 +47,12 @@ public class FXParametersController extends AbstractFXController implements Para
private final FXParametersModel model;
private final Preferences preferences;
private final ExtractionModelProvider extractionModelProvider;
@Inject
FXParametersController(final FXParametersModel model, final Preferences preferences, final ExtractionModelProvider extractionModelProvider) {
FXParametersController(final FXParametersModel model, final FXParametersBinder binder, final Preferences preferences) {
this.model = requireNonNull(model);
this.preferences = requireNonNull(preferences);
this.extractionModelProvider = requireNonNull(extractionModelProvider);
binder.createBindings();
}
@FXML
@@ -83,17 +82,31 @@ public class FXParametersController extends AbstractFXController implements Para
maxLinesField.textProperty().bindBidirectional(model.maxLinesProperty(), new NumberStringConverter());
loadPreferences();
model.extractionModelProviderProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
final var name = newValue.getClass().getSimpleName();
Platform.runLater(() -> {
final var modelName = preferences.get(name + ".extractionModel", model.extractionModel().name());
final var loadedModel = newValue.getExtractionModel(modelName);
model.setExtractionModel(loadedModel);
});
}
});
}
private void loadPreferences() {
final var extractionModel = preferences.get("extractionModel", model.extractionModel().name());
final var outputFormat = preferences.get("outputFormat", model.outputFormat().name());
final var fontFamily = preferences.get("fontName", model.fontName());
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));
final var extractionModelProvider = model.extractionModelProviderProperty().get();
final var name = extractionModelProvider.getClass().getSimpleName();
final var modelName = preferences.get(name + ".extractionModel", model.extractionModel().name());
model.setExtractionModel(extractionModelProvider.getExtractionModel(modelName));
model.setOutputFormat(OutputFormat.valueOf(outputFormat));
model.setFontName(fontFamily);
model.setFontSize(fontSize);
@@ -106,7 +119,7 @@ public class FXParametersController extends AbstractFXController implements Para
@Override
public void save() {
logger.info("Saving preferences");
preferences.put("extractionModel", model.extractionModel().name());
preferences.put(model.extractionModelProviderProperty().get().getClass().getSimpleName() + ".extractionModel", model.extractionModel().name());
preferences.put("outputFormat", model.outputFormat().name());
preferences.put("fontName", model.fontName());
preferences.putInt("fontSize", model.fontSize());

View File

@@ -21,9 +21,11 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
/**
* FX implementation of {@link ParametersModel}
@@ -31,6 +33,8 @@ import javax.inject.Singleton;
@Singleton
public class FXParametersModel implements ParametersModel {
private final ObservableMap<String, ExtractionModelProvider> availableExtractionModelProviders;
private final ObjectProperty<ExtractionModelProvider> extractionModelProvider;
private final ObservableList<ExtractionModel> availableExtractionModels;
private final ObjectProperty<ExtractionModel> extractionModel;
private final ObservableList<OutputFormat> availableOutputFormats;
@@ -43,10 +47,12 @@ public class FXParametersModel implements ParametersModel {
private final IntegerProperty maxLines;
@Inject
FXParametersModel(final ExtractionModelProvider extractionModelProvider, @FontName final String defaultFontFamily,
FXParametersModel(final Map<String, ExtractionModelProvider> extractionModelProviders, final ExtractionModelProvider defaultProvider, @FontName final String defaultFontFamily,
@FontSize final int defaultFontSize, @MaxLineLength final int defaultMaxLineLength, @MaxLines final int defaultMaxLines) {
this.availableExtractionModels = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(extractionModelProvider.getAvailableExtractionModels()));
this.extractionModel = new SimpleObjectProperty<>(extractionModelProvider.getDefaultExtractionModel());
this.availableExtractionModelProviders = FXCollections.unmodifiableObservableMap(FXCollections.observableMap(extractionModelProviders));
this.extractionModelProvider = new SimpleObjectProperty<>(defaultProvider);
this.availableExtractionModels = FXCollections.observableArrayList(defaultProvider.getAvailableExtractionModels());
this.extractionModel = new SimpleObjectProperty<>(defaultProvider.getDefaultExtractionModel());
this.availableOutputFormats = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(OutputFormat.SRT));
this.outputFormat = new SimpleObjectProperty<>(OutputFormat.SRT);
this.availableFontFamilies = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList("Arial"));
@@ -57,6 +63,12 @@ public class FXParametersModel implements ParametersModel {
this.maxLines = new SimpleIntegerProperty(defaultMaxLines);
font.bind(Bindings.createObjectBinding(() -> new FontImpl(fontName(), fontSize()), fontName, fontSize));
extractionModelProvider.addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
availableExtractionModels.setAll(newValue.getAvailableExtractionModels());
extractionModel.set(newValue.getDefaultExtractionModel());
}
});
}
@Override
@@ -166,4 +178,12 @@ public class FXParametersModel implements ParametersModel {
public IntegerProperty maxLinesProperty() {
return maxLines;
}
ObservableMap<String, ExtractionModelProvider> availableExtractionModelProviders() {
return availableExtractionModelProviders;
}
ObjectProperty<ExtractionModelProvider> extractionModelProviderProperty() {
return extractionModelProvider;
}
}

View File

@@ -1,10 +1,8 @@
package com.github.gtache.autosubtitle.gui.setup.fx;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.gui.fx.AbstractFXController;
import com.github.gtache.autosubtitle.gui.setup.SetupController;
import com.github.gtache.autosubtitle.modules.setup.impl.SubtitleExtractorSetup;
import com.github.gtache.autosubtitle.modules.setup.impl.TranslatorSetup;
import com.github.gtache.autosubtitle.modules.setup.impl.VideoConverterSetup;
import com.github.gtache.autosubtitle.setup.SetupEvent;
import com.github.gtache.autosubtitle.setup.SetupException;
import com.github.gtache.autosubtitle.setup.SetupListener;
@@ -12,26 +10,29 @@ import com.github.gtache.autosubtitle.setup.SetupManager;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Window;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.controlsfx.control.PrefixSelectionComboBox;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import static java.util.Objects.requireNonNull;
/**
* FX implementation of {@link SetupController}
@@ -41,233 +42,81 @@ public class FXSetupController extends AbstractFXController implements SetupCont
private static final Logger logger = LogManager.getLogger(FXSetupController.class);
@FXML
private GridPane setupGrid;
@FXML
private ResourceBundle resources;
@FXML
private Label converterNameLabel;
@FXML
private Label converterStatusLabel;
@FXML
private Label extractorNameLabel;
@FXML
private Label extractorStatusLabel;
@FXML
private Label translatorNameLabel;
@FXML
private Label translatorStatusLabel;
@FXML
private MenuButton converterButton;
@FXML
private MenuButton extractorButton;
@FXML
private MenuButton translatorButton;
@FXML
private ProgressBar converterProgress;
@FXML
private ProgressBar extractorProgress;
@FXML
private ProgressBar translatorProgress;
@FXML
private Label converterProgressLabel;
@FXML
private Label extractorProgressLabel;
@FXML
private Label translatorProgressLabel;
private final FXSetupModel model;
private final SetupManager converterManager;
private final SetupManager extractorManager;
private final SetupManager translatorManager;
private final Map<SetupManager, ObjectProperty<SetupStatus>> statusMap;
private final Map<SetupManager, StringProperty> setupProgressMessageMap;
private final Map<SetupManager, DoubleProperty> setupProgressMap;
private final Preferences preferences;
@Inject
FXSetupController(final FXSetupModel model,
@VideoConverterSetup final SetupManager converterManager,
@SubtitleExtractorSetup final SetupManager extractorManager,
@TranslatorSetup final SetupManager translatorManager) {
this.model = Objects.requireNonNull(model);
this.converterManager = Objects.requireNonNull(converterManager);
this.extractorManager = Objects.requireNonNull(extractorManager);
this.translatorManager = Objects.requireNonNull(translatorManager);
statusMap = HashMap.newHashMap(3);
setupProgressMessageMap = HashMap.newHashMap(3);
setupProgressMap = HashMap.newHashMap(3);
FXSetupController(final FXSetupModel model, final Preferences preferences) {
this.model = requireNonNull(model);
this.preferences = requireNonNull(preferences);
}
@FXML
void initialize() {
statusMap.put(converterManager, model.videoConverterStatusProperty());
statusMap.put(extractorManager, model.subtitleExtractorStatusProperty());
statusMap.put(translatorManager, model.translatorStatusProperty());
setupProgressMessageMap.put(converterManager, model.videoConverterSetupProgressLabelProperty());
setupProgressMessageMap.put(extractorManager, model.subtitleExtractorSetupProgressLabelProperty());
setupProgressMessageMap.put(translatorManager, model.translatorSetupProgressLabelProperty());
setupProgressMap.put(converterManager, model.videoConverterSetupProgressProperty());
setupProgressMap.put(extractorManager, model.subtitleExtractorSetupProgressProperty());
setupProgressMap.put(translatorManager, model.translatorSetupProgressProperty());
bindMenu(converterButton, converterManager);
bindMenu(extractorButton, extractorManager);
bindMenu(translatorButton, translatorManager);
model.setSubtitleExtractorStatus(extractorManager.status());
model.setVideoConverterStatus(converterManager.status());
model.setTranslatorStatus(translatorManager.status());
converterNameLabel.setText(converterManager.name());
extractorNameLabel.setText(extractorManager.name());
translatorNameLabel.setText(translatorManager.name());
bindLabelStatus(converterStatusLabel, model.videoConverterStatusProperty());
bindLabelStatus(extractorStatusLabel, model.subtitleExtractorStatusProperty());
bindLabelStatus(translatorStatusLabel, model.translatorStatusProperty());
converterProgress.progressProperty().bindBidirectional(model.videoConverterSetupProgressProperty());
extractorProgress.progressProperty().bindBidirectional(model.subtitleExtractorSetupProgressProperty());
translatorProgress.progressProperty().bindBidirectional(model.translatorSetupProgressProperty());
converterProgress.visibleProperty().bind(model.videoConverterSetupProgressProperty().greaterThan(-2));
extractorProgress.visibleProperty().bind(model.subtitleExtractorSetupProgressProperty().greaterThan(-2));
translatorProgress.visibleProperty().bind(model.translatorSetupProgressProperty().greaterThan(-2));
converterProgressLabel.textProperty().bind(model.videoConverterSetupProgressLabelProperty());
extractorProgressLabel.textProperty().bind(model.subtitleExtractorSetupProgressLabelProperty());
translatorProgressLabel.textProperty().bind(model.translatorSetupProgressLabelProperty());
converterProgressLabel.visibleProperty().bind(converterProgress.visibleProperty());
extractorProgressLabel.visibleProperty().bind(extractorProgress.visibleProperty());
translatorProgressLabel.visibleProperty().bind(translatorProgress.visibleProperty());
createGridRows();
Arrays.stream(ToolType.values()).forEach(t -> model.selectedToolProperty(t)
.addListener((observable, oldValue, newValue) -> saveSelectedTool(t, newValue)));
loadPreferences();
}
private void bindMenu(final MenuButton button, final SetupManager setupManager) {
button.disableProperty().bind(Bindings.isEmpty(button.getItems()));
statusMap.get(setupManager).addListener((observable, oldValue, newValue) -> {
button.getItems().clear();
switch (newValue) {
case NOT_INSTALLED -> {
final var installItem = new MenuItem(resources.getString("setup.menu.install.label"));
installItem.setOnAction(e -> tryInstall(setupManager));
button.getItems().add(installItem);
}
case BUNDLE_INSTALLED -> {
final var reinstallItem = new MenuItem(resources.getString("setup.menu.reinstall.label"));
reinstallItem.setOnAction(e -> tryReinstall(setupManager));
final var uninstallItem = new MenuItem(resources.getString("setup.menu.uninstall.label"));
uninstallItem.setOnAction(e -> tryUninstall(setupManager));
button.getItems().addAll(reinstallItem, uninstallItem);
}
case UPDATE_AVAILABLE -> {
final var updateItem = new MenuItem(resources.getString("setup.menu.update.label"));
updateItem.setOnAction(e -> tryUpdate(setupManager));
final var reinstallItem = new MenuItem(resources.getString("setup.menu.reinstall.label"));
reinstallItem.setOnAction(e -> tryReinstall(setupManager));
final var uninstallItem = new MenuItem(resources.getString("setup.menu.uninstall.label"));
uninstallItem.setOnAction(e -> tryUninstall(setupManager));
button.getItems().addAll(updateItem, reinstallItem, uninstallItem);
}
case null, default -> {
// Do nothing, buttons are cleared
}
private void saveSelectedTool(final ToolType type, final String value) {
if (value != null) {
preferences.put(type.name(), value);
try {
preferences.flush();
} catch (final BackingStoreException e) {
logger.error("Error saving preferences", e);
}
});
}
}
private void bindLabelStatus(final Label label, final ObjectProperty<SetupStatus> status) {
label.textProperty().bind(Bindings.createStringBinding(() -> resources.getString("setup.status." + status.get().name().toLowerCase() + ".label"), status));
private void loadPreferences() {
for (final var type : ToolType.values()) {
final var value = preferences.get(type.name(), model.selectedTool(type));
model.selectedToolProperty(type).set(value);
}
}
@Override
public void installVideoConverter() {
tryInstall(converterManager);
public void install(final ToolType type) {
trySetup(type, SetupManager::install, "install");
}
@Override
public void uninstallVideoConverter() {
tryUninstall(converterManager);
public void uninstall(final ToolType type) {
trySetup(type, SetupManager::uninstall, "uninstall");
}
@Override
public void updateVideoConverter() {
tryUpdate(converterManager);
public void update(final ToolType type) {
trySetup(type, SetupManager::update, "update");
}
@Override
public void reinstallVideoConverter() {
tryReinstall(converterManager);
public void reinstall(final ToolType type) {
trySetup(type, SetupManager::reinstall, "reinstall");
}
@Override
public void installSubtitleExtractor() {
tryInstall(extractorManager);
}
@Override
public void uninstallSubtitleExtractor() {
tryUninstall(extractorManager);
}
@Override
public void updateSubtitleExtractor() {
tryUpdate(extractorManager);
}
@Override
public void reinstallSubtitleExtractor() {
tryReinstall(extractorManager);
}
@Override
public void installTranslator() {
tryInstall(translatorManager);
}
@Override
public void uninstallTranslator() {
tryUninstall(translatorManager);
}
@Override
public void updateTranslator() {
tryUpdate(translatorManager);
}
@Override
public void reinstallTranslator() {
tryReinstall(translatorManager);
}
private void tryInstall(final SetupManager manager) {
trySetup(manager, SetupManager::install, "install");
}
private void tryUninstall(final SetupManager manager) {
trySetup(manager, SetupManager::uninstall, "uninstall");
}
private void tryReinstall(final SetupManager manager) {
trySetup(manager, SetupManager::reinstall, "reinstall");
}
private void tryUpdate(final SetupManager manager) {
trySetup(manager, SetupManager::update, "update");
}
private void trySetup(final SetupManager manager, final SetupConsumer consumer, final String operation) {
private void trySetup(final ToolType type, final SetupConsumer consumer, final String operation) {
final var manager = model.selectedSetupManager(type);
manager.addListener(this);
CompletableFuture.runAsync(() -> {
try {
consumer.accept(manager);
Platform.runLater(() -> {
statusMap.get(manager).set(manager.status());
setupProgressMap.get(manager).set(-2);
model.setManagerStatus(type, manager.status());
model.setSetupProgress(type, -2);
});
} catch (final SetupException e) {
logger.error("Error {}ing {}", operation, manager.name(), e);
Platform.runLater(() -> {
statusMap.get(manager).set(SetupStatus.ERRORED);
setupProgressMap.get(manager).set(-2);
model.setManagerStatus(type, SetupStatus.ERRORED);
model.setSetupProgress(type, -2);
showErrorDialog(resources.getString("setup." + operation + ".error.title"),
MessageFormat.format(resources.getString("setup." + operation + ".error.label"), e.getMessage()));
});
@@ -300,15 +149,105 @@ public class FXSetupController extends AbstractFXController implements SetupCont
private void onAction(final SetupEvent event, final String display) {
final var manager = event.setupManager();
final var property = setupProgressMessageMap.get(manager);
final var type = manager.type();
final var progress = event.progress();
final var progressProperty = setupProgressMap.get(manager);
Platform.runLater(() -> {
property.set(display);
progressProperty.set(progress);
model.setSetupProgressLabel(type, display);
model.setSetupProgress(type, progress);
});
}
private void createGridRows() {
for (final var value : ToolType.values()) {
createGridRow(value);
}
}
private void createGridRow(final ToolType type) {
if (!model.availableSetupManagers(type).isEmpty()) {
createSelectionRow(type);
createProgressRow(type);
}
}
private void createSelectionRow(final ToolType type) {
final var label = new Label(resources.getString("setup." + type.name().toLowerCase() + ".choice.label"));
final var comboBox = new PrefixSelectionComboBox<SetupManager>();
comboBox.setItems(model.availableSetupManagers(type));
comboBox.setConverter(new SetupManagerStringConverter());
comboBox.valueProperty().bindBidirectional(model.selectedSetupManagerProperty(type));
comboBox.disableProperty().bind(model.setupInProgressProperty(type));
final var rowIndex = setupGrid.getRowCount() - 1;
final var constraints = new RowConstraints();
constraints.setFillHeight(true);
constraints.setVgrow(Priority.SOMETIMES);
setupGrid.getRowConstraints().add(rowIndex, constraints);
setupGrid.add(label, 0, rowIndex);
setupGrid.add(comboBox, 1, rowIndex);
}
private void createProgressRow(final ToolType type) {
final var statusLabel = new Label();
final var installationButton = new MenuButton(resources.getString("setup.menu.label"));
final var progressBar = new ProgressBar();
final var progressLabel = new Label();
statusLabel.textProperty().bind(model.managerStatusProperty(type).map(s -> resources.getString("setup.status." + s.name().toLowerCase() + ".label")));
progressLabel.textProperty().bind(model.setupProgressLabelProperty(type));
progressLabel.visibleProperty().bind(model.setupInProgressProperty(type));
progressBar.progressProperty().bind(model.setupProgressProperty(type));
progressBar.visibleProperty().bind(model.setupInProgressProperty(type));
installationButton.disableProperty().bind(model.setupInProgressProperty(type).or(Bindings.isEmpty(installationButton.getItems())));
bindMenu(installationButton, type);
fillMenu(installationButton, type);
final var rowIndex = setupGrid.getRowCount() - 1;
final var constraints = new RowConstraints();
constraints.setFillHeight(true);
constraints.setVgrow(Priority.SOMETIMES);
setupGrid.getRowConstraints().add(rowIndex, constraints);
setupGrid.add(statusLabel, 1, rowIndex);
setupGrid.add(installationButton, 2, rowIndex);
setupGrid.add(progressBar, 3, rowIndex);
setupGrid.add(progressLabel, 4, rowIndex);
}
private void bindMenu(final MenuButton button, final ToolType type) {
model.managerStatusProperty(type).addListener((observable, oldValue, newValue) -> fillMenu(button, type));
}
private void fillMenu(final MenuButton button, final ToolType type) {
final var value = model.managerStatus(type);
button.getItems().clear();
switch (value) {
case NOT_INSTALLED -> {
final var installItem = new MenuItem(resources.getString("setup.menu.install.label"));
installItem.setOnAction(e -> install(type));
button.getItems().add(installItem);
}
case BUNDLE_INSTALLED -> {
final var reinstallItem = new MenuItem(resources.getString("setup.menu.reinstall.label"));
reinstallItem.setOnAction(e -> reinstall(type));
final var uninstallItem = new MenuItem(resources.getString("setup.menu.uninstall.label"));
uninstallItem.setOnAction(e -> uninstall(type));
button.getItems().addAll(reinstallItem, uninstallItem);
}
case UPDATE_AVAILABLE -> {
final var updateItem = new MenuItem(resources.getString("setup.menu.update.label"));
updateItem.setOnAction(e -> update(type));
final var reinstallItem = new MenuItem(resources.getString("setup.menu.reinstall.label"));
reinstallItem.setOnAction(e -> reinstall(type));
final var uninstallItem = new MenuItem(resources.getString("setup.menu.uninstall.label"));
uninstallItem.setOnAction(e -> uninstall(type));
button.getItems().addAll(updateItem, reinstallItem, uninstallItem);
}
case null, default -> {
// Do nothing, buttons are cleared
}
}
}
@FunctionalInterface
private interface SetupConsumer {
void accept(SetupManager manager) throws SetupException;
@@ -321,6 +260,7 @@ public class FXSetupController extends AbstractFXController implements SetupCont
@Override
public Window window() {
return converterNameLabel.getScene().getWindow();
return setupGrid.getScene().getWindow();
}
}

View File

@@ -1,16 +1,25 @@
package com.github.gtache.autosubtitle.gui.setup.fx;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.gui.setup.SetupModel;
import com.github.gtache.autosubtitle.setup.SetupManager;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Collection;
import java.util.Map;
/**
* FX implementation of {@link SetupModel}
@@ -18,152 +27,146 @@ import javax.inject.Singleton;
@Singleton
public class FXSetupModel implements SetupModel {
private final ObjectProperty<SetupStatus> subtitleExtractorStatus;
private final DoubleProperty subtitleExtractorSetupProgress;
private final StringProperty subtitleExtractorSetupProgressLabel;
private final ObjectProperty<SetupStatus> videoConverterStatus;
private final DoubleProperty videoConverterSetupProgress;
private final StringProperty videoConverterSetupProgressLabel;
private final ObjectProperty<SetupStatus> translatorStatus;
private final DoubleProperty translatorSetupProgress;
private final StringProperty translatorSetupProgressLabel;
private final ObservableMap<ToolType, ObservableList<SetupManager>> availableSetupManagers;
private final ObservableMap<ToolType, StringProperty> selectedTools;
private final ObservableMap<ToolType, ObjectProperty<SetupManager>> selectedSetupManagers;
private final ObservableMap<ToolType, ObjectProperty<SetupStatus>> setupStatuses;
private final ObservableMap<ToolType, DoubleProperty> setupProgresses;
private final ObservableMap<ToolType, StringProperty> setupProgressLabels;
private final ObservableMap<ToolType, ReadOnlyBooleanWrapper> setupInProgress;
@Inject
FXSetupModel() {
this.subtitleExtractorStatus = new SimpleObjectProperty<>(SetupStatus.ERRORED);
this.subtitleExtractorSetupProgress = new SimpleDoubleProperty(-2);
this.subtitleExtractorSetupProgressLabel = new SimpleStringProperty("");
this.videoConverterStatus = new SimpleObjectProperty<>(SetupStatus.ERRORED);
this.videoConverterSetupProgress = new SimpleDoubleProperty(-2);
this.videoConverterSetupProgressLabel = new SimpleStringProperty("");
this.translatorStatus = new SimpleObjectProperty<>(SetupStatus.ERRORED);
this.translatorSetupProgress = new SimpleDoubleProperty(-2);
this.translatorSetupProgressLabel = new SimpleStringProperty("");
FXSetupModel(final Map<ToolType, Map<String, SetupManager>> setupManagers,
final Map<ToolType, SetupManager> defaultManagers) {
final var tmpAvailableSetupManagers = FXCollections.<ToolType, ObservableList<SetupManager>>observableHashMap();
final var tmpSelectedTools = FXCollections.<ToolType, StringProperty>observableHashMap();
final var tmpSelectedSetupManagers = FXCollections.<ToolType, ObjectProperty<SetupManager>>observableHashMap();
final var tmpSetupStatuses = FXCollections.<ToolType, ObjectProperty<SetupStatus>>observableHashMap();
final var tmpSetupProgresses = FXCollections.<ToolType, DoubleProperty>observableHashMap();
final var tmpSetupProgressLabels = FXCollections.<ToolType, StringProperty>observableHashMap();
final var tmpSetupEditables = FXCollections.<ToolType, ReadOnlyBooleanWrapper>observableHashMap();
setupManagers.forEach((type, managers) -> {
tmpAvailableSetupManagers.put(type, getUnmodifiableObservableList(managers.values()));
tmpSelectedTools.put(type, new SimpleStringProperty(getKeyForValue(managers, defaultManagers.get(type))));
tmpSelectedSetupManagers.put(type, new SimpleObjectProperty<>());
tmpSetupStatuses.put(type, new SimpleObjectProperty<>(SetupStatus.ERRORED));
tmpSetupProgresses.put(type, new SimpleDoubleProperty(-2));
tmpSetupProgressLabels.put(type, new SimpleStringProperty(""));
tmpSetupEditables.put(type, new ReadOnlyBooleanWrapper(false));
});
this.availableSetupManagers = FXCollections.unmodifiableObservableMap(tmpAvailableSetupManagers);
this.selectedTools = FXCollections.unmodifiableObservableMap(tmpSelectedTools);
this.selectedSetupManagers = FXCollections.unmodifiableObservableMap(tmpSelectedSetupManagers);
this.setupStatuses = FXCollections.unmodifiableObservableMap(tmpSetupStatuses);
this.setupProgresses = FXCollections.unmodifiableObservableMap(tmpSetupProgresses);
this.setupProgressLabels = FXCollections.unmodifiableObservableMap(tmpSetupProgressLabels);
this.setupInProgress = FXCollections.unmodifiableObservableMap(tmpSetupEditables);
selectedSetupManagers.forEach((type, value) -> value.addListener((observable, oldValue, newValue) ->
{
setupStatuses.get(type).set(newValue == null ? SetupStatus.ERRORED : newValue.status());
selectedTools.get(type).set(newValue == null ?
getKeyForValue(setupManagers.get(type), defaultManagers.get(type)) :
getKeyForValue(setupManagers.get(type), newValue));
}));
selectedTools.forEach((type, value) -> value.addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
final var manager = setupManagers.get(type).get(newValue);
if (manager != null) {
setSelectedSetupManager(type, manager);
}
}
}));
setupInProgress.forEach((type, value) -> value.bind(setupProgresses.get(type).greaterThan(-2)));
defaultManagers.forEach(this::setSelectedSetupManager);
}
private static String getKeyForValue(final Map<String, ? extends SetupManager> managers, final SetupManager manager) {
return managers.entrySet().stream().filter(e -> e.getValue() == manager).findFirst().orElseThrow().getKey();
}
@Override
public SetupStatus subtitleExtractorStatus() {
return subtitleExtractorStatus.get();
public ObservableList<SetupManager> availableSetupManagers(final ToolType type) {
return this.availableSetupManagers.getOrDefault(type, FXCollections.emptyObservableList());
}
@Override
public void setSubtitleExtractorStatus(final SetupStatus status) {
subtitleExtractorStatus.set(status);
}
ObjectProperty<SetupStatus> subtitleExtractorStatusProperty() {
return subtitleExtractorStatus;
public SetupManager selectedSetupManager(final ToolType type) {
return this.selectedSetupManagers.get(type).get();
}
@Override
public double subtitleExtractorSetupProgress() {
return subtitleExtractorSetupProgress.get();
public void setSelectedSetupManager(final ToolType type, final SetupManager manager) {
this.selectedSetupManagers.get(type).set(manager);
}
ObjectProperty<SetupManager> selectedSetupManagerProperty(final ToolType type) {
return this.selectedSetupManagers.get(type);
}
@Override
public void setSubtitleExtractorSetupProgress(final double progress) {
subtitleExtractorSetupProgress.set(progress);
public String selectedTool(final ToolType type) {
return this.selectedTools.get(type).get();
}
DoubleProperty subtitleExtractorSetupProgressProperty() {
return subtitleExtractorSetupProgress;
public StringProperty selectedToolProperty(final ToolType type) {
return this.selectedTools.get(type);
}
@Override
public String subtitleExtractorSetupProgressLabel() {
return subtitleExtractorSetupProgressLabel.get();
public SetupStatus managerStatus(final ToolType type) {
return this.setupStatuses.get(type).get();
}
@Override
public void setSubtitleExtractorSetupProgressLabel(final String label) {
subtitleExtractorSetupProgressLabel.set(label);
public void setManagerStatus(final ToolType type, final SetupStatus status) {
this.setupStatuses.get(type).set(status);
}
StringProperty subtitleExtractorSetupProgressLabelProperty() {
return subtitleExtractorSetupProgressLabel;
ObjectProperty<SetupStatus> managerStatusProperty(final ToolType type) {
return this.setupStatuses.get(type);
}
@Override
public SetupStatus videoConverterStatus() {
return videoConverterStatus.get();
public double setupProgress(final ToolType type) {
return this.setupProgresses.get(type).get();
}
@Override
public void setVideoConverterStatus(final SetupStatus status) {
videoConverterStatus.set(status);
public void setSetupProgress(final ToolType type, final double progress) {
this.setupProgresses.get(type).set(progress);
}
ObjectProperty<SetupStatus> videoConverterStatusProperty() {
return videoConverterStatus;
DoubleProperty setupProgressProperty(final ToolType type) {
return this.setupProgresses.get(type);
}
@Override
public double videoConverterSetupProgress() {
return videoConverterSetupProgress.get();
public String setupProgressLabel(final ToolType type) {
return this.setupProgressLabels.get(type).get();
}
@Override
public void setVideoConverterSetupProgress(final double progress) {
videoConverterSetupProgress.set(progress);
public void setSetupProgressLabel(final ToolType type, final String label) {
this.setupProgressLabels.get(type).set(label);
}
DoubleProperty videoConverterSetupProgressProperty() {
return videoConverterSetupProgress;
StringProperty setupProgressLabelProperty(final ToolType type) {
return this.setupProgressLabels.get(type);
}
@Override
public String videoConverterSetupProgressLabel() {
return videoConverterSetupProgressLabel.get();
public boolean isSetupInProgress(final ToolType type) {
return this.setupInProgress.get(type).get();
}
@Override
public void setVideoConverterSetupProgressLabel(final String label) {
videoConverterSetupProgressLabel.set(label);
ReadOnlyBooleanProperty setupInProgressProperty(final ToolType type) {
return this.setupInProgress.get(type).getReadOnlyProperty();
}
StringProperty videoConverterSetupProgressLabelProperty() {
return videoConverterSetupProgressLabel;
}
@Override
public SetupStatus translatorStatus() {
return translatorStatus.get();
}
@Override
public void setTranslatorStatus(final SetupStatus status) {
translatorStatus.set(status);
}
ObjectProperty<SetupStatus> translatorStatusProperty() {
return translatorStatus;
}
@Override
public double translatorSetupProgress() {
return translatorSetupProgress.get();
}
@Override
public void setTranslatorSetupProgress(final double progress) {
translatorSetupProgress.set(progress);
}
DoubleProperty translatorSetupProgressProperty() {
return translatorSetupProgress;
}
@Override
public String translatorSetupProgressLabel() {
return translatorSetupProgressLabel.get();
}
@Override
public void setTranslatorSetupProgressLabel(final String label) {
translatorSetupProgressLabel.set(label);
}
StringProperty translatorSetupProgressLabelProperty() {
return translatorSetupProgressLabel;
private static <T> ObservableList<T> getUnmodifiableObservableList(final Collection<? extends T> collection) {
return FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(collection));
}
}

View File

@@ -0,0 +1,16 @@
package com.github.gtache.autosubtitle.gui.setup.fx;
import com.github.gtache.autosubtitle.setup.SetupManager;
import javafx.util.StringConverter;
class SetupManagerStringConverter extends StringConverter<SetupManager> {
@Override
public String toString(final SetupManager object) {
return object.name();
}
@Override
public SetupManager fromString(final String string) {
return null;
}
}

View File

@@ -1,8 +1,10 @@
package com.github.gtache.autosubtitle.gui.subtitles.fx;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.gui.fx.FXBinder;
import com.github.gtache.autosubtitle.gui.parameters.fx.FXParametersModel;
import com.github.gtache.autosubtitle.gui.setup.fx.FXSetupModel;
import com.github.gtache.autosubtitle.gui.work.WorkStatus;
import com.github.gtache.autosubtitle.gui.work.fx.FXWorkModel;
import com.github.gtache.autosubtitle.subtitle.converter.impl.FormatOptionsImpl;
@@ -14,7 +16,8 @@ import javafx.beans.binding.Bindings;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
/**
* Binds the subtitles model
@@ -24,13 +27,15 @@ public class FXSubtitlesBinder implements FXBinder {
private final FXWorkModel workModel;
private final FXParametersModel parametersModel;
private final FXSetupModel setupModel;
private final FXSubtitlesModel subtitlesModel;
@Inject
FXSubtitlesBinder(final FXWorkModel workModel, final FXParametersModel parametersModel, final FXSubtitlesModel subtitlesModel) {
this.workModel = Objects.requireNonNull(workModel);
this.parametersModel = Objects.requireNonNull(parametersModel);
this.subtitlesModel = Objects.requireNonNull(subtitlesModel);
FXSubtitlesBinder(final FXWorkModel workModel, final FXParametersModel parametersModel, final FXSetupModel setupModel, final FXSubtitlesModel subtitlesModel) {
this.workModel = requireNonNull(workModel);
this.parametersModel = requireNonNull(parametersModel);
this.setupModel = requireNonNull(setupModel);
this.subtitlesModel = requireNonNull(subtitlesModel);
}
@Override
@@ -74,5 +79,12 @@ public class FXSubtitlesBinder implements FXBinder {
final var parseOptions = new ParseOptionsImpl(parametersModel.maxLineLength(), parametersModel.maxLines(), parametersModel.font());
return new ImportOptionsImpl(parseOptions);
}, parametersModel.maxLineLengthProperty(), parametersModel.maxLinesProperty(), parametersModel.fontProperty()));
setupModel.selectedToolProperty(ToolType.TRANSLATOR).addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
subtitlesModel.translatorProperty().set(subtitlesModel.availableTranslators().get(newValue));
}
});
}
}

View File

@@ -10,7 +10,6 @@ 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.ObservableSubtitleImpl;
import com.github.gtache.autosubtitle.translation.TranslationException;
import com.github.gtache.autosubtitle.translation.Translator;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty;
@@ -87,21 +86,18 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
private TableColumn<ObservableSubtitleImpl, String> textColumn;
private final FXSubtitlesModel model;
private final FXSubtitlesBinder binder;
private final SubtitleImporterExporter<?> importerExporter;
private final TimeFormatter timeFormatter;
private final List<String> subtitleExtensions;
private final Translator<?> translator;
@Inject
FXSubtitlesController(final FXSubtitlesModel model, final FXSubtitlesBinder binder, final SubtitleImporterExporter importerExporter, final TimeFormatter timeFormatter,
final Translator translator) {
FXSubtitlesController(final FXSubtitlesModel model, final FXSubtitlesBinder binder,
final SubtitleImporterExporter importerExporter, final TimeFormatter timeFormatter) {
this.model = requireNonNull(model);
this.binder = requireNonNull(binder);
this.importerExporter = requireNonNull(importerExporter);
this.timeFormatter = requireNonNull(timeFormatter);
this.subtitleExtensions = importerExporter.supportedSingleFileExtensions().stream().map(c -> "*." + c).sorted().toList();
this.translator = requireNonNull(translator);
binder.createBindings();
}
@FXML
@@ -135,7 +131,6 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
});
translationsCombobox.setOnAction(e -> translateToNewLanguage());
binder.createBindings();
}
private void translateToNewLanguage() {
@@ -149,10 +144,10 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
if (model.selectedCollection() == null) {
return null;
} else {
return translator.translate(model.selectedCollection(), value);
return model.translatorProperty().get().translate(model.selectedCollection(), value);
}
} else {
return translator.translate(mainCollection, value);
return model.translatorProperty().get().translate(mainCollection, value);
}
} catch (final TranslationException ex) {
throw new CompletionException(ex);

View File

@@ -6,6 +6,7 @@ import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.ImportOptions;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
import com.github.gtache.autosubtitle.translation.Translator;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
@@ -22,6 +23,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
/**
* FX implementation of {@link SubtitlesModel}
@@ -39,6 +41,8 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
private final ObjectProperty<ObservableSubtitleCollectionImpl> selectedCollection;
private final ObservableList<ObservableSubtitleImpl> selectedSubtitles;
private final ObjectProperty<ObservableSubtitleImpl> selectedSubtitle;
private final ObservableMap<String, Translator<?>> availableTranslators;
private final ObjectProperty<Translator<?>> translator;
private final BooleanProperty canLoadSubtitles;
private final BooleanProperty canAddSubtitle;
@@ -50,7 +54,8 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
private final ObjectProperty<ImportOptions> importOptions;
@Inject
FXSubtitlesModel() {
FXSubtitlesModel(final Map<String, Translator<?>> translators, final Translator defaultTranslator) {
this.availableTranslators = FXCollections.unmodifiableObservableMap(FXCollections.observableMap(translators));
this.availableVideoLanguages = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(Arrays.stream(Language.values())
.sorted((o1, o2) -> {
if (o1 == Language.AUTO) {
@@ -78,6 +83,7 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
this.isTranslating = new SimpleBooleanProperty(false);
this.exportOptions = new SimpleObjectProperty<>();
this.importOptions = new SimpleObjectProperty<>();
this.translator = new SimpleObjectProperty<>(defaultTranslator);
canSaveSubtitles.bind(Bindings.isNotEmpty(collections));
collections.addListener((MapChangeListener<Language, ObservableSubtitleCollectionImpl>) change ->
@@ -275,4 +281,12 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
public ObjectProperty<ImportOptions> importOptionsProperty() {
return importOptions;
}
public ObservableMap<String, Translator<?>> availableTranslators() {
return availableTranslators;
}
public ObjectProperty<Translator<?>> translatorProperty() {
return translator;
}
}

View File

@@ -1,7 +1,5 @@
package com.github.gtache.autosubtitle.gui.work.fx;
import com.github.gtache.autosubtitle.VideoConverter;
import com.github.gtache.autosubtitle.VideoLoader;
import com.github.gtache.autosubtitle.gui.fx.AbstractFXController;
import com.github.gtache.autosubtitle.gui.media.fx.FXMediaController;
import com.github.gtache.autosubtitle.gui.work.WorkController;
@@ -9,7 +7,6 @@ import com.github.gtache.autosubtitle.gui.work.WorkStatus;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractorListener;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
@@ -52,18 +49,14 @@ public class FXWorkController extends AbstractFXController implements WorkContro
@FXML
private TextField fileField;
@FXML
private Button extractButton;
@FXML
private Button exportSoftButton;
@FXML
private Button exportHardButton;
@FXML
private FXMediaController mediaController;
@FXML
private Label progressLabel;
@FXML
@@ -74,19 +67,11 @@ public class FXWorkController extends AbstractFXController implements WorkContro
private ResourceBundle resources;
private final FXWorkModel model;
private final FXWorkBinder binder;
private final SubtitleExtractor<?> subtitleExtractor;
private final VideoConverter videoConverter;
private final VideoLoader videoLoader;
@Inject
FXWorkController(final FXWorkModel model, final FXWorkBinder binder, final SubtitleExtractor subtitleExtractor,
final VideoLoader videoLoader, final VideoConverter videoConverter) {
FXWorkController(final FXWorkModel model, final FXWorkBinder binder) {
this.model = requireNonNull(model);
this.binder = requireNonNull(binder);
this.subtitleExtractor = requireNonNull(subtitleExtractor);
this.videoConverter = requireNonNull(videoConverter);
this.videoLoader = requireNonNull(videoLoader);
binder.createBindings();
}
@FXML
@@ -103,9 +88,11 @@ public class FXWorkController extends AbstractFXController implements WorkContro
}
});
binder.createBindings();
subtitleExtractor.addListener(this);
model.subtitleExtractorProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
newValue.addListener(FXWorkController.this);
}
});
}
private void bindProgress() {
@@ -142,7 +129,7 @@ public class FXWorkController extends AbstractFXController implements WorkContro
private SubtitleCollection<?> extractAsync() {
try {
return subtitleExtractor.extract(model.video(), model.extractOptions());
return model.subtitleExtractorProperty().get().extract(model.video(), model.extractOptions());
} catch (final ExtractException e) {
throw new CompletionException(e);
}
@@ -161,7 +148,7 @@ public class FXWorkController extends AbstractFXController implements WorkContro
@Override
public void loadVideo(final Path file) {
try {
final var loadedVideo = videoLoader.loadVideo(file);
final var loadedVideo = model.videoLoaderProperty().get().loadVideo(file);
fileField.setText(file.toAbsolutePath().toString());
model.videoProperty().set(loadedVideo);
} catch (final IOException e) {
@@ -182,7 +169,7 @@ public class FXWorkController extends AbstractFXController implements WorkContro
model.setStatus(WorkStatus.EXPORTING);
CompletableFuture.runAsync(() -> {
try {
videoConverter.addSoftSubtitles(model.video(), model.collections().values(), model.exportOptions(), file.toPath());
model.videoConverterProperty().get().addSoftSubtitles(model.video(), model.collections().values(), model.exportOptions(), file.toPath());
} catch (final IOException e) {
throw new CompletionException(e);
}
@@ -207,7 +194,7 @@ public class FXWorkController extends AbstractFXController implements WorkContro
model.setStatus(WorkStatus.EXPORTING);
CompletableFuture.runAsync(() -> {
try {
videoConverter.addHardSubtitles(model.video(), model.collections().get(model.extractOptions().language()), model.exportOptions(), file.toPath());
model.videoConverterProperty().get().addHardSubtitles(model.video(), model.collections().get(model.extractOptions().language()), model.exportOptions(), file.toPath());
} catch (final IOException e) {
throw new CompletionException(e);
}

View File

@@ -2,12 +2,15 @@ package com.github.gtache.autosubtitle.gui.work.fx;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoConverter;
import com.github.gtache.autosubtitle.VideoLoader;
import com.github.gtache.autosubtitle.gui.work.WorkModel;
import com.github.gtache.autosubtitle.gui.work.WorkStatus;
import com.github.gtache.autosubtitle.subtitle.EditableSubtitle;
import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
import javafx.beans.property.BooleanProperty;
@@ -24,6 +27,7 @@ import javafx.collections.ObservableMap;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
/**
* FX implementation of {@link WorkModel}
@@ -31,6 +35,12 @@ import javax.inject.Singleton;
@Singleton
public class FXWorkModel implements WorkModel {
private final ObservableMap<String, SubtitleExtractor<?>> availableSubtitleExtractors;
private final ObjectProperty<SubtitleExtractor<?>> subtitleExtractor;
private final ObservableMap<String, VideoLoader> availableVideoLoaders;
private final ObjectProperty<VideoLoader> videoLoader;
private final ObservableMap<String, VideoConverter> availableVideoConverters;
private final ObjectProperty<VideoConverter> videoConverter;
private final ObjectProperty<Video> video;
private final ObjectProperty<WorkStatus> workStatus;
private final DoubleProperty progress;
@@ -46,7 +56,15 @@ public class FXWorkModel implements WorkModel {
private final ObjectProperty<ExtractOptions> extractOptions;
@Inject
FXWorkModel() {
FXWorkModel(final Map<String, SubtitleExtractor<?>> subtitleExtractors, final SubtitleExtractor defaultExtractor,
final Map<String, VideoLoader> videoLoaders, final VideoLoader defaultVideoLoader,
final Map<String, VideoConverter> videoConverters, final VideoConverter defaultVideoConverter) {
this.availableSubtitleExtractors = FXCollections.unmodifiableObservableMap(FXCollections.observableMap(subtitleExtractors));
this.subtitleExtractor = new SimpleObjectProperty<>(defaultExtractor);
this.availableVideoLoaders = FXCollections.unmodifiableObservableMap(FXCollections.observableMap(videoLoaders));
this.videoLoader = new SimpleObjectProperty<>(defaultVideoLoader);
this.availableVideoConverters = FXCollections.unmodifiableObservableMap(FXCollections.observableMap(videoConverters));
this.videoConverter = new SimpleObjectProperty<>(defaultVideoConverter);
this.video = new SimpleObjectProperty<>();
this.workStatus = new SimpleObjectProperty<>(WorkStatus.IDLE);
this.progress = new SimpleDoubleProperty(-1);
@@ -175,7 +193,7 @@ public class FXWorkModel implements WorkModel {
exportOptions.set(options);
}
public ObjectProperty<ExportOptions> exportOptionsProperty() {
ObjectProperty<ExportOptions> exportOptionsProperty() {
return exportOptions;
}
@@ -189,7 +207,31 @@ public class FXWorkModel implements WorkModel {
extractOptions.set(options);
}
public ObjectProperty<ExtractOptions> extractOptionsProperty() {
ObjectProperty<ExtractOptions> extractOptionsProperty() {
return extractOptions;
}
ObservableMap<String, SubtitleExtractor<?>> availableSubtitleExtractors() {
return availableSubtitleExtractors;
}
ObjectProperty<SubtitleExtractor<?>> subtitleExtractorProperty() {
return subtitleExtractor;
}
ObservableMap<String, VideoLoader> availableVideoLoaders() {
return availableVideoLoaders;
}
ObjectProperty<VideoLoader> videoLoaderProperty() {
return videoLoader;
}
ObservableMap<String, VideoConverter> availableVideoConverters() {
return availableVideoConverters;
}
ObjectProperty<VideoConverter> videoConverterProperty() {
return videoConverter;
}
}

View File

@@ -2,13 +2,11 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
<GridPane fx:id="setupGrid" hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/22"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.gtache.autosubtitle.gui.setup.fx.FXSetupController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
@@ -18,35 +16,11 @@
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS"/>
<RowConstraints vgrow="ALWAYS"/>
</rowConstraints>
<children>
<Label fx:id="converterNameLabel" text="Label" GridPane.rowIndex="1"/>
<Label fx:id="converterStatusLabel" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label fx:id="extractorNameLabel" text="Label" GridPane.rowIndex="2"/>
<Label fx:id="extractorStatusLabel" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Label fx:id="translatorNameLabel" text="Label" GridPane.rowIndex="3"/>
<Label fx:id="translatorStatusLabel" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
<MenuButton fx:id="converterButton" mnemonicParsing="false" text="%setup.menu.label" GridPane.columnIndex="2"
GridPane.rowIndex="1"/>
<MenuButton fx:id="extractorButton" mnemonicParsing="false" text="%setup.menu.label" GridPane.columnIndex="2"
GridPane.rowIndex="2"/>
<MenuButton fx:id="translatorButton" mnemonicParsing="false" text="%setup.menu.label" GridPane.columnIndex="2"
GridPane.rowIndex="3"/>
<Label text="%setup.description.label" GridPane.columnSpan="2147483647"/>
<ProgressBar fx:id="converterProgress" prefWidth="200.0" progress="0.0" GridPane.columnIndex="3"
GridPane.rowIndex="1"/>
<ProgressBar fx:id="extractorProgress" prefWidth="200.0" progress="0.0" GridPane.columnIndex="3"
GridPane.rowIndex="2"/>
<ProgressBar fx:id="translatorProgress" prefWidth="200.0" progress="0.0" GridPane.columnIndex="3"
GridPane.rowIndex="3"/>
<Label fx:id="converterProgressLabel" text="Label" GridPane.columnIndex="4" GridPane.rowIndex="1"/>
<Label fx:id="extractorProgressLabel" text="Label" GridPane.columnIndex="4" GridPane.rowIndex="2"/>
<Label fx:id="translatorProgressLabel" text="Label" GridPane.columnIndex="4" GridPane.rowIndex="3"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>

View File

@@ -52,7 +52,7 @@ class TestFXParametersController extends FxRobot {
this.videoConverter = requireNonNull(videoConverter);
this.translator = requireNonNull(translator);
this.timeFormatter = requireNonNull(timeFormatter);
this.model = spy(new FXParametersModel(mock(ExtractionModelProvider.class), "Arial", 12, 40, 1));
this.model = spy(new FXParametersModel(Map.of(), mock(ExtractionModelProvider.class), "Arial", 12, 40, 1));
this.binder = requireNonNull(binder);
this.window = FxToolkit.registerPrimaryStage();
this.controller = spy(FXParametersController.class);

View File

@@ -11,6 +11,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -42,7 +43,7 @@ class TestFXParametersModel {
void beforeEach() {
when(provider.getDefaultExtractionModel()).thenReturn(defaultExtractionModel);
when(provider.getAvailableExtractionModels()).thenReturn(availableExtractionModels);
this.model = new FXParametersModel(provider, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_MAX_LINE_LENGTH, DEFAULT_MAX_LINES);
this.model = new FXParametersModel(Map.of(), provider, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_MAX_LINE_LENGTH, DEFAULT_MAX_LINES); //TODO
}
@Test

View File

@@ -33,16 +33,16 @@ class TestFXSetupController extends FxRobot {
private final FXSetupModel model;
private final FXSetupController controller;
private final FXWorkBinder binder;
private final SubtitleExtractor extractor;
private final SubtitleExtractor<?> extractor;
private final Map<String, SubtitleConverter> subtitleConverters;
private final VideoLoader videoLoader;
private final VideoConverter videoConverter;
private final Translator translator;
private final Translator<?> translator;
private final TimeFormatter timeFormatter;
private final Stage window;
TestFXSetupController(@Mock final SubtitleExtractor extractor, @Mock final SubtitleConverter subtitleConverter, @Mock final VideoLoader videoLoader,
@Mock final VideoConverter videoConverter, @Mock final Translator translator, @Mock final TimeFormatter timeFormatter,
TestFXSetupController(@Mock final SubtitleExtractor<?> extractor, @Mock final SubtitleConverter<?> subtitleConverter, @Mock final VideoLoader videoLoader,
@Mock final VideoConverter videoConverter, @Mock final Translator<?> translator, @Mock final TimeFormatter timeFormatter,
@Mock final FXWorkBinder binder) throws TimeoutException {
this.extractor = requireNonNull(extractor);
this.subtitleConverters = Map.of("srt", requireNonNull(subtitleConverter));
@@ -50,7 +50,7 @@ class TestFXSetupController extends FxRobot {
this.videoConverter = requireNonNull(videoConverter);
this.translator = requireNonNull(translator);
this.timeFormatter = requireNonNull(timeFormatter);
this.model = spy(new FXSetupModel());
this.model = spy(new FXSetupModel(Map.of(), Map.of())); //TODO
this.binder = requireNonNull(binder);
this.window = FxToolkit.registerPrimaryStage();
this.controller = spy(FXSetupController.class);

View File

@@ -1,97 +1,12 @@
package com.github.gtache.autosubtitle.gui.setup.fx;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import org.junit.jupiter.api.Test;
import static com.github.gtache.autosubtitle.setup.SetupStatus.ERRORED;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Map;
class TestFXSetupModel {
private final FXSetupModel model;
TestFXSetupModel() {
this.model = new FXSetupModel();
}
@Test
void testSubtitleExtractorStatus() {
assertEquals(ERRORED, model.subtitleExtractorStatus());
assertEquals(ERRORED, model.subtitleExtractorStatusProperty().get());
model.setSubtitleExtractorStatus(SetupStatus.SYSTEM_INSTALLED);
assertEquals(SetupStatus.SYSTEM_INSTALLED, model.subtitleExtractorStatus());
assertEquals(SetupStatus.SYSTEM_INSTALLED, model.subtitleExtractorStatusProperty().get());
}
@Test
void testSubtitleExtractorSetupProgress() {
assertEquals(-2, model.subtitleExtractorSetupProgress());
assertEquals(-2, model.subtitleExtractorSetupProgressProperty().get());
model.setSubtitleExtractorSetupProgress(0.5);
assertEquals(0.5, model.subtitleExtractorSetupProgress());
assertEquals(0.5, model.subtitleExtractorSetupProgressProperty().get());
}
@Test
void testSubtitleExtractorSetupProgressLabel() {
assertEquals("", model.subtitleExtractorSetupProgressLabel());
assertEquals("", model.subtitleExtractorSetupProgressLabelProperty().get());
model.setSubtitleExtractorSetupProgressLabel("test");
assertEquals("test", model.subtitleExtractorSetupProgressLabel());
assertEquals("test", model.subtitleExtractorSetupProgressLabelProperty().get());
}
@Test
void testVideoConverterStatus() {
assertEquals(ERRORED, model.videoConverterStatus());
assertEquals(ERRORED, model.videoConverterStatusProperty().get());
model.setVideoConverterStatus(SetupStatus.SYSTEM_INSTALLED);
assertEquals(SetupStatus.SYSTEM_INSTALLED, model.videoConverterStatus());
assertEquals(SetupStatus.SYSTEM_INSTALLED, model.videoConverterStatusProperty().get());
}
@Test
void testVideoConverterSetupProgress() {
assertEquals(-2, model.videoConverterSetupProgress());
assertEquals(-2, model.videoConverterSetupProgressProperty().get());
model.setVideoConverterSetupProgress(0.5);
assertEquals(0.5, model.videoConverterSetupProgress());
assertEquals(0.5, model.videoConverterSetupProgressProperty().get());
}
@Test
void testVideoConverterSetupProgressLabel() {
assertEquals("", model.videoConverterSetupProgressLabel());
assertEquals("", model.videoConverterSetupProgressLabelProperty().get());
model.setVideoConverterSetupProgressLabel("test");
assertEquals("test", model.videoConverterSetupProgressLabel());
assertEquals("test", model.videoConverterSetupProgressLabelProperty().get());
}
@Test
void testTranslatorStatus() {
assertEquals(ERRORED, model.translatorStatus());
assertEquals(ERRORED, model.translatorStatusProperty().get());
model.setTranslatorStatus(SetupStatus.SYSTEM_INSTALLED);
assertEquals(SetupStatus.SYSTEM_INSTALLED, model.translatorStatus());
assertEquals(SetupStatus.SYSTEM_INSTALLED, model.translatorStatusProperty().get());
}
@Test
void testTranslatorSetupProgress() {
assertEquals(-2, model.translatorSetupProgress());
assertEquals(-2, model.translatorSetupProgressProperty().get());
model.setTranslatorSetupProgress(0.5);
assertEquals(0.5, model.translatorSetupProgress());
assertEquals(0.5, model.translatorSetupProgressProperty().get());
}
@Test
void testTranslatorSetupProgressLabel() {
assertEquals("", model.translatorSetupProgressLabel());
assertEquals("", model.translatorSetupProgressLabelProperty().get());
model.setTranslatorSetupProgressLabel("test");
assertEquals("test", model.translatorSetupProgressLabel());
assertEquals("test", model.translatorSetupProgressLabelProperty().get());
this.model = new FXSetupModel(Map.of(), Map.of()); //TODO
}
}

View File

@@ -1,11 +1,14 @@
package com.github.gtache.autosubtitle.gui.subtitles.fx;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.gui.parameters.fx.FXParametersModel;
import com.github.gtache.autosubtitle.gui.setup.fx.FXSetupModel;
import com.github.gtache.autosubtitle.gui.work.WorkStatus;
import com.github.gtache.autosubtitle.gui.work.fx.FXWorkModel;
import com.github.gtache.autosubtitle.setup.SetupManager;
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
import com.github.gtache.autosubtitle.subtitle.converter.impl.FormatOptionsImpl;
import com.github.gtache.autosubtitle.subtitle.converter.impl.ParseOptionsImpl;
@@ -15,6 +18,7 @@ import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
import com.github.gtache.autosubtitle.subtitle.impl.ExportOptionsImpl;
import com.github.gtache.autosubtitle.subtitle.impl.FontImpl;
import com.github.gtache.autosubtitle.subtitle.impl.ImportOptionsImpl;
import com.github.gtache.autosubtitle.translation.Translator;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
@@ -26,6 +30,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@@ -35,6 +40,14 @@ class TestFXSubtitlesBinder {
private final FXWorkModel workModel;
private final FXSubtitlesModel subtitlesModel;
private final Map<ToolType, Map<String, SetupManager>> setupManagers;
private final Map<ToolType, SetupManager> defaultManagers;
private final Map<String, Translator<?>> translators;
private final SetupManager firstManager;
private final SetupManager secondManager;
private final Translator<?> firstTranslator;
private final Translator<?> secondTranslator;
private final FXSetupModel setupModel;
private final FXParametersModel parametersModel;
private final FXSubtitlesBinder binder;
private final String defaultFontFamily;
@@ -42,7 +55,9 @@ class TestFXSubtitlesBinder {
private final int defaultMaxLineLength;
private final int defaultMaxLines;
TestFXSubtitlesBinder(@Mock final ExtractionModelProvider extractionModelProvider) {
TestFXSubtitlesBinder(@Mock final ExtractionModelProvider extractionModelProvider, @Mock final SetupManager firstManager,
@Mock final SetupManager secondManager, @Mock final Translator<?> firstTranslator,
@Mock final Translator<?> secondTranslator) {
this.workModel = spy(FXWorkModel.class);
this.defaultFontFamily = "Arial";
this.defaultFontSize = 12;
@@ -50,8 +65,16 @@ class TestFXSubtitlesBinder {
this.defaultMaxLines = 2;
this.parametersModel = mock(FXParametersModel.class, withSettings().defaultAnswer(CALLS_REAL_METHODS)
.useConstructor(extractionModelProvider, defaultFontFamily, defaultFontSize, defaultMaxLineLength, defaultMaxLines));
this.subtitlesModel = new FXSubtitlesModel();
this.binder = new FXSubtitlesBinder(workModel, parametersModel, subtitlesModel);
this.firstManager = Objects.requireNonNull(firstManager);
this.secondManager = Objects.requireNonNull(secondManager);
this.firstTranslator = Objects.requireNonNull(firstTranslator);
this.secondTranslator = Objects.requireNonNull(secondTranslator);
this.translators = Map.of("first", firstTranslator, "second", secondTranslator);
this.defaultManagers = Map.of(ToolType.TRANSLATOR, firstManager);
this.setupManagers = Map.of(ToolType.TRANSLATOR, Map.of("first", firstManager, "second", secondManager));
this.setupModel = mock(FXSetupModel.class, withSettings().defaultAnswer(CALLS_REAL_METHODS).useConstructor(setupManagers, defaultManagers));
this.subtitlesModel = new FXSubtitlesModel(translators, firstTranslator);
this.binder = new FXSubtitlesBinder(workModel, parametersModel, setupModel, subtitlesModel);
}
@BeforeEach
@@ -180,4 +203,13 @@ class TestFXSubtitlesBinder {
parametersModel.setMaxLines(4);
assertEquals(new ImportOptionsImpl(new ParseOptionsImpl(1, 4, new FontImpl("TT", 14))), subtitlesModel.importOptions());
}
@Test
void testTranslatorsBinding() {
assertEquals(firstTranslator, subtitlesModel.translatorProperty().get());
setupModel.setSelectedSetupManager(ToolType.TRANSLATOR, secondManager);
assertEquals(secondTranslator, subtitlesModel.translatorProperty().get());
setupModel.setSelectedSetupManager(ToolType.TRANSLATOR, null);
assertEquals(secondTranslator, subtitlesModel.translatorProperty().get());
}
}

View File

@@ -52,7 +52,7 @@ class TestFXSubtitlesController extends FxRobot {
this.videoConverter = requireNonNull(videoConverter);
this.translator = requireNonNull(translator);
this.timeFormatter = requireNonNull(timeFormatter);
this.model = spy(new FXSubtitlesModel());
this.model = spy(new FXSubtitlesModel(Map.of(), translator));
this.binder = requireNonNull(binder);
this.window = FxToolkit.registerPrimaryStage();
this.controller = spy(FXSubtitlesController.class);

View File

@@ -5,24 +5,36 @@ import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.ImportOptions;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
import com.github.gtache.autosubtitle.translation.Translator;
import javafx.collections.FXCollections;
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.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestFXSubtitlesModel {
private final FXSubtitlesModel model;
private final Map<String, Translator<?>> translators;
private final Translator<?> translator1;
private final Translator<?> translator2;
TestFXSubtitlesModel() {
this.model = new FXSubtitlesModel();
TestFXSubtitlesModel(@Mock final Translator<?> translator1, @Mock final Translator<?> translator2) {
this.translator1 = requireNonNull(translator1);
this.translator2 = requireNonNull(translator2);
this.translators = Map.of("1", translator1, "2", translator2);
this.model = new FXSubtitlesModel(translators, translator1);
}
@Test
@@ -202,4 +214,16 @@ class TestFXSubtitlesModel {
final var options = mock(ImportOptions.class);
assertDoesNotThrow(() -> model.importOptionsProperty().set(options));
}
@Test
void testAvailableTranslators() {
assertEquals(translators, model.availableTranslators());
assertThrows(UnsupportedOperationException.class, () -> model.availableTranslators().clear());
}
@Test
void testSelectedTranslator() {
assertEquals(translator1, model.translatorProperty().get());
assertDoesNotThrow(() -> model.translatorProperty().set(translator2));
}
}

View File

@@ -2,7 +2,9 @@ package com.github.gtache.autosubtitle.gui.work.fx;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoConverter;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.VideoLoader;
import com.github.gtache.autosubtitle.gui.parameters.fx.FXParametersModel;
import com.github.gtache.autosubtitle.gui.subtitles.fx.FXSubtitlesModel;
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
@@ -10,6 +12,7 @@ import com.github.gtache.autosubtitle.subtitle.converter.impl.FormatOptionsImpl;
import com.github.gtache.autosubtitle.subtitle.converter.impl.ParseOptionsImpl;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModelProvider;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.extractor.impl.ExtractOptionsImpl;
import com.github.gtache.autosubtitle.subtitle.impl.ExportOptionsImpl;
import com.github.gtache.autosubtitle.subtitle.impl.FontImpl;
@@ -19,6 +22,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -49,7 +53,7 @@ class TestFXWorkBinder {
this.defaultMaxLines = 1;
this.parametersModel = mock(FXParametersModel.class, withSettings().defaultAnswer(CALLS_REAL_METHODS).useConstructor(extractionModelProvider, defaultFontFamily, defaultFontSize, defaultMaxLineLength, defaultMaxLines));
this.subtitlesModel = spy(FXSubtitlesModel.class);
this.workModel = new FXWorkModel();
this.workModel = new FXWorkModel(Map.of(), mock(SubtitleExtractor.class), Map.of(), mock(VideoLoader.class), Map.of(), mock(VideoConverter.class)); //TODO
this.binder = new FXWorkBinder(workModel, parametersModel, subtitlesModel);
}

View File

@@ -18,6 +18,7 @@ import org.testfx.util.WaitForAsyncUtils;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.TimeoutException;
@@ -41,10 +42,10 @@ class TestFXWorkController extends FxRobot {
this.extractor = requireNonNull(extractor);
this.videoLoader = requireNonNull(videoLoader);
this.videoConverter = requireNonNull(videoConverter);
this.model = spy(new FXWorkModel());
this.model = spy(new FXWorkModel(Map.of(), extractor, Map.of(), videoLoader, Map.of(), videoConverter));
this.binder = requireNonNull(binder);
this.window = FxToolkit.registerPrimaryStage();
this.controller = spy(new FXWorkController(model, binder, extractor, videoLoader, videoConverter));
this.controller = spy(new FXWorkController(model, binder));
FxToolkit.setupStage(w -> {
final var loader = new FXMLLoader(getClass().getResource("/com/github/gtache/autosubtitle/gui/fx/workView.fxml"));

View File

@@ -2,11 +2,14 @@ package com.github.gtache.autosubtitle.gui.work.fx;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoConverter;
import com.github.gtache.autosubtitle.VideoLoader;
import com.github.gtache.autosubtitle.gui.work.WorkStatus;
import com.github.gtache.autosubtitle.subtitle.EditableSubtitle;
import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
import org.junit.jupiter.api.Test;
@@ -22,7 +25,7 @@ class TestFXWorkModel {
private final FXWorkModel model;
TestFXWorkModel() {
this.model = new FXWorkModel();
this.model = new FXWorkModel(Map.of(), mock(SubtitleExtractor.class), Map.of(), mock(VideoLoader.class), Map.of(), mock(VideoConverter.class)); //TODO
}
@Test

View File

@@ -24,6 +24,10 @@
<groupId>com.github.gtache.autosubtitle</groupId>
<artifactId>autosubtitle-gui-fx</artifactId>
</dependency>
<dependency>
<groupId>com.github.gtache.autosubtitle</groupId>
<artifactId>autosubtitle-whisper-base</artifactId>
</dependency>
<dependency>
<groupId>com.github.gtache.autosubtitle</groupId>
<artifactId>autosubtitle-whisperx</artifactId>

View File

@@ -5,6 +5,7 @@ import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegModule;
import com.github.gtache.autosubtitle.modules.gui.fx.FXModule;
import com.github.gtache.autosubtitle.modules.gui.impl.GuiCoreModule;
import com.github.gtache.autosubtitle.modules.impl.CoreModule;
import com.github.gtache.autosubtitle.modules.whisper.base.WhisperModule;
import com.github.gtache.autosubtitle.modules.whisperx.WhisperXModule;
import dagger.Component;
import javafx.fxml.FXMLLoader;
@@ -17,7 +18,7 @@ import javax.inject.Singleton;
@FunctionalInterface
@Singleton
@Component(modules = {CoreModule.class, GuiCoreModule.class, FXModule.class, FFmpegModule.class,
WhisperXModule.class, DeepLModule.class})
WhisperModule.class, WhisperXModule.class, DeepLModule.class})
public interface RunComponent {
/**

View File

@@ -5,6 +5,7 @@ module com.github.gtache.autosubtitle.gui.run {
requires com.github.gtache.autosubtitle.deepl;
requires com.github.gtache.autosubtitle.ffmpeg;
requires com.github.gtache.autosubtitle.gui.fx;
requires com.github.gtache.autosubtitle.whisper.base;
requires com.github.gtache.autosubtitle.whisperx;
requires javafx.fxml;
requires javafx.graphics;