diff --git a/MultiSubDownloader/pom.xml b/MultiSubDownloader/pom.xml index a8026fcd..a034d091 100644 --- a/MultiSubDownloader/pom.xml +++ b/MultiSubDownloader/pom.xml @@ -7,19 +7,12 @@ 4.0.0 - multisubdownloader - MultiSubDownloader + multisubdownloader + MultiSubDownloader - - - d-maven - http://d-maven.googlecode.com/svn/trunk/repo - - - - - ${maven.build.timestamp} - + + ${maven.build.timestamp} + @@ -62,6 +55,14 @@ com.fasterxml.jackson.core jackson-databind + + systems.manifold + manifold-ext-rt + + + systems.manifold + manifold-props-rt + org.projectlombok lombok @@ -109,11 +110,52 @@ src/main/resources true - - - - org.apache.maven.plugins + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-generated-sources + generate-sources + + add-source + + + + ${project.build.directory}/generated-source/openapi/src/main/java + + + + + + + org.apache.maven.plugins maven-compiler-plugin + + + -Xplugin:Manifold + + + + systems.manifold + manifold-ext + ${manifold.version} + + + systems.manifold + manifold-props + ${manifold.version} + + + systems.manifold + manifold-strings + ${manifold.version} + + + org.codehaus.mojo @@ -159,15 +201,12 @@ - - org.apache.maven.plugins - maven-compiler-plugin - org.openapitools openapi-generator-maven-plugin + gestdown generate @@ -203,7 +242,33 @@ java.time.OffsetDateTime=java.time.Instant java.time.LocalDateTime=java.time.Instant - + + + + opensubtitles + + generate + + + java + ${project.basedir}/src/main/resources/opensubtitles/open-api.json + ${project.build.directory}/generated-source/openapi + org.opensubtitles.model + org.opensubtitles.api + org.opensubtitles.invoker + org.opensubtitles + + + Authentication,Download,Subtitles + false + + integer=int,int=int + false + true + is + false + + diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java new file mode 100644 index 00000000..90cbbeb6 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java @@ -0,0 +1,82 @@ +package extensions.java.awt.Component; + +import java.awt.*; +import java.awt.event.MouseListener; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; +import org.jetbrains.annotations.Nullable; + +@UtilityClass +@Extension +public class ComponentExt { + + public static void setRecursive(@This Component component, Consumer consumer) { + setRecursive(component, consumer, _ -> true); + } + + public static void setRecursive(@This Component component, Consumer consumer, + Predicate condition) { + if (component != null) { + consumer.accept(component); + if (component instanceof Container container && condition.test(container)) { + container.getComponents().forEach(child -> setRecursive(child, consumer, condition)); + } + } + } + + public static @Self Component mouseListener(@This Component component, @Nullable MouseListener listener) { + component.addMouseListener(listener); + return component; + } + + public static @Self Component addTo(@This Component child, Container parent) { + parent.addComponent(child); + return child; + } + + public static @Self Component addTo(@This Component child, Container parent, Object constraints) { + parent.addComponent(child, constraints); + return child; + } + + public static @Self Component withSize(@This Component component, Dimension size) { + component.size = size; + component.minimumSize = size; + component.maximumSize = size; + component.preferredSize = size; + return component; + } + + public static @Self Component minSize(@This Component component, Dimension minSize) { + component.minimumSize = minSize; + return component; + } + + public static @Self Component minSize(@This Component component, int minWidth, int minHeight) { + return component.minSize(new Dimension(minWidth, minHeight)); + } + + public static @Self Component maxSize(@This Component component, Dimension maximumSize) { + component.maximumSize = maximumSize; + return component; + } + + public static @Self Component maxSize(@This Component component, int maxWidth, int maxHeight) { + return component.maxSize(new Dimension(maxWidth, maxHeight)); + } + + public static @Self Component font(@This Component component, Font font) { + component.setFont(font); + return component; + } + + public static @Self Component focusable(@This Component component, boolean focusable) { + component.setFocusable(focusable); + return component; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java new file mode 100644 index 00000000..ba02ec56 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java @@ -0,0 +1,33 @@ +package extensions.java.awt.Container; + +import java.awt.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class ContainerExt { + + public static @Self Container addComponent(@This Container component, Component child) { + component.add(child); + return component; + } + + public static @Self Container addComponent(@This Container component, Component child, Object constraints) { + component.add(child, constraints); + return component; + } + + public static @Self Container addComponent(@This Container component, Object constraints, Component child) { + component.add(child, constraints); + return component; + } + + public static @Self Container layout(@This Container container, LayoutManager mgr) { + container.setLayout(mgr); + return container; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java new file mode 100644 index 00000000..a1c2d9f1 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java @@ -0,0 +1,46 @@ +package extensions.javax.swing.AbstractButton; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.util.function.Consumer; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; +import org.lodder.subtools.sublibrary.util.BooleanConsumer; + +@UtilityClass +@Extension +public class AbstractButtonExt { + + public static @Self AbstractButton actionListener(@This AbstractButton abstractButton, ActionListener listener) { + abstractButton.addActionListener(listener); + return abstractButton; + } + + public static @Self AbstractButton actionListener(@This AbstractButton abstractButton, Runnable listener) { + return abstractButton.actionListener(_ -> listener.run()); + } + + public static @Self AbstractButton actionListenerSelf(@This AbstractButton abstractButton, + Consumer selfConsumerListener) { + return abstractButton.actionListener(_ -> selfConsumerListener.accept(abstractButton)); + } + + public static @Self AbstractButton selectedListener(@This AbstractButton abstractButton, + BooleanConsumer selectedConsumer) { + return abstractButton.actionListener(_ -> selectedConsumer.accept(abstractButton.isSelected())); + } + + public static @Self AbstractButton actionCommand(@This AbstractButton abstractButton, String actionCommand) { + abstractButton.setActionCommand(actionCommand); + return abstractButton; + } + + public static @Self AbstractButton withMargin(@This AbstractButton abstractButton, Insets insets) { + abstractButton.setMargin(insets); + return abstractButton; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java new file mode 100644 index 00000000..ad34b8be --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java @@ -0,0 +1,18 @@ +package extensions.javax.swing.JButton; + +import javax.swing.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class JButtonExt { + + public static @Self JButton defaultButtonFor(@This JButton abstractButton, JRootPane rootPane) { + rootPane.setDefaultButton(abstractButton); + return abstractButton; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java new file mode 100644 index 00000000..bbef78db --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java @@ -0,0 +1,29 @@ +package extensions.javax.swing.JCheckBox; + +import javax.swing.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; +import org.lodder.subtools.sublibrary.util.BooleanConsumer; + +@Extension +@UtilityClass +public class JCheckBoxExt { + + public static @Self JCheckBox addCheckedChangeListener(@This JCheckBox checkBox, BooleanConsumer... listeners) { + checkBox.addItemListener(e -> listeners.forEach(lis -> lis.accept(((JCheckBox) e.getSource()).isSelected()))); + return checkBox; + } + + public static @Self JCheckBox visible(@This JCheckBox checkBox, boolean visible) { + checkBox.setVisible(visible); + return checkBox; + } + + public static @Self JCheckBox selected(@This JCheckBox checkBox, boolean selected) { + checkBox.setSelected(selected); + return checkBox; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java new file mode 100644 index 00000000..c333c1c5 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java @@ -0,0 +1,67 @@ +package extensions.javax.swing.JComboBox; + +import javax.annotation.Nullable; +import javax.swing.*; +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.google.common.collect.Iterables; +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; +import manifold.ext.rt.api.ThisClass; +import net.jodah.typetools.TypeResolver; +import org.lodder.subtools.multisubdownloader.gui.ToStringListCellRenderer; + +@UtilityClass +@Extension +public class JComboBoxExt { + + public static JComboBox create(@ThisClass Class> thisClass, E... values) { + return new JComboBox<>(values); + } + + public static JComboBox create(@ThisClass Class> thisClass, Collection items) { + Class elementType = (Class) TypeResolver.resolveRawArguments(Collection.class, items.getClass())[0]; + return new JComboBox<>(Iterables.toArray(items, elementType)); + } + + public static E getSelectedValue(@This JComboBox comboBox) { + return (E) comboBox.getSelectedItem(); + } + + public static @Self JComboBox renderer(@This JComboBox comboBox, ListCellRenderer renderer) { + comboBox.setRenderer(renderer); + return comboBox; + } + + public static @Self JComboBox toStringRenderer(@This JComboBox comboBox, + Function toStringRenderer) { + return comboBox.renderer(ToStringListCellRenderer.of(comboBox.getRenderer(), toStringRenderer)); + } + + public static @Self JComboBox toMessageStringRenderer(@This JComboBox comboBox, + Function toStringRenderer) { + return comboBox.renderer(ToStringListCellRenderer.ofMessage(comboBox.getRenderer(), toStringRenderer)); + } + + public static @Self JComboBox itemListener(@This JComboBox comboBox, Runnable itemListener) { + comboBox.addItemListener(_ -> itemListener.run()); + return comboBox; + } + + + public static @Self JComboBox selectedValue(@This JComboBox comboBox, @Nullable E item) { + comboBox.setSelectedItem(item); + return comboBox; + } + + public static @Self JComboBox selectedItemConsumer(@This JComboBox comboBox, Consumer + actionListener) { + //noinspection unchecked + comboBox.addActionListener(ae -> actionListener.accept(((JComboBox) (ae.getSource())).getSelectedValue())); + return comboBox; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java new file mode 100644 index 00000000..7b41897c --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java @@ -0,0 +1,66 @@ +package extensions.javax.swing.JComponent; + +import javax.swing.*; +import java.awt.*; + +import extensions.java.awt.Component.ComponentExt; +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; +import org.jetbrains.annotations.Nullable; + +@UtilityClass +@Extension +public class JComponentExt { + + public static @Self JComponent enabled(@This JComponent component, boolean enabled) { + component.setEnabled(enabled); + return component; + } + + public static @Self JComponent enabled(@This JComponent component) { + return component.enabled(true); + } + + public static @Self JComponent disabled(@This JComponent component) { + return component.enabled(false); + } + + public static @Self JComponent toolTipText(@This JComponent component, String text) { + component.setToolTipText(text); + return component; + } + + public static @Self JComponent hidden(@This JComponent component) { + return component.visible(false); + } + + public static @Self JComponent visible(@This JComponent component) { + return component.visible(true); + } + + public static @Self JComponent visible(@This JComponent component, boolean visible) { + component.setVisible(visible); + return component; + } + + public static @Self JComponent background(@This JComponent component, @Nullable Color background) { + component.background = background; + return component; + } + + public static void setEnabledRecursive(@This JComponent component, boolean enabled) { + ComponentExt.setRecursive(component, c -> c.setEnabled(enabled)); + } + + public static @Self JComponent enabledRecursive(@This JComponent component, boolean enabled) { + setEnabledRecursive(component, enabled); + return component; + } + + public static @Self JComponent withToolTipText(@This JComponent component, String text) { + component.setToolTipText(text); + return component; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java new file mode 100644 index 00000000..2e36b5de --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java @@ -0,0 +1,20 @@ +package extensions.javax.swing.JScrollPane; + +import javax.swing.*; +import java.awt.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class JScrollPaneExt { + + public static @Self JScrollPane viewportView(@This JScrollPane scrollPane, Component view) { + scrollPane.setViewportView(view); + return scrollPane; + } + +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java new file mode 100644 index 00000000..edb2cf4f --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java @@ -0,0 +1,23 @@ +package extensions.javax.swing.JSlider; + +import javax.swing.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class JSliderExt { + + public static @Self JSlider minimum(@This JSlider slider, int minimum) { + slider.setMinimum(minimum); + return slider; + } + + public static @Self JSlider maximum(@This JSlider slider, int maximum) { + slider.setMaximum(maximum); + return slider; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java new file mode 100644 index 00000000..ddf74549 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java @@ -0,0 +1,18 @@ +package extensions.javax.swing.JTextArea; + +import javax.swing.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class JTextAreaExt { + + public static @Self JTextArea autoScrolls(@This JTextArea textArea, boolean autoScrolls) { + textArea.setAutoscrolls(autoScrolls); + return textArea; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java new file mode 100644 index 00000000..14e22efb --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java @@ -0,0 +1,18 @@ +package extensions.javax.swing.JTextField; + +import javax.swing.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class JTextFieldExt { + + public static @Self JTextField columns(@This JTextField textField, int columns) { + textField.setColumns(columns); + return textField; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java new file mode 100644 index 00000000..7c0fc933 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java @@ -0,0 +1,26 @@ +package extensions.javax.swing.text.JTextComponent; + +import javax.swing.text.*; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class JTextComponentExt { + + public static @Self JTextComponent editable(@This JTextComponent textComponent, boolean editable) { + textComponent.setEditable(editable); + return textComponent; + } + + public static @Self JTextComponent editable(@This JTextComponent textComponent) { + return textComponent.editable(true); + } + + public static @Self JTextComponent notEditable(@This JTextComponent textComponent) { + return textComponent.editable(false); + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java b/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java new file mode 100644 index 00000000..0beb2719 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java @@ -0,0 +1,19 @@ +package extensions.org.apache.commons.cli.CommandLine; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.apache.commons.cli.CommandLine; +import org.lodder.subtools.multisubdownloader.cli.CliOption; + +@Extension +@UtilityClass +public class CommandLineExt { + public static boolean hasCliOption(@This CommandLine line, CliOption cliOption) { + return line.hasOption(cliOption.getValue()); + } + + public static String getCliOptionValue(@This CommandLine line, CliOption cliOption) { + return line.getOptionValue(cliOption.getValue()); + } +} \ No newline at end of file diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java index c5773d48..1547a677 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java @@ -1,13 +1,13 @@ package org.lodder.subtools.multisubdownloader; +import static java.util.concurrent.TimeUnit.*; + import javax.swing.*; import java.awt.*; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.prefs.Preferences; import ch.qos.logback.classic.Level; @@ -26,7 +26,6 @@ import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; -import org.lodder.subtools.multisubdownloader.util.CLIExtension; import org.lodder.subtools.sublibrary.ConfigProperties; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; @@ -37,12 +36,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({ CLIExtension.class, Files.class }) +@ExtensionMethod({ Files.class }) public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + private static SettingsControl prefCtrl; private static Splash splash; - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); public static void main(String[] args) throws ReflectiveOperationException, UnsupportedLookAndFeelException { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -70,8 +70,8 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu final Container app = new Container(); final Manager manager = createManager(!line.hasCliOption(CliOption.NO_GUI)); prefCtrl = new SettingsControl(manager); - Messages.setLanguage(prefCtrl.getSettings().getLanguage()); - Bootstrapper bootstrapper = new Bootstrapper(app, prefCtrl.getSettings(), preferences, manager); + Messages.language = prefCtrl.settings.language; + Bootstrapper bootstrapper = new Bootstrapper(app, prefCtrl.settings, preferences, manager); if (line.hasCliOption(CliOption.TRACE)) { setLogLevel(Level.ALL); @@ -80,7 +80,7 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu } if (line.hasCliOption(CliOption.NO_GUI)) { - bootstrapper.initialize(new UserInteractionHandlerCLI(prefCtrl.getSettings())); + bootstrapper.initialize(new UserInteractionHandlerCLI(prefCtrl.settings)); CLI cmd = new CLI(prefCtrl, app); /* Defined here so there is output on console */ @@ -101,7 +101,7 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu /* Defined here so there is output in the splash */ importPreferences(line); - bootstrapper.initialize(new UserInteractionHandlerGUI(prefCtrl.getSettings(), null)); + bootstrapper.initialize(new UserInteractionHandlerGUI(prefCtrl.settings, null)); EventQueue.invokeLater(() -> { try { JFrame window = new GUI(prefCtrl, app); @@ -115,9 +115,10 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu } new Thread(() -> { SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); - List providerNames = subtitleProviderStore.getAllProviders().stream().map(SubtitleProvider::getProviderName) - .map(providerName -> providerName.contains("-") ? providerName.split("-")[0] : providerName) - .map(providerName -> providerName + "-").toList(); + List providerNames = + subtitleProviderStore.getAllProviders().stream().map(SubtitleProvider::getProviderName) + .map(providerName -> providerName.contains("-") ? providerName.split("-")[0] : providerName) + .map(providerName -> providerName + "-").toList(); manager.clearExpiredCacheBuilder() .cacheType(CacheType.DISK) .keyFilter((String key) -> providerNames.stream().noneMatch(key::startsWith)) @@ -127,7 +128,8 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu } private static void setLogLevel(Level level) { - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + ch.qos.logback.classic.Logger root = + (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.setLevel(level); } @@ -147,24 +149,25 @@ private static void importPreferences(CommandLine line) { public static Options getCLIOptions() { Options options = new Options(); - Arrays.stream(CliOption.values()).forEach( - cliOption -> options.addOption(cliOption.getValue(), cliOption.getLongValue(), cliOption.isHasArg(), cliOption.getDescription())); + CliOption.values().forEach( + cliOption -> options.addOption(cliOption.value, cliOption.longValue, cliOption.hasArg, + cliOption.description)); return options; } private static Manager createManager(boolean useGui) { if (splash != null) { - splash.setProgressMsg(Messages.getString("App.Starting")); + splash.setProgressMsg(Messages.getText("App.Starting")); } DiskCache diskCache = SerializableDiskCache.cacheBuilder().keyType(String.class).valueType(Serializable.class) - .timeToLive(TimeUnit.SECONDS.convert(500, TimeUnit.DAYS)) + .timeToLive(SECONDS.convert(500, DAYS)) .maxItems(2500) .build(); InMemoryCache inMemoryCache = InMemoryCache.builder().keyType(String.class).valueType(Serializable.class) - .timeToLive(TimeUnit.SECONDS.convert(10, TimeUnit.MINUTES)) + .timeToLive(SECONDS.convert(10, MINUTES)) .timerInterval(100L) .maxItems(500) .build(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java index 0ba21335..cfa4f34e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; @@ -13,7 +12,7 @@ import org.lodder.subtools.multisubdownloader.actions.UserInteractionHandlerAction; import org.lodder.subtools.multisubdownloader.cli.CliOption; import org.lodder.subtools.multisubdownloader.cli.actions.CliSearchAction; -import org.lodder.subtools.multisubdownloader.cli.progress.CLIFileindexerProgress; +import org.lodder.subtools.multisubdownloader.cli.progress.CLIFileIndexerProgress; import org.lodder.subtools.multisubdownloader.cli.progress.CLISearchProgress; import org.lodder.subtools.multisubdownloader.exceptions.CliException; import org.lodder.subtools.multisubdownloader.exceptions.SearchSetupException; @@ -24,7 +23,6 @@ import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; -import org.lodder.subtools.multisubdownloader.util.CLIExtension; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.ManagerException; @@ -33,16 +31,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ CLIExtension.class }) public class CLI { private static final Logger LOGGER = LoggerFactory.getLogger(CLI.class); private final Container app; private final Settings settings; - private final Manager manager; private boolean recursive = false; private Language language; private boolean force = false; @@ -57,17 +51,17 @@ public class CLI { public CLI(SettingsControl settingControl, Container app) { this.app = app; this.settings = settingControl.getSettings(); - this.manager = (Manager) this.app.make("Manager"); + Manager manager = (Manager) this.app.make("Manager"); checkUpdate(manager); UserInteractionHandlerCLI userInteractionHandler = new UserInteractionHandlerCLI(settings); userInteractionHandlerAction = new UserInteractionHandlerAction(settings, userInteractionHandler); - downloadAction = new DownloadAction(settings, (Manager) this.app.make("Manager"), userInteractionHandler); + downloadAction = new DownloadAction(settings, manager, userInteractionHandler); } private void checkUpdate(Manager manager) { UpdateAvailableGithub u = new UpdateAvailableGithub(manager, settings); - if (u.shouldCheckForNewUpdate(settings.getUpdateCheckPeriod()) && u.isNewVersionAvailable()) { - System.out.println(Messages.getString("UpdateAppAvailable") + ": " + u.getLatestDownloadUrl()); + if (u.shouldCheckForNewUpdate(settings.updateCheckPeriod) && u.isNewVersionAvailable()) { + System.out.println(Messages.getText("UpdateAppAvailable") + ": " + u.getLatestDownloadUrl()); } } @@ -80,7 +74,7 @@ public void setUp(CommandLine line) throws CliException { this.subtitleSelection = line.hasCliOption(CliOption.SELECTION); this.verboseProgress = line.hasCliOption(CliOption.VERBOSE_PROGRESS); this.dryRun = line.hasCliOption(CliOption.DRY_RUN); - Messages.setLanguage(language); + Messages.language = language; } public void run() { @@ -95,7 +89,8 @@ public void download(List releases) { try { this.download(release); } catch (Exception e) { - LOGGER.error("Error while downloading subtitle for %s (%s)".formatted(release.getReleaseDescription(), e.getMessage()), e); + LOGGER.error("Error while downloading subtitle for ${release.releaseDescription} (%${e.getMessage()})", + e); } } } @@ -104,9 +99,8 @@ public void search() { try { CliSearchAction .createWithSettings(settings) - .manager(manager) .subtitleProviderStore((SubtitleProviderStore) app.make("SubtitleProviderStore")) - .indexingProgressListener(new CLIFileindexerProgress().verbose(verboseProgress)) + .indexingProgressListener(new CLIFileIndexerProgress().verbose(verboseProgress)) .searchProgressListener(new CLISearchProgress().verbose(verboseProgress)) .cli(this) .fileListAction(new FileListAction(this.settings)) @@ -129,20 +123,22 @@ private void download(Release release) { if (downloadAll) { selection = release.getMatchingSubs(); if (!selection.isEmpty()) { - System.out.println("Downloading ALL found subtitles for release: " + release.getFileName()); + System.out.println("Downloading ALL found subtitles for release: ${release.fileName}"); } } else { selection = userInteractionHandlerAction.subtitleSelection(release, subtitleSelection, dryRun); } if (selection.isEmpty()) { - System.out.println("No subtitles found for: " + release.getFileName()); + System.out.println("No subtitles found for: ${release.fileName}"); } else { IntStream.range(0, selection.size()).forEach(j -> { - System.out.println("Downloading subtitle: " + release.getMatchingSubs().get(0).getFileName()); + System.out.println("Downloading subtitle: " + release.matchingSubs.get(j).fileName); try { - downloadAction.download(release, release.getMatchingSubs().get(j), selection.size() == 1 ? null : j + 1); + downloadAction.download(release, release.matchingSubs.get(j), selection.size() == 1 ? null : j + 1); } catch (IOException | ManagerException e) { - LOGGER.error("Error while downloading subtitle for %s (%s)".formatted(release.getReleaseDescription(), e.getMessage()), e); + LOGGER.error( + "Error while downloading subtitle for ${release.releaseDescription} (${e.getMessage()})", + e); } }); } @@ -152,15 +148,17 @@ private List getFolders(CommandLine line) { if (line.hasCliOption(CliOption.FOLDER)) { return List.of(Path.of(line.getCliOptionValue(CliOption.FOLDER))); } else { - return new ArrayList<>(this.settings.getDefaultFolders()); + return new ArrayList<>(this.settings.defaultFolders); } } private Language getLanguage(CommandLine line) throws CliException { if (line.hasCliOption(CliOption.LANGUAGE)) { String languageString = line.getCliOptionValue(CliOption.LANGUAGE); - return Arrays.stream(Language.values()).filter(lang -> lang.name().equalsIgnoreCase(languageString)).findAny() - .orElseThrow(() -> new CliException(Messages.getString("App.NoValidLanguage"))); + return Language.values().stream() + .filter(lang -> lang.name().equalsIgnoreCase(languageString)) + .findAny() + .orElseThrow(() -> new CliException(Messages.getText("App.NoValidLanguage"))); } else { return Language.ENGLISH; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java index 8d377771..6617ce59 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader; +import static org.lodder.subtools.multisubdownloader.Messages.*; +import static org.lodder.subtools.multisubdownloader.gui.extra.table.SearchColumnName.*; + import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; @@ -17,7 +20,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; -import lombok.experimental.ExtensionMethod; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.multisubdownloader.gui.Menu; @@ -61,17 +63,13 @@ import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.VideoType; -import org.lodder.subtools.sublibrary.util.FileUtils; -import org.lodder.subtools.sublibrary.util.StringUtil; import org.lodder.subtools.sublibrary.util.TriConsumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({ FileUtils.class }) public class GUI extends JFrame implements PropertyChangeListener { - @Serial - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; private final Container app; private final Manager manager; private final Settings settings; @@ -95,7 +93,7 @@ public GUI(final SettingsControl settingsControl, Container app) { this.app = app; this.manager = (Manager) this.app.make("Manager"); this.settings = (Settings) this.app.make("Settings"); - this.userInteractionHandler = new UserInteractionHandlerGUI(settingsControl.getSettings(), this); + this.userInteractionHandler = new UserInteractionHandlerGUI(settingsControl.settings, this); setTitle(ConfigProperties.getInstance().getProperty("name")); /* * setIconImage(Toolkit.getDefaultToolkit().getImage( @@ -104,8 +102,8 @@ public GUI(final SettingsControl settingsControl, Container app) { this.settingsControl = settingsControl; initialize(); restoreScreenSettings(); - pnlSearchFile.getResultPanel().disableButtons(); - pnlSearchText.getResultPanel().disableButtons(); + pnlSearchFile.resultPanel.disableButtons(); + pnlSearchText.resultPanel.disableButtons(); new Thread(() -> checkUpdate(false)).start(); initPopupMenu(); } @@ -119,20 +117,21 @@ public void redraw() { private void checkUpdate(final boolean forceUpdateCheck) { UpdateAvailableGithub u = new UpdateAvailableGithub(manager, settings); - Optional updateUrl = (forceUpdateCheck && u.isNewVersionAvailable()) - || (!forceUpdateCheck && u.shouldCheckForNewUpdate(settingsControl.getSettings().getUpdateCheckPeriod()) - && u.isNewVersionAvailable()) ? u.getLatestDownloadUrl() : Optional.empty(); + Optional updateUrl = (forceUpdateCheck && u.isNewVersionAvailable()) || + (!forceUpdateCheck && u.shouldCheckForNewUpdate(settingsControl.settings.updateCheckPeriod) && + u.isNewVersionAvailable()) ? u.getLatestDownloadUrl() : Optional.empty(); if (updateUrl.isPresent()) { final JEditorPane editorPane = new JEditorPane(); editorPane.setPreferredSize(new Dimension(800, 50)); editorPane.setEditable(false); editorPane.setContentType("text/html"); - editorPane.setText("" + Messages.getString("UpdateAppAvailable") + "!:
" + updateUrl.get() + ""); + editorPane.setText("" + getText("UpdateAppAvailable") + "!:
" + + updateUrl.get() + ""); editorPane.addHyperlinkListener(hyperlinkEvent -> { - if (hyperlinkEvent.getEventType() == HyperlinkEvent.EventType.ACTIVATED && Desktop.isDesktopSupported()) { + if (hyperlinkEvent.getEventType() == HyperlinkEvent.EventType.ACTIVATED && + Desktop.isDesktopSupported()) { try { Desktop.getDesktop().browse(hyperlinkEvent.getURL().toURI()); } catch (Exception e) { @@ -140,9 +139,10 @@ private void checkUpdate(final boolean forceUpdateCheck) { } } }); - JOptionPane.showMessageDialog(this, editorPane, ConfigProperties.getInstance().getProperty("name"), JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, editorPane, ConfigProperties.getInstance().getProperty("name"), + JOptionPane.INFORMATION_MESSAGE); } else if (forceUpdateCheck) { - JOptionPane.showMessageDialog(this, Messages.getString("MainWindow.NoUpdateAvailable"), + JOptionPane.showMessageDialog(this, getText("MainWindow.NoUpdateAvailable"), ConfigProperties.getInstance().getProperty("name"), JOptionPane.INFORMATION_MESSAGE); } @@ -152,7 +152,7 @@ private void checkUpdate(final boolean forceUpdateCheck) { * Initialize the contents of the frame. */ private void initialize() { - MemoryFolderChooser.getInstance().setMemory(settingsControl.getSettings().getLastOutputDir()); + MemoryFolderChooser.getInstance().setMemory(settingsControl.settings.lastOutputDir); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { @@ -162,73 +162,77 @@ public void windowClosing(WindowEvent e) { setBounds(100, 100, 925, 680); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); final GridBagLayout gridBagLayout = new GridBagLayout(); - gridBagLayout.columnWidths = new int[] { 448, 0 }; - gridBagLayout.rowHeights = new int[] { 0, 125, 15, 0 }; - gridBagLayout.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gridBagLayout.rowWeights = new double[] { 1.0, 1.0, 0.0, Double.MIN_VALUE }; + gridBagLayout.columnWidths = new int[]{ 448, 0 }; + gridBagLayout.rowHeights = new int[]{ 0, 125, 15, 0 }; + gridBagLayout.columnWeights = new double[]{ 1.0, Double.MIN_VALUE }; + gridBagLayout.rowWeights = new double[]{ 1.0, 1.0, 0.0, Double.MIN_VALUE }; getContentPane().setLayout(gridBagLayout); JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP); - GridBagConstraints gbc_tabbedPane = new GridBagConstraints(); - gbc_tabbedPane.insets = new Insets(0, 0, 5, 0); - gbc_tabbedPane.fill = GridBagConstraints.BOTH; - gbc_tabbedPane.gridx = 0; - gbc_tabbedPane.gridy = 0; - getContentPane().add(tabbedPane, gbc_tabbedPane); + GridBagConstraints gbcTabbedPane = new GridBagConstraints(); + gbcTabbedPane.insets = new Insets(0, 0, 5, 0); + gbcTabbedPane.fill = GridBagConstraints.BOTH; + gbcTabbedPane.gridx = 0; + gbcTabbedPane.gridy = 0; + getContentPane().add(tabbedPane, gbcTabbedPane); createFileSearchPanel(); - tabbedPane.addTab(Messages.getString("MainWindow.SearchOnFile"), null, pnlSearchFile, null); + tabbedPane.addTab(getText("MainWindow.SearchOnFile"), null, pnlSearchFile, null); createTextSearchPanel(); - tabbedPane.addTab(Messages.getString("MainWindow.SearchOnName"), null, pnlSearchText, null); + tabbedPane.addTab(getText("MainWindow.SearchOnName"), null, pnlSearchText, null); pnlLogging = new LoggingPanel(); - final GridBagConstraints gbc_pnlLogging = new GridBagConstraints(); - gbc_pnlLogging.fill = GridBagConstraints.BOTH; - gbc_pnlLogging.insets = new Insets(0, 0, 5, 0); - gbc_pnlLogging.gridx = 0; - gbc_pnlLogging.gridy = 1; - getContentPane().add(pnlLogging, gbc_pnlLogging); + final GridBagConstraints gbcPnlLogging = new GridBagConstraints(); + gbcPnlLogging.fill = GridBagConstraints.BOTH; + gbcPnlLogging.insets = new Insets(0, 0, 5, 0); + gbcPnlLogging.gridx = 0; + gbcPnlLogging.gridy = 1; + getContentPane().add(pnlLogging, gbcPnlLogging); StatusLabel lblStatus = new StatusLabel(""); StatusMessenger.instance.addListener(lblStatus); - final GridBagConstraints gbc_lblStatus = new GridBagConstraints(); - gbc_lblStatus.anchor = GridBagConstraints.SOUTHWEST; - gbc_lblStatus.gridx = 0; - gbc_lblStatus.gridy = 2; - getContentPane().add(lblStatus, gbc_lblStatus); + final GridBagConstraints gbcLblStatus = new GridBagConstraints(); + gbcLblStatus.anchor = GridBagConstraints.SOUTHWEST; + gbcLblStatus.gridx = 0; + gbcLblStatus.gridy = 2; + getContentPane().add(lblStatus, gbcLblStatus); createMenu(); setJMenuBar(menuBar); } private void createMenu() { - Settings settings = settingsControl.getSettings(); - BiConsumer visibilityFunction = pnlSearchFile.getResultPanel().getTable()::setColumnVisibility; + Settings settings = settingsControl.settings; + BiConsumer visibilityFunction = + pnlSearchFile.resultPanel.getTable()::setColumnVisibility; BiConsumer showRenameDialog = - (videoType, title) -> new RenameDialog(self(), settings, videoType, title, manager, userInteractionHandler).setVisible(true); + (videoType, title) -> new RenameDialog(self(), settings, videoType, title, manager, + userInteractionHandler).setVisible(true); ExportImport exportImport = new ExportImport(manager, settingsControl, userInteractionHandler, this); - menuBar = new Menu() - .withShowOnlyFound(settings.isOptionsShowOnlyFound()) + menuBar = new Menu().withShowOnlyFound(settings.optionsShowOnlyFound) .withFileQuitAction(this::close) - .withViewFilenameAction(() -> visibilityFunction.accept(SearchColumnName.FILENAME, menuBar.isViewFilenameSelected())) - .withViewTypeAction(() -> visibilityFunction.accept(SearchColumnName.TYPE, menuBar.isViewTypeSelected())) - .withViewTitleAction(() -> visibilityFunction.accept(SearchColumnName.TITLE, menuBar.isViewTitleSelected())) - .withViewSeasonAction(() -> visibilityFunction.accept(SearchColumnName.SEASON, menuBar.isViewSeasonSelected())) - .withViewEpisodeAction(() -> visibilityFunction.accept(SearchColumnName.EPISODE, menuBar.isViewEpisodeSelected())) + .withViewFilenameAction(() -> visibilityFunction.accept(FILENAME, menuBar.isViewFilenameSelected())) + .withViewTypeAction(() -> visibilityFunction.accept(TYPE, menuBar.isViewTypeSelected())) + .withViewTitleAction(() -> visibilityFunction.accept(TITLE, menuBar.isViewTitleSelected())) + .withViewSeasonAction(() -> visibilityFunction.accept(SEASON, menuBar.isViewSeasonSelected())) + .withViewEpisodeAction(() -> visibilityFunction.accept(EPISODE, menuBar.isViewEpisodeSelected())) .withViewShowOnlyFoundAction(() -> { - settings.setOptionsShowOnlyFound(menuBar.isShowOnlyFound()); - ((VideoTableModel) pnlSearchFile.getResultPanel().getTable().getModel()).setShowOnlyFound(menuBar.isShowOnlyFound()); + settings.optionsShowOnlyFound = menuBar.isShowOnlyFound(); + ((VideoTableModel) pnlSearchFile.resultPanel.getTable().getModel()) + .setShowOnlyFound(menuBar.isShowOnlyFound()); }) .withViewClearLogAction(() -> pnlLogging.setLogText("")) - .withEditRenameTVAction(() -> showRenameDialog.accept(VideoType.EPISODE, Messages.getString("Menu.RenameSerie"))) - .withEditRenameMovieAction(() -> showRenameDialog.accept(VideoType.MOVIE, Messages.getString("Menu.RenameMovie"))) + .withEditRenameTVAction(() -> showRenameDialog.accept(VideoType.EPISODE, getText("Menu.RenameSerie"))) + .withEditRenameMovieAction(() -> showRenameDialog.accept(VideoType.MOVIE, getText("Menu.RenameMovie"))) .withEditPreferencesAction( - () -> new PreferenceDialog(self(), settingsControl, (Emitter) app.make("EventEmitter"), manager, userInteractionHandler) - .setVisible(true)) + () -> new PreferenceDialog(self(), settingsControl, (Emitter) app.make("EventEmitter"), manager, + userInteractionHandler).setVisible(true)) .withTranslateShowNamesAction(this::showTranslateShowNames) - .withExportTranslationsAction(() -> exportImport.exportSettings(ExportImport.SettingsType.SERIE_MAPPING)) - .withImportTranslationsAction(() -> exportImport.importSettings(ExportImport.SettingsType.SERIE_MAPPING)) + .withExportTranslationsAction( + () -> exportImport.exportSettings(ExportImport.SettingsType.SERIE_MAPPING)) + .withImportTranslationsAction( + () -> exportImport.importSettings(ExportImport.SettingsType.SERIE_MAPPING)) .withExportPreferencesAction(() -> exportImport.exportSettings(ExportImport.SettingsType.PREFERENCES)) .withImportPreferencesAction(() -> exportImport.importSettings(ExportImport.SettingsType.PREFERENCES)) .withCheckUpdateAction(() -> checkUpdate(true)) @@ -236,20 +240,20 @@ private void createMenu() { } private void createTextSearchPanel() { - Settings settings = this.settingsControl.getSettings(); + Settings settings = this.settingsControl.settings; /* resolve the SubtitleProviderStore from the Container */ SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) this.app.make("SubtitleProviderStore"); ResultPanel resultPanel = new ResultPanel(); SearchTextInputPanel pnlSearchTextInput = new SearchTextInputPanel(); pnlSearchText = new SearchPanel<>(pnlSearchTextInput, resultPanel); - pnlSearchTextInput.setSelectedlanguage(settings.getSubtitleLanguage() == null ? Language.DUTCH : settings.getSubtitleLanguage()); + pnlSearchTextInput.setSelectedLanguage( + settings.subtitleLanguage == null ? Language.DUTCH : settings.subtitleLanguage); resultPanel.showSelectFoundSubtitlesButton(); resultPanel.setTable(createSubtitleTable()); - resultPanel.setDownloadAction(arg -> downloadText()); + resultPanel.setDownloadAction(_ -> downloadText()); TextGuiSearchAction searchAction = TextGuiSearchAction.createWithSettings(settings) - .manager(manager) .subtitleProviderStore(subtitleProviderStore) .mainWindow(this) .searchPanel(pnlSearchText) @@ -268,35 +272,32 @@ private CustomTable createSubtitleTable() { } private void createFileSearchPanel() { - Settings settings = this.settingsControl.getSettings(); + Settings settings = this.settingsControl.settings; ResultPanel resultPanel = new ResultPanel(); pnlSearchFileInput = new SearchFileInputPanel(); - pnlSearchFileInput.setRecursiveSelected(settings.isOptionRecursive()); - pnlSearchFileInput.setSelectedlanguage(settings.getSubtitleLanguage() == null ? Language.DUTCH : settings.getSubtitleLanguage()); + pnlSearchFileInput.setRecursiveSelected(settings.optionRecursive); + pnlSearchFileInput.setSelectedLanguage( + settings.subtitleLanguage == null ? Language.DUTCH : settings.subtitleLanguage); pnlSearchFile = new SearchPanel<>(pnlSearchFileInput, resultPanel); resultPanel.setTable(createVideoTable()); - FileGuiSearchAction searchAction = FileGuiSearchAction - .createWithSettings(settings) - .manager(manager) + FileGuiSearchAction searchAction = FileGuiSearchAction.createWithSettings(settings) .subtitleProviderStore((SubtitleProviderStore) this.app.make("SubtitleProviderStore")) .mainWindow(this) .searchPanel(pnlSearchFile) .releaseFactory(new ReleaseFactory(settings, (Manager) app.make("Manager"))) .build(); - pnlSearchFileInput.addSelectFolderAction(arg -> selectIncomingFolder()); + pnlSearchFileInput.addSelectFolderAction(_ -> selectIncomingFolder()); pnlSearchFileInput.addSearchAction(searchAction); - resultPanel.setDownloadAction(arg -> download()); - resultPanel.setMoveAction(arg -> { - final int response = - JOptionPane.showConfirmDialog( - self(), - Messages.getString("MainWindow.OnlyMoveToLibraryStructure"), Messages.getString("App.Confirm"), //$NON-NLS-2$ - JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + resultPanel.setDownloadAction(_ -> download()); + resultPanel.setMoveAction(_ -> { + final int response = JOptionPane.showConfirmDialog(self(), getText("MainWindow.OnlyMoveToLibraryStructure"), + getText("App.Confirm"), //$NON-NLS-2$ + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (response == JOptionPane.YES_OPTION) { rename(); } @@ -307,8 +308,8 @@ private CustomTable createVideoTable() { CustomTable customTable = new CustomTable(); VideoTableModel videoTableModel = VideoTableModel.getDefaultVideoTableModel(); customTable.setModel(videoTableModel); - videoTableModel.setShowOnlyFound(settingsControl.getSettings().isOptionsShowOnlyFound()); - videoTableModel.setUserInteractionHandler(userInteractionHandler); + videoTableModel.setShowOnlyFound(settingsControl.settings.optionsShowOnlyFound); + videoTableModel.userInteractionHandler = userInteractionHandler; final RowSorter sorter = new TableRowSorter<>(customTable.getModel()); customTable.setRowSorter(sorter); customTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); @@ -330,30 +331,32 @@ private CustomTable createVideoTable() { } private void restoreScreenSettings() { - CustomTable customTable = pnlSearchFile.getResultPanel().getTable(); - TriConsumer> visibilityConsumer = (searchColumn, hidden, setVisibleConsumer) -> { - setVisibleConsumer.accept(!hidden); - customTable.setColumnVisibility(searchColumn, !hidden); - }; - - ScreenSettings screenSettings = settingsControl.getSettings().getScreenSettings(); - - visibilityConsumer.accept(SearchColumnName.EPISODE, screenSettings.isHideEpisode(), menuBar::withViewEpisodeSelected); - visibilityConsumer.accept(SearchColumnName.FILENAME, screenSettings.isHideFilename(), menuBar::withViewFileNameSelected); - visibilityConsumer.accept(SearchColumnName.SEASON, screenSettings.isHideSeason(), menuBar::withViewSeasonSelected); - visibilityConsumer.accept(SearchColumnName.TYPE, screenSettings.isHideType(), menuBar::withViewTypeSelected); - visibilityConsumer.accept(SearchColumnName.TITLE, screenSettings.isHideTitle(), menuBar::withViewTitleSelected); + CustomTable customTable = pnlSearchFile.resultPanel.getTable(); + TriConsumer> visibilityConsumer = + (searchColumn, hidden, setVisibleConsumer) -> { + setVisibleConsumer.accept(!hidden); + customTable.setColumnVisibility(searchColumn, !hidden); + }; + + ScreenSettings screenSettings = settingsControl.settings.screenSettings; + + visibilityConsumer.accept(SearchColumnName.EPISODE, screenSettings.hideEpisode, + menuBar::withViewEpisodeSelected); + visibilityConsumer.accept(FILENAME, screenSettings.hideFilename, menuBar::withViewFileNameSelected); + visibilityConsumer.accept(SearchColumnName.SEASON, screenSettings.hideSeason, menuBar::withViewSeasonSelected); + visibilityConsumer.accept(SearchColumnName.TYPE, screenSettings.hideType, menuBar::withViewTypeSelected); + visibilityConsumer.accept(SearchColumnName.TITLE, screenSettings.hideTitle, menuBar::withViewTitleSelected); } private void initPopupMenu() { popupMenu = new MyPopupMenu(); - JMenuItem menuItem = new JMenuItem(Messages.getString("App.Copy")); - menuItem.addActionListener(arg0 -> { + JMenuItem menuItem = new JMenuItem(getText("App.Copy")); + menuItem.addActionListener(_ -> { final CustomTable t = (CustomTable) popupMenu.getInvoker(); final DefaultTableModel model = (DefaultTableModel) t.getModel(); - int col = t.columnAtPoint(popupMenu.getClickLocation()); - int row = t.rowAtPoint(popupMenu.getClickLocation()); + int col = t.columnAtPoint(popupMenu.clickLocation); + int row = t.rowAtPoint(popupMenu.clickLocation); try { StringSelection selection = new StringSelection((String) model.getValueAt(row, col)); @@ -366,8 +369,8 @@ private void initPopupMenu() { // add the listener to the jtable MouseListener popupListener = new PopupListener(popupMenu); // add the listener specifically to the header - CustomTable customTable = pnlSearchFile.getResultPanel().getTable(); - CustomTable subtitleTable = pnlSearchText.getResultPanel().getTable(); + CustomTable customTable = pnlSearchFile.resultPanel.getTable(); + CustomTable subtitleTable = pnlSearchText.resultPanel.getTable(); customTable.addMouseListener(popupListener); customTable.getTableHeader().addMouseListener(popupListener); subtitleTable.addMouseListener(popupListener); @@ -375,73 +378,76 @@ private void initPopupMenu() { } protected void showTranslateShowNames() { - final MappingEpisodeNameDialog tDialog = new MappingEpisodeNameDialog(this, settingsControl, (Manager) this.app.make("Manager"), - (SubtitleProviderStore) this.app.make("SubtitleProviderStore"), userInteractionHandler); + final MappingEpisodeNameDialog tDialog = + new MappingEpisodeNameDialog(this, settingsControl, (Manager) this.app.make("Manager"), + (SubtitleProviderStore) this.app.make("SubtitleProviderStore"), userInteractionHandler); tDialog.setVisible(true); } private void showAbout() { - String version = ConfigProperties.getInstance().getProperty(Messages.getString("MainWindow.Version")); + String version = ConfigProperties.getInstance().getProperty(getText("MainWindow.Version")); StringBuilder sb = new StringBuilder(); - sb.append(Messages.getString("MainWindow.CurrentVersion")).append(": ").append(version); + sb.append(getText("MainWindow.CurrentVersion")).append(": ").append(version); if (version.contains("-SNAPSHOT")) { sb.append(" (%s)".formatted(PropertiesReader.getProperty("build.timestamp"))); } - JOptionPane.showConfirmDialog(this, sb.toString(), ConfigProperties.getInstance().getProperty("name"), JOptionPane.CLOSED_OPTION); + JOptionPane.showConfirmDialog(this, sb.toString(), ConfigProperties.getInstance().getProperty("name"), + JOptionPane.CLOSED_OPTION); } protected void rename() { - CustomTable customTable = pnlSearchFile.getResultPanel().getTable(); - RenameWorker renameWorker = - new RenameWorker(customTable, settingsControl.getSettings(), (Manager) this.app.make("Manager"), userInteractionHandler); + CustomTable customTable = pnlSearchFile.resultPanel.getTable(); + RenameWorker renameWorker = new RenameWorker(customTable, settingsControl.settings, + (Manager) this.app.make("Manager"), userInteractionHandler); renameWorker.addPropertyChangeListener(this); - pnlSearchFile.getResultPanel().enableButtons(); + pnlSearchFile.resultPanel.enableButtons(); progressDialog = new ProgressDialog(this, renameWorker); progressDialog.setVisible(true); renameWorker.execute(); } private void download() { - CustomTable customTable = pnlSearchFile.getResultPanel().getTable(); - DownloadWorker downloadWorker = new DownloadWorker(customTable, settingsControl.getSettings(), (Manager) this.app.make("Manager"), this); + CustomTable customTable = pnlSearchFile.resultPanel.getTable(); + DownloadWorker downloadWorker = + new DownloadWorker(customTable, settingsControl.settings, (Manager) this.app.make("Manager"), this); downloadWorker.addPropertyChangeListener(this); - pnlSearchFile.getResultPanel().disableButtons(); + pnlSearchFile.resultPanel.disableButtons(); progressDialog = new ProgressDialog(this, downloadWorker); progressDialog.setVisible(true); downloadWorker.execute(); } private void downloadText() { - MemoryFolderChooser.getInstance().selectDirectory(getContentPane(), Messages.getString("MainWindow.SelectFolder")) + MemoryFolderChooser.getInstance() + .selectDirectory(getContentPane(), getText("MainWindow.SelectFolder")) .ifPresent(path -> { - CustomTable subtitleTable = pnlSearchText.getResultPanel().getTable(); + CustomTable subtitleTable = pnlSearchText.resultPanel.getTable(); final VideoTableModel model = (VideoTableModel) subtitleTable.getModel(); for (int i = 0; i < model.getRowCount(); i++) { if ((Boolean) model.getValueAt(i, subtitleTable.getColumnIdByName(SearchColumnName.SELECT))) { - final Subtitle subtitle = (Subtitle) model.getValueAt(i, subtitleTable.getColumnIdByName(SearchColumnName.OBJECT)); + final Subtitle subtitle = (Subtitle) model.getValueAt(i, + subtitleTable.getColumnIdByName(SearchColumnName.OBJECT)); String filename = ""; - if (!subtitle.getFileName().endsWith(".srt")) { - filename = subtitle.getFileName() + ".srt"; + if (!subtitle.fileName.endsWith(".srt")) { + filename = subtitle.fileName + ".srt"; } - if (OsCheck.getOperatingSystemType() == OSType.Windows) { - filename = StringUtil.removeIllegalWindowsChars(filename); + if (OsCheck.operatingSystemType == OSType.WINDOWS) { + filename = filename.removeIllegalWindowsChars(); } try { - if (subtitle.getSourceLocation() == Subtitle.SourceLocation.FILE) { - subtitle.getFile().copyToDir(path); + if (subtitle.sourceLocation == Subtitle.SourceLocation.FILE) { + subtitle.file.copyToDir(path); } else { - Manager manager = (Manager) this.app.make("Manager"); - String url = - subtitle.getSourceLocation() == Subtitle.SourceLocation.URL ? subtitle.getUrl() - : subtitle.getUrlSupplier().get(); - manager.store(url, path.resolve(filename)); + String url = subtitle.sourceLocation == Subtitle.SourceLocation.URL ? subtitle.url : + subtitle.urlSupplier.get(); + ((Manager) this.app.make("Manager")).store(url, path.resolve(filename)); } } catch (IOException | ManagerException e) { LOGGER.error("downloadText", e); } catch (SubtitlesProviderException e) { - LOGGER.error("Error while getting url for [%s] for subtitle provider [%s] (%s)".formatted(filename, - e.getSubtitleProvider(), e.getMessage()), e); + LOGGER.error("Error while getting url for [%s] for subtitle provider [%s] (%s)" + .formatted(filename, e.getSubtitleProvider(), e.getMessage()), e); throw new RuntimeException(e); } } @@ -455,52 +461,55 @@ protected GUI self() { } public void showErrorMessage(String message) { - JOptionPane.showConfirmDialog(this, message, ConfigProperties.getInstance().getProperty("name"), JOptionPane.CLOSED_OPTION, - JOptionPane.ERROR_MESSAGE); + JOptionPane.showConfirmDialog(this, message, ConfigProperties.getInstance().getProperty("name"), + JOptionPane.CLOSED_OPTION, JOptionPane.ERROR_MESSAGE); } private void selectIncomingFolder() { - MemoryFolderChooser.getInstance().selectDirectory(self(), Messages.getString("MainWindow.SelectFolder")) - .map(Path::toAbsolutePath).map(Path::toString).ifPresent(pnlSearchFileInput::setIncomingPath); + MemoryFolderChooser.getInstance() + .selectDirectory(self(), getText("MainWindow.SelectFolder")) + .map(Path::toAbsolutePath) + .map(Path::toString) + .ifPresent(pnlSearchFileInput::setIncomingPath); } @Override public void propertyChange(PropertyChangeEvent event) { if (event.getSource() instanceof DownloadWorker downloadWorker) { if (downloadWorker.isDone()) { - pnlSearchFile.getResultPanel().enableButtons(); + pnlSearchFile.resultPanel.enableButtons(); progressDialog.setVisible(false); } else { final int progress = downloadWorker.getProgress(); progressDialog.updateProgress(progress); - StatusMessenger.instance.message(Messages.getString("MainWindow.StatusDownload")); + StatusMessenger.instance.message(getText("MainWindow.StatusDownload")); } } else if (event.getSource() instanceof RenameWorker renameWorker) { if (renameWorker.isDone()) { - pnlSearchFile.getResultPanel().enableButtons(); + pnlSearchFile.resultPanel.enableButtons(); progressDialog.setVisible(false); } else { final int progress = renameWorker.getProgress(); progressDialog.updateProgress(progress); - StatusMessenger.instance.message(Messages.getString("MainWindow.StatusRename")); + StatusMessenger.instance.message(getText("MainWindow.StatusRename")); } } } private void close() { - settingsControl.getSettings().setOptionRecursive(pnlSearchFileInput.isRecursiveSelected()); - settingsControl.getSettings().setSubtitleLanguage(pnlSearchFileInput.getSelectedLanguage()); + settingsControl.settings.optionRecursive = pnlSearchFileInput.isRecursiveSelected(); + settingsControl.settings.subtitleLanguage = pnlSearchFileInput.getSelectedLanguage(); storeScreenSettings(); settingsControl.store(); } private void storeScreenSettings() { - CustomTable customTable = pnlSearchFile.getResultPanel().getTable(); - settingsControl.getSettings().getScreenSettings().setHideEpisode(customTable.isHideColumn(SearchColumnName.EPISODE)); - settingsControl.getSettings().getScreenSettings().setHideFilename(customTable.isHideColumn(SearchColumnName.FILENAME)); - settingsControl.getSettings().getScreenSettings().setHideSeason(customTable.isHideColumn(SearchColumnName.SEASON)); - settingsControl.getSettings().getScreenSettings().setHideTitle(customTable.isHideColumn(SearchColumnName.TITLE)); - settingsControl.getSettings().getScreenSettings().setHideType(customTable.isHideColumn(SearchColumnName.TYPE)); + CustomTable customTable = pnlSearchFile.resultPanel.getTable(); + settingsControl.settings.screenSettings.hideEpisode = customTable.isHideColumn(SearchColumnName.EPISODE); + settingsControl.settings.screenSettings.hideFilename = customTable.isHideColumn(FILENAME); + settingsControl.settings.screenSettings.hideSeason = customTable.isHideColumn(SearchColumnName.SEASON); + settingsControl.settings.screenSettings.hideTitle = customTable.isHideColumn(SearchColumnName.TITLE); + settingsControl.settings.screenSettings.hideType = customTable.isHideColumn(SearchColumnName.TYPE); } public ProgressDialog setProgressDialog(Cancelable worker) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java index 4cb427ca..ab3a0449 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java @@ -11,6 +11,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import lombok.RequiredArgsConstructor; import org.jsoup.nodes.Element; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.settings.model.UpdateCheckPeriod; @@ -22,16 +23,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.RequiredArgsConstructor; - @RequiredArgsConstructor public class UpdateAvailableGithub { private static final Logger LOGGER = LoggerFactory.getLogger(UpdateAvailableGithub.class); - private final static String DOMAIN = "https://github.com"; - private final static String REPO_URI = "/phdelodder/SubTools"; - private final static String REPO_URL = DOMAIN + REPO_URI; + private static final String DOMAIN = "https://github.com"; + private static final String REPO_URI = "/phdelodder/SubTools"; + private static final String REPO_URL = DOMAIN + REPO_URI; private final Manager manager; private final Settings settings; @@ -52,14 +51,14 @@ public boolean shouldCheckForNewUpdate(UpdateCheckPeriod updateCheckPeriod) { } public Optional getLatestDownloadUrl() { - return switch (settings.getUpdateType()) { + return switch (settings.updateType) { case STABLE -> getUrlLatestNewStableGithubRelease(); case NIGHTLY -> getUrlLatestNewNightlyGithubRelease(); }; } public boolean isNewVersionAvailable() { - return switch (settings.getUpdateType()) { + return switch (settings.updateType) { case STABLE -> getUrlLatestNewStableGithubRelease().isPresent(); case NIGHTLY -> getUrlLatestNewNightlyGithubRelease().isPresent(); }; @@ -76,9 +75,9 @@ private Optional getUrlLatestNewStableGithubRelease() { .userAgent(null) .cacheType(CacheType.NONE) .getAsJsoupDocument() - .selectFirst("#repo-content-turbo-frame .box a[href='" + REPO_URI + "/releases/latest']"); - Pattern versionPattern = Pattern.compile("[0-9]*\\.[0-9]\\.[0-9]"); - String versionText = element.parent().selectFirst("a").text(); + .selectFirstByCss("#repo-content-turbo-frame .box a[href='$REPO_URI/releases/latest']"); + Pattern versionPattern = Pattern.compile("\\d*\\.\\d\\.\\d"); + String versionText = element.getParent().selectFirstByCss("a").getText(); Matcher matcher = versionPattern.matcher(versionText); matcher.find(); String version = matcher.group(); @@ -90,12 +89,16 @@ private Optional getUrlLatestNewStableGithubRelease() { .userAgent(null) .cacheType(CacheType.NONE) .getAsJsoupDocument() - .selectFirst(".Box-row a[href$='.jar']"); - String url = DOMAIN + artifactElement.attr("href"); + .selectFirstByCss(".Box-row a[href$='.jar']"); + String url = DOMAIN + artifactElement.getAttr("href"); updateLastUpdateCheck(); return Optional.of(url); } catch (Exception e) { - LOGGER.error(Messages.getString("LoggingPanel.UpdateCheckFailed")); + if (LOGGER.isTraceEnabled) { + LOGGER.trace(Messages.getText("LoggingPanel.UpdateCheckFailed"), e); + } else { + LOGGER.error(Messages.getText("LoggingPanel.UpdateCheckFailed")); + } return Optional.empty(); } }).getOptional(); @@ -109,23 +112,33 @@ private Optional getUrlLatestNewNightlyGithubRelease() { try { LocalDateTime buildTista = getBuildTista(); - Element rowElement = manager.getPageContentBuilder().url(REPO_URL + "/actions?query=branch%3Amaster") - .userAgent(null) - .cacheType(CacheType.MEMORY) - .getAsJsoupDocument() - .selectFirst("#partial-actions-workflow-runs .Box-row"); - LocalDateTime nightlyBuildTista = - zonedDateTimeStringToLocalDateTime(rowElement.selectFirst(".d-inline relative-time").attr("datetime")); + Element rowElement = + manager.getPageContentBuilder().url(REPO_URL + "/actions?query=branch%3Amaster") + .userAgent(null) + .cacheType(CacheType.MEMORY) + .getAsJsoupDocument() + .selectFirstByCss("#partial-actions-workflow-runs .Box-row"); + LocalDateTime nightlyBuildTista = zonedDateTimeStringToLocalDateTime( + rowElement.selectFirstByCss(".d-inline relative-time").getAttr("datetime")); if (nightlyBuildTista.isBefore(buildTista)) { return Optional.empty(); } - String url = "https://nightly.link" + rowElement.selectFirst(".Link--primary").attr("href"); - String downloadUrl = manager.getPageContentBuilder().url(url).cacheType(CacheType.MEMORY).getAsJsoupDocument() - .selectFirst("table td a").attr("href"); + String url = + "https://nightly.link" + rowElement.selectFirstByCss(".Link--primary").getAttr("href"); + String downloadUrl = manager.getPageContentBuilder() + .url(url) + .cacheType(CacheType.MEMORY) + .getAsJsoupDocument() + .selectFirstByCss("table td a") + .getAttr("href"); updateLastUpdateCheck(); return Optional.of(downloadUrl); } catch (Exception e) { - LOGGER.error(Messages.getString("LoggingPanel.UpdateCheckFailed")); + if (LOGGER.isTraceEnabled) { + LOGGER.trace(Messages.getText("LoggingPanel.UpdateCheckFailed"), e); + } else { + LOGGER.error(Messages.getText("LoggingPanel.UpdateCheckFailed")); + } return Optional.empty(); } }).getOptional(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java index d366f1d4..8f6ca21c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java @@ -8,13 +8,13 @@ public interface UserInteractionHandler extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler { default List getAutomaticSelection(List subtitles) { - List shortlist = !getSettings().isOptionsMinAutomaticSelection() ? subtitles : + List shortlist = !settings.optionsMinAutomaticSelection ? subtitles : subtitles.stream() - .filter(subtitle -> subtitle.getScore() >= getSettings().getOptionsMinAutomaticSelectionValue()) + .filter(subtitle -> subtitle.score >= settings.optionsMinAutomaticSelectionValue) .toList(); - if (getSettings().isOptionsDefaultSelection()) { - List defaultSelectionsFound = getSettings().getOptionsDefaultSelectionQualityList().stream() - .flatMap(q -> shortlist.stream().filter(subtitle -> q.isTypeForValue(subtitle.getQuality()))) + if (settings.optionsDefaultSelection) { + List defaultSelectionsFound = settings.optionsDefaultSelectionQualityList.stream() + .flatMap(q -> shortlist.stream().filter(subtitle -> q.isTypeForValue(subtitle.quality))) .distinct().toList(); if (!defaultSelectionsFound.isEmpty()) { return defaultSelectionsFound; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java index 8e393db3..12a69101 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java @@ -43,23 +43,26 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @Override public List selectSubtitles(Release release) { - System.out.printf("\n%s : %s%n", Messages.getString("SelectDialog.SelectCorrectSubtitleThisRelease"), release.getFileName()); + System.out.printf("\n%s : %s%n", Messages.getText("SelectDialog.SelectCorrectSubtitleThisRelease"), + release.fileName); return PrompterUtil .getElementsFromList(release.getMatchingSubs()) .displayAsTable(createTableDisplayer()) - .message(Messages.getString("SelectDialog.EnterListSelectedSubtitles")) + .message(Messages.getText("SelectDialog.EnterListSelectedSubtitles")) .sort(Comparator.comparing(Subtitle::getScore)) .includeNull() .prompt(prompter); } - private ColumnDisplayer createSubtitleDisplayer(SubtitleTableColumnName column, Function toStringMapper) { - return new ColumnDisplayer<>(column.getColumnName(), (Subtitle s) -> String.valueOf(toStringMapper.apply(s))); + private ColumnDisplayer createSubtitleDisplayer(SubtitleTableColumnName column, + Function toStringMapper) { + return new ColumnDisplayer<>(column.columnName, subtitle -> String.valueOf(toStringMapper.apply(subtitle))); } private TableDisplayer createTableDisplayer() { - return new TableDisplayer<>(Stream.of(SCORE, FILENAME, RELEASEGROUP, QUALITY, SOURCE, UPLOADER, HEARINGIMPAIRED) - .map(stcn -> createSubtitleDisplayer(stcn, stcn.getValueFunction())).toList()); + return new TableDisplayer<>(Stream.of(SCORE, FILENAME, RELEASEGROUP, QUALITY, SOURCE, UPLOADER, + HEARINGIMPAIRED) + .map(stcn -> createSubtitleDisplayer(stcn, stcn.valueFunction)).toList()); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java index e590e366..21197b26 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java @@ -1,18 +1,17 @@ package org.lodder.subtools.multisubdownloader; +import javax.swing.*; import java.util.List; -import javax.swing.JFrame; - +import lombok.Getter; import org.lodder.subtools.multisubdownloader.gui.dialog.SelectDialog; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -import lombok.Getter; - @Getter -public class UserInteractionHandlerGUI extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandlerGUI implements UserInteractionHandler { +public class UserInteractionHandlerGUI extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandlerGUI + implements UserInteractionHandler { public UserInteractionHandlerGUI(UserInteractionSettingsIntf settings, JFrame frame) { super(settings, frame); @@ -20,7 +19,7 @@ public UserInteractionHandlerGUI(UserInteractionSettingsIntf settings, JFrame fr @Override public List selectSubtitles(Release release) { - List selection = new SelectDialog(getFrame(), release.getMatchingSubs(), release).getSelection(); + List selection = new SelectDialog(frame, release.getMatchingSubs(), release).getSelection(); return selection.stream().map(i -> release.getMatchingSubs().get(i)).toList(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java index a778af81..739da2fa 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java @@ -6,28 +6,23 @@ import java.nio.file.StandardCopyOption; import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.experimental.ExtensionMethod; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.util.FileUtils; -import org.lodder.subtools.sublibrary.util.StreamExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ StringUtils.class, FileUtils.class, Files.class, StreamExtension.class }) +@RequiredArgsConstructor +@ExtensionMethod({ Files.class }) public class CleanAction { private static final Logger LOGGER = LoggerFactory.getLogger(CleanAction.class); + private static final String SAMPLE_DIR_NAME = "sample"; + private static final Set FILE_FILTERS = Set.of("nfo", "jpg", "sfv", "srr", "srs", "nzb", "torrent", "txt"); private final LibrarySettings librarySettings; - private final Set fileFilters = Set.of("nfo", "jpg", "sfv", "srr", "srs", "nzb", "torrent", "txt"); - private final static String sampleDirName = "sample"; - - public CleanAction(LibrarySettings librarySettings) { - this.librarySettings = librarySettings; - } public void cleanUpFiles(Release release, Path destination, String videoFileName) throws IOException { LOGGER.trace("cleanUpFiles: LibraryOtherFileAction {}", librarySettings.getLibraryOtherFileAction()); @@ -36,23 +31,25 @@ public void cleanUpFiles(Release release, Path destination, String videoFileName } release.getPath().list().asThrowingStream(IOException.class) - .filter(p -> (p.isDirectory() && p.fileNameContainsIgnoreCase(sampleDirName)) - || (p.isRegularFile() && fileFilters.contains(p.getExtension()))) + .filter(p -> (p.isDirectory() && p.fileNameContainsIgnoreCase(SAMPLE_DIR_NAME)) + || (p.isRegularFile() && FILE_FILTERS.contains(p.getExtension()))) .forEach(p -> { - switch (librarySettings.getLibraryOtherFileAction()) { + switch (librarySettings.libraryOtherFileAction) { case MOVE -> move(p, destination); case MOVEANDRENAME -> moveAndRename(p, destination, videoFileName); case REMOVE -> delete(p); case RENAME -> rename(p, destination, videoFileName); - case NOTHING -> {} - default -> {} + case NOTHING -> { } + default -> { } } }); } private void rename(Path path, Path destinationFolder, String videoFileName) throws IOException { if (path.isRegularFile()) { - String fileName = path.fileNameContainsIgnoreCase(sampleDirName) ? sampleDirName : StringUtils.substringBeforeLast(videoFileName, "."); + String fileName = + path.fileNameContainsIgnoreCase(SAMPLE_DIR_NAME) ? SAMPLE_DIR_NAME : + StringUtils.substringBeforeLast(videoFileName, "."); String extension = path.getExtension(); if (!extension.isBlank()) { extension = "." + extension; @@ -64,12 +61,14 @@ private void rename(Path path, Path destinationFolder, String videoFileName) thr } private void delete(Path path) throws IOException { - FileUtils.delete(path); + path.deletePath(); } private void moveAndRename(Path path, Path destinationFolder, String videoFileName) throws IOException { if (path.isRegularFile()) { - String fileName = path.fileNameContainsIgnoreCase(sampleDirName) ? sampleDirName : StringUtils.substringBeforeLast(videoFileName, "."); + String fileName = + path.fileNameContainsIgnoreCase(SAMPLE_DIR_NAME) ? SAMPLE_DIR_NAME : + StringUtils.substringBeforeLast(videoFileName, "."); String extension = path.getExtension(); if (!extension.isBlank()) { extension = "." + extension; @@ -81,7 +80,7 @@ private void moveAndRename(Path path, Path destinationFolder, String videoFileNa } private void move(Path origin, Path destinationFolder) throws IOException { - FileUtils.moveToDir(origin, destinationFolder, StandardCopyOption.REPLACE_EXISTING); + origin.moveToDir(destinationFolder, StandardCopyOption.REPLACE_EXISTING); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java index 51e9d507..5ae56bff 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java @@ -4,6 +4,8 @@ import java.nio.file.Files; import java.nio.file.Path; +import lombok.RequiredArgsConstructor; +import lombok.experimental.ExtensionMethod; import org.lodder.subtools.multisubdownloader.lib.library.FilenameLibraryBuilder; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; @@ -17,14 +19,10 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ FileUtils.class, Files.class }) +@ExtensionMethod({ Files.class }) @RequiredArgsConstructor public class DownloadAction { @@ -35,15 +33,15 @@ public class DownloadAction { private final UserInteractionHandler userInteractionHandler; public void download(Release release, Subtitle subtitle, Integer version) throws IOException, ManagerException { - switch (release.getVideoType()) { - case EPISODE -> download(release, subtitle, settings.getEpisodeLibrarySettings(), version); - case MOVIE -> download(release, subtitle, settings.getMovieLibrarySettings(), version); - default -> throw new IllegalArgumentException("Unexpected value: " + release.getVideoType()); + switch (release.videoType) { + case EPISODE -> download(release, subtitle, settings.episodeLibrarySettings, version); + case MOVIE -> download(release, subtitle, settings.movieLibrarySettings, version); + default -> throw new IllegalArgumentException("Unexpected value: " + release.videoType); } } public void download(Release release, Subtitle subtitle) throws IOException, ManagerException { - LOGGER.info("Downloading subtitle: [{}] for release: [{}]", subtitle.getFileName(), release.getFileName()); + LOGGER.info("Downloading subtitle: [{}] for release: [{}]", subtitle.fileName, release.fileName); download(release, subtitle, 0); } @@ -66,25 +64,25 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar Path subFile = path.resolve(subFileName); boolean success; - if (subtitle.getSourceLocation() == Subtitle.SourceLocation.FILE) { - subtitle.getFile().copyToDir(path); + if (subtitle.sourceLocation == Subtitle.SourceLocation.FILE) { + subtitle.file.copyToDir(path); success = true; } else { String url; try { - url = subtitle.getSourceLocation() == Subtitle.SourceLocation.URL ? subtitle.getUrl() : subtitle.getUrlSupplier().get(); + url = subtitle.sourceLocation == Subtitle.SourceLocation.URL ? subtitle.url : subtitle.urlSupplier.get(); success = manager.store(url, subFile); LOGGER.debug("doDownload file was [{}] ", success); } catch (SubtitlesProviderException e) { - LOGGER.error("Error while getting url for [%s] for subtitle provider [%s] (%s)".formatted(release.getReleaseDescription(), - e.getSubtitleProvider(), e.getMessage()), e); + LOGGER.error("Error while getting url for [${release.releaseDescription}] " + + "for subtitle provider [${e.subtitleProvider}] (${e.getMessage()})", e); throw new RuntimeException(e); } } if (success) { if (!librarySettings.hasLibraryAction(LibraryActionType.NOTHING)) { - Path oldLocationFile = release.getPath().resolve(release.getFileName()); + Path oldLocationFile = release.getPath().resolve(release.fileName); if (oldLocationFile.exists()) { LOGGER.info("Moving/Renaming [{}] to folder [{}] this might take a while... ", videoFileName, path); oldLocationFile.moveToDir(path); @@ -92,13 +90,13 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar CleanAction cleanAction = new CleanAction(librarySettings); cleanAction.cleanUpFiles(release, path, videoFileName); } - if (librarySettings.isLibraryRemoveEmptyFolders() && release.getPath().isEmptyDir()) { - FileUtils.delete(release.getPath()); + if (librarySettings.isLibraryRemoveEmptyFolders() && release.path.isEmptyDir()) { + release.path.deletePath(); } } } if (librarySettings.isLibraryBackupSubtitle()) { - String langFolder = subtitle.getLanguage() == null ? Language.ENGLISH.getName() : subtitle.getLanguage().getName(); + String langFolder = subtitle.language == null ? Language.ENGLISH.getName() : subtitle.language.getName(); Path backupPath = librarySettings.getLibraryBackupSubtitlePath().resolve(langFolder); if (!backupPath.exists()) { @@ -110,7 +108,7 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar } if (librarySettings.isLibraryBackupUseWebsiteFileName()) { - subFile.copyToDirAndRename(backupPath, subtitle.getFileName()); + subFile.copyToDirAndRename(backupPath, subtitle.fileName); } else { subFile.copyToDirAndRename(backupPath, subFileName); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java index a01cdd09..da0466d9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java @@ -9,34 +9,34 @@ import java.util.Optional; import java.util.Set; +import extensions.java.nio.file.Path.PathExt; +import lombok.RequiredArgsConstructor; +import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.set; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.listeners.IndexingProgressListener; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.control.VideoPatterns; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ Files.class, FileUtils.class }) +@RequiredArgsConstructor +@ExtensionMethod({ Files.class }) public class FileListAction { - private IndexingProgressListener indexingProgressListener; + private static final Logger LOGGER = LoggerFactory.getLogger(FileListAction.class); + private static final String SUBTITLE_EXTENSION = "srt"; + + private final Settings settings; + @set IndexingProgressListener indexingProgressListener; private int progressFileIndex; private int progressFilesTotal; - private final Settings settings; - private final static String subtitleExtension = "srt"; - - private static final Logger LOGGER = LoggerFactory.getLogger(FileListAction.class); - public FileListAction(Settings settings) { - this.settings = settings; - } public List getFileListing(Path dir, boolean recursive, Language language, boolean forceSubtitleOverwrite) { - LOGGER.trace("getFileListing: dir [{}] Recursive [{}] languageCode [{}] forceSubtitleOverwrite [{}]", dir, recursive, language, + LOGGER.trace("getFileListing: dir [{}] Recursive [{}] languageCode [{}] forceSubtitleOverwrite [{}]", dir, + recursive, language, forceSubtitleOverwrite); /* Reset progress counters */ this.progressFileIndex = 0; @@ -76,7 +76,8 @@ private List _getFileListing(Path dir, boolean recursive, Language languag try { if (file.isRegularFile()) { - if (isValidVideoFile(file) && (forceSubtitleOverwrite || !fileHasSubtitles(file, language)) && !isExcludedFile(file)) { + if (isValidVideoFile(file) && (forceSubtitleOverwrite || !fileHasSubtitles(file, language)) && + !isExcludedFile(file)) { filelist.add(file); } } else if (recursive && !isExcludedDir(file)) { @@ -93,7 +94,7 @@ private List _getFileListing(Path dir, boolean recursive, Language languag } private boolean isExcludedDir(Path path) { - boolean excludedDir = settings.getExcludeList().stream().anyMatch(item -> item.isExcludedPath(path)); + boolean excludedDir = settings.excludeList.stream().anyMatch(item -> item.isExcludedPath(path)); if (excludedDir) { LOGGER.trace("isExcludedDir, skipping [{}]", path); } @@ -101,7 +102,7 @@ private boolean isExcludedDir(Path path) { } private boolean isExcludedFile(Path path) { - boolean excludedFile = settings.getExcludeList().stream().anyMatch(item -> item.isExcludedPath(path)); + boolean excludedFile = settings.excludeList.stream().anyMatch(item -> item.isExcludedPath(path)); if (excludedFile) { LOGGER.trace("isExcludedFile, skipping [{}]", path); } @@ -116,7 +117,7 @@ public boolean fileHasSubtitles(Path file, Language language) throws IOException String extension = file.getExtension(); Optional subtitleNameOptional = VideoPatterns.EXTENSIONS.stream() .filter(extension::equals) - .map(x -> file.changeExtension(subtitleExtension)) + .map(x -> file.changeExtension(SUBTITLE_EXTENSION)) .findAny(); if (subtitleNameOptional.isEmpty()) { @@ -127,23 +128,22 @@ public boolean fileHasSubtitles(Path file, Language language) throws IOException if (f.exists()) { return true; } else { - String subtitleExtensionWithDot = "." + subtitleExtension; + String subtitleExtensionWithDot = "." + SUBTITLE_EXTENSION; Set langCodes = new HashSet<>(); - langCodes.add(language.getLangCode()); - langCodes.addAll(language.getLangCodesOther()); - String customLangCode = settings.getEpisodeLibrarySettings().getLangCodeMap().get(language); + langCodes.add(language.langCode); + langCodes.addAll(language.langCodesOther); + String customLangCode = settings.episodeLibrarySettings.getLangCodeMap().get(language); if (!StringUtils.isBlank(customLangCode)) { langCodes.add(customLangCode); } - List filters = langCodes.stream().map(word -> word + "." + subtitleExtension).toList(); + List filters = langCodes.stream().map(word -> word + "." + SUBTITLE_EXTENSION).toList(); String subtitleNameWithoutExtension = subtitleName.replace(subtitleExtensionWithDot, ""); - return file.getParent().list().map(FileUtils::getFileNameAsString).filter(fileName -> filters.stream().anyMatch(fileName::endsWith)) + return file.getParent() + .list() + .map(PathExt::getFileNameAsString) + .filter(fileName -> filters.stream().anyMatch(fileName::endsWith)) .anyMatch(fileName -> fileName.contains(subtitleNameWithoutExtension)); } } - - public void setIndexingProgressListener(IndexingProgressListener indexingProgressListener) { - this.indexingProgressListener = indexingProgressListener; - } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java index dfd8d580..028d241c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java @@ -4,6 +4,8 @@ import java.nio.file.Files; import java.nio.file.Path; +import lombok.RequiredArgsConstructor; +import lombok.experimental.ExtensionMethod; import org.lodder.subtools.multisubdownloader.lib.library.FilenameLibraryBuilder; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; @@ -14,23 +16,19 @@ import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ FileUtils.class, Files.class }) +@ExtensionMethod({ Files.class }) @RequiredArgsConstructor public class RenameAction { + private static final Logger LOGGER = LoggerFactory.getLogger(RenameAction.class); + private final LibrarySettings librarySettings; private final Manager manager; private final UserInteractionHandler userInteractionHandler; - private static final Logger LOGGER = LoggerFactory.getLogger(RenameAction.class); - public void rename(Path f, Release release) { String filename = switch (librarySettings.getLibraryAction()) { case MOVE, NOTHING -> f.getFileNameAsString(); @@ -38,7 +36,7 @@ public void rename(Path f, Release release) { }; LOGGER.trace("rename: filename [{}]", filename); - + Path newDir = switch (librarySettings.getLibraryAction()) { case MOVE, MOVEANDRENAME -> PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); case RENAME, NOTHING -> release.getPath(); @@ -54,7 +52,7 @@ public void rename(Path f, Release release) { } LOGGER.trace("rename: newDir [{}]", newDir); - Path file = release.getPath().resolve(release.getFileName()); + Path file = release.getPath().resolve(release.fileName); try { if (librarySettings.hasLibraryAction(LibraryActionType.MOVE) || librarySettings.hasLibraryAction(LibraryActionType.MOVEANDRENAME)) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java index 5db2937f..f46510f1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java @@ -1,7 +1,12 @@ package org.lodder.subtools.multisubdownloader.actions; +import static manifold.ext.props.rt.api.PropOption.*; + import java.util.List; +import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.set; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.exceptions.SearchSetupException; @@ -14,29 +19,30 @@ import org.lodder.subtools.multisubdownloader.workers.SearchHandler; import org.lodder.subtools.multisubdownloader.workers.SearchManager; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Getter(value = AccessLevel.PROTECTED) public abstract class SearchAction implements Runnable, Cancelable, SearchHandler { - private final Manager manager; - private final @NonNull Settings settings; - private final @NonNull SubtitleProviderStore subtitleProviderStore; - private StatusListener statusListener; - private SearchManager searchManager; - private List releases; - private static final Logger LOGGER = LoggerFactory.getLogger(SearchAction.class); + @val(Protected) Settings settings; + @val(Protected) SubtitleProviderStore subtitleProviderStore; + + @get(Protected) @set(Private) StatusListener statusListener; + @get(Protected) @set(Private) SearchManager searchManager; + @get(Protected) @set(Private) List releases; + @get(Protected) abstract Language language; + abstract @get(Protected) IndexingProgressListener indexingProgressListener; + abstract @get(Protected) UserInteractionHandler userInteractionHandler; + abstract @get(Protected) SearchProgressListener searchProgressListener; + + protected SearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore) { + this.settings = settings; + this.subtitleProviderStore = subtitleProviderStore; + } + @Override public void run() { LOGGER.trace("SearchAction is being executed"); @@ -51,15 +57,13 @@ public void run() { } private void search() throws ActionException { - this.statusListener = this.getIndexingProgressListener(); - this.getIndexingProgressListener().reset(); - this.getSearchProgressListener().reset(); + this.statusListener = this.indexingProgressListener; + this.indexingProgressListener.reset(); + this.searchProgressListener.reset(); validate(); - Language language = this.getLanguage(); - - setStatusMessage(Messages.getString("SearchAction.StatusIndexing")); + setStatusMessage(Messages.getText("SearchAction.StatusIndexing")); this.releases = createReleases(); @@ -72,9 +76,9 @@ private void search() throws ActionException { return; } - this.getIndexingProgressListener().completed(); + this.indexingProgressListener.completed(); - this.statusListener = this.getSearchProgressListener(); + this.statusListener = this.searchProgressListener; /* Create a new SearchManager. */ this.searchManager = @@ -82,9 +86,9 @@ private void search() throws ActionException { /* Tell the manager which language we want */ .language(language) /* Tell the manager where to push progressUpdates */ - .progressListener(getSearchProgressListener()) + .progressListener(searchProgressListener) /* Tell the manager how to handle user interactions */ - .userInteractionHandler(getUserInteractionHandler()) + .userInteractionHandler(userInteractionHandler) /* Listen for when the manager tells us Subtitles are found */ .onFound(this); @@ -96,7 +100,7 @@ private void search() throws ActionException { /* Tell the manager which releases to search. */ this.releases.forEach(searchManager::addRelease); - setStatusMessage(Messages.getString("SearchAction.StatusSearching")); + setStatusMessage(Messages.getText("SearchAction.StatusSearching")); /* Tell the manager to start searching */ this.searchManager.start(); @@ -116,17 +120,8 @@ public boolean cancel(boolean mayInterruptIfRunning) { this.searchManager.cancel(mayInterruptIfRunning); } Thread.currentThread().interrupt(); - this.getIndexingProgressListener().completed(); - this.getSearchProgressListener().completed(); + this.indexingProgressListener.completed(); + this.searchProgressListener.completed(); return true; } - - protected abstract Language getLanguage(); - - protected abstract UserInteractionHandler getUserInteractionHandler(); - - protected abstract IndexingProgressListener getIndexingProgressListener(); - - protected abstract SearchProgressListener getSearchProgressListener(); - } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java index 46d89289..d4d903d8 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java @@ -2,6 +2,7 @@ import java.util.List; +import lombok.RequiredArgsConstructor; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.SubtitleComparator; import org.lodder.subtools.multisubdownloader.settings.model.Settings; @@ -11,8 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.RequiredArgsConstructor; - @RequiredArgsConstructor public class UserInteractionHandlerAction { @@ -47,13 +46,13 @@ public List subtitleSelection(Release release, final boolean subtitleS } else { if (!release.getMatchingSubs().isEmpty()) { LOGGER.debug("determineWhatSubtitleDownload for videoFile: [{}] # found subs: [{}]", - release.getFileName(), release.getMatchingSubs().size()); - if (settings.isOptionsAlwaysConfirm()) { + release.fileName, release.getMatchingSubs().size()); + if (settings.optionsAlwaysConfirm) { return userInteractionHandler.selectSubtitles(release); } else if (release.getMatchingSubs().size() == 1 - && release.getMatchingSubs().get(0).getSubtitleMatchType() == SubtitleMatchType.EXACT) { + && release.getMatchingSubs().first.subtitleMatchType == SubtitleMatchType.EXACT) { LOGGER.debug("determineWhatSubtitleDownload: Exact Match"); - return List.of(release.getMatchingSubs().get(0)); + return List.of(release.getMatchingSubs().first); } else if (release.getMatchingSubs().size() > 1) { LOGGER.debug("determineWhatSubtitleDownload: Multiple subs detected"); @@ -62,7 +61,7 @@ public List subtitleSelection(Release release, final boolean subtitleS shortlist.forEach(release::addMatchingSub); // automatic selection results in 1 result if (shortlist.size() == 1) { - return List.of(release.getMatchingSubs().get(0)); + return List.of(release.getMatchingSubs().first); } // nothing match the minimum automatic selection value if (shortlist.isEmpty()) { @@ -75,14 +74,14 @@ public List subtitleSelection(Release release, final boolean subtitleS return userInteractionHandler.selectSubtitles(release); } else { LOGGER.info("Multiple subs detected for: [{}] Unhandleable for CMD! switch to GUI or use '--selection' as switch in de CMD", - release.getFileName()); + release.fileName); } } else if (release.getMatchingSubs().size() == 1) { LOGGER.debug("determineWhatSubtitleDownload: only one sub taking it!!!!"); - return List.of(release.getMatchingSubs().get(0)); + return List.of(release.getMatchingSubs().first); } } - LOGGER.debug("determineWhatSubtitleDownload: No subs found for [{}]", release.getFileName()); + LOGGER.debug("determineWhatSubtitleDownload: No subs found for [{}]", release.fileName); } return List.of(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java index 6af9c91b..897eb139 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java @@ -1,12 +1,10 @@ package org.lodder.subtools.multisubdownloader.cli; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.get; import org.lodder.subtools.multisubdownloader.Messages; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor +@AllArgsConstructor public enum CliOption { HELP("help", false, "App.OptionHelpMsg"), NO_GUI("nogui", false, "App.OptionNoGuiMsg"), @@ -24,17 +22,17 @@ public enum CliOption { DRY_RUN("dryrun", false, "App.OptionDryRun"), CONFIRM_PROVIDER_MAPPING("confirmProviderMapping", false, "App.OptionConfirmProviderMapping"); - private final String value; - private final String longValue; - private final boolean hasArg; - private final String msgCode; + @get String value; + @get String longValue; + @get boolean hasArg; + @get String msgCode; CliOption(String value, boolean hasArg, String description) { this(value, null, hasArg, description); } public String getDescription() { - return Messages.getString(msgCode); + return Messages.getText(msgCode); } } \ No newline at end of file diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java index d11ade2e..35df99ff 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java @@ -1,17 +1,19 @@ package org.lodder.subtools.multisubdownloader.cli.actions; +import static manifold.ext.props.rt.api.PropOption.*; + import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import lombok.AccessLevel; -import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.override; import org.lodder.subtools.multisubdownloader.CLI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; @@ -26,43 +28,36 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({FileUtils.class, Files.class}) -@Setter +@ExtensionMethod({ Files.class }) public class CliSearchAction extends SearchAction { private static final Logger LOGGER = LoggerFactory.getLogger(CliSearchAction.class); + private final @NonNull CLI cli; private final @NonNull FileListAction fileListAction; - @Getter - private final @NonNull Language language; private final @NonNull ReleaseFactory releaseFactory; private final @NonNull SubtitleFiltering filtering; - private final boolean overwriteSubtitles; private final @NonNull List folders; private final boolean recursive; - @Getter(value = AccessLevel.PROTECTED) - private final @NonNull IndexingProgressListener indexingProgressListener; - @Getter(value = AccessLevel.PROTECTED) - private final @NonNull SearchProgressListener searchProgressListener; - public interface CliSearchActionBuilderManager { - CliSearchActionBuilderSubtitleProviderStore manager(Manager manager); - } + @get @override @NonNull Language language; + @get(Protected) @override @NonNull IndexingProgressListener indexingProgressListener; + @get(Protected) @override @NonNull SearchProgressListener searchProgressListener; public interface CliSearchActionBuilderSubtitleProviderStore { - CliSearchActionBuilderIndexingProgressListener subtitleProviderStore(SubtitleProviderStore subtitleProviderStore); + CliSearchActionBuilderIndexingProgressListener subtitleProviderStore( + SubtitleProviderStore subtitleProviderStore); } public interface CliSearchActionBuilderIndexingProgressListener { - CliSearchActionBuilderSearchProgressListener indexingProgressListener(IndexingProgressListener indexingProgressListener); + CliSearchActionBuilderSearchProgressListener indexingProgressListener( + IndexingProgressListener indexingProgressListener); } public interface CliSearchActionBuilderSearchProgressListener { @@ -101,7 +96,7 @@ public interface CliSearchActionBuilderOther { CliSearchAction build() throws SearchSetupException; } - public static CliSearchActionBuilderManager createWithSettings(Settings settings) { + public static CliSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { return new CliSearchActionBuilder(settings); } @@ -112,9 +107,8 @@ public static class CliSearchActionBuilder implements CliSearchActionBuilderSearchProgressListener, CliSearchActionBuilderIndexingProgressListener, CliSearchActionBuilderSubtitleProviderStore, CliSearchActionBuilderCLI, CliSearchActionBuilderFileListAction, CliSearchActionBuilderLanguage, CliSearchActionBuilderReleaseFactory, - CliSearchActionBuilderFiltering, CliSearchActionBuilderFolders, CliSearchActionBuilderOther, CliSearchActionBuilderManager { + CliSearchActionBuilderFiltering, CliSearchActionBuilderFolders, CliSearchActionBuilderOther { private final Settings settings; - private Manager manager; private SubtitleProviderStore subtitleProviderStore; private IndexingProgressListener indexingProgressListener; private SearchProgressListener searchProgressListener; @@ -129,16 +123,18 @@ public static class CliSearchActionBuilder @Override public CliSearchAction build() throws SearchSetupException { - return new CliSearchAction(manager, settings, subtitleProviderStore, indexingProgressListener, searchProgressListener, cli, - fileListAction, language, releaseFactory, filtering, folders, overwriteSubtitles, recursive); + return new CliSearchAction(settings, subtitleProviderStore, indexingProgressListener, + searchProgressListener, cli, fileListAction, language, releaseFactory, filtering, folders, + overwriteSubtitles, recursive); } } - private CliSearchAction(Manager manager, Settings settings, SubtitleProviderStore subtitleProviderStore, - IndexingProgressListener indexingProgressListener, SearchProgressListener searchProgressListener, - CLI cli, FileListAction fileListAction, Language language, ReleaseFactory releaseFactory, - SubtitleFiltering filtering, List folders, boolean overwriteSubtitles, boolean recursive) throws SearchSetupException { - super(manager, settings, subtitleProviderStore); + private CliSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, + IndexingProgressListener indexingProgressListener, SearchProgressListener searchProgressListener, CLI cli, + FileListAction fileListAction, Language language, ReleaseFactory releaseFactory, + SubtitleFiltering filtering, List folders, boolean overwriteSubtitles, boolean recursive) + throws SearchSetupException { + super(settings, subtitleProviderStore); this.indexingProgressListener = indexingProgressListener; this.searchProgressListener = searchProgressListener; this.cli = cli; @@ -156,10 +152,11 @@ private CliSearchAction(Manager manager, Settings settings, SubtitleProviderStor @Override protected List createReleases() { - fileListAction.setIndexingProgressListener(this.getIndexingProgressListener()); + fileListAction.indexingProgressListener = this.indexingProgressListener; List files = this.folders.stream() - .flatMap(folder -> fileListAction.getFileListing(folder, recursive, language, overwriteSubtitles).stream()) + .flatMap(folder -> fileListAction.getFileListing(folder, recursive, language, overwriteSubtitles) + .stream()) .toList(); /* fix: remove carriage return from progressbar */ @@ -171,8 +168,8 @@ protected List createReleases() { LOGGER.debug("# Files found to process [{}] ", total); - System.out.println(Messages.getString("CliSearchAction.ParsingFoundFiles")); - this.getIndexingProgressListener().progress(progress); + System.out.println(Messages.getText("CliSearchAction.ParsingFoundFiles")); + this.indexingProgressListener.progress(progress); List releases = new ArrayList<>(); for (Path file : files) { @@ -180,9 +177,9 @@ protected List createReleases() { progress = (int) Math.floor((float) index / total * 100); /* Tell progressListener which file we are processing */ - this.getIndexingProgressListener().progress(file.getFileNameAsString()); + this.indexingProgressListener.progress(file.getFileNameAsString()); - Release release = this.releaseFactory.createRelease(file, getUserInteractionHandler()); + Release release = this.releaseFactory.createRelease(file, userInteractionHandler); if (release == null) { continue; } @@ -190,7 +187,7 @@ protected List createReleases() { releases.add(release); /* Update progressListener */ - this.getIndexingProgressListener().progress(progress); + this.indexingProgressListener.progress(progress); } return releases; @@ -198,21 +195,23 @@ protected List createReleases() { @Override public void onFound(Release release, List subtitles) { - subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).forEach(release::addMatchingSub); - if (getSearchManager().getProgress() < 100) { + subtitles.stream() + .filter(subtitle -> filtering.useSubtitle(subtitle, release)) + .forEach(release::addMatchingSub); + if (searchManager.progress < 100) { return; } - LOGGER.debug("found files for doDownload [{}]", getReleases().size()); + LOGGER.debug("found files for doDownload [{}]", releases.size()); /* stop printing progress */ - this.getSearchProgressListener().completed(); + this.searchProgressListener.completed(); - this.cli.download(getReleases()); + this.cli.download(releases); } @Override protected UserInteractionHandler getUserInteractionHandler() { - return new UserInteractionHandlerCLI(getSettings()); + return new UserInteractionHandlerCLI(settings); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileindexerProgress.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java similarity index 69% rename from MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileindexerProgress.java rename to MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java index b5670916..9b462aa8 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileindexerProgress.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java @@ -3,18 +3,18 @@ import org.lodder.subtools.multisubdownloader.actions.ActionException; import org.lodder.subtools.multisubdownloader.listeners.IndexingProgressListener; -public class CLIFileindexerProgress extends CLIProgress implements IndexingProgressListener { +public class CLIFileIndexerProgress extends CLIProgress implements IndexingProgressListener { private String currentFile; - public CLIFileindexerProgress() { + public CLIFileIndexerProgress() { super(); currentFile = ""; } @Override public void progress(int progress) { - setProgress(progress); + this.progress = progress; this.printProgress(); } @@ -26,7 +26,7 @@ public void progress(String directory) { @Override public void completed() { - if (!this.isEnabled()) { + if (!enabled) { return; } this.disable(); @@ -34,12 +34,12 @@ public void completed() { @Override public void reset() { - this.setEnabled(true); + this.enabled = true; } @Override public void onError(ActionException exception) { - if (!this.isEnabled()) { + if (!enabled) { return; } System.out.println("Error: " + exception.getMessage()); @@ -47,7 +47,7 @@ public void onError(ActionException exception) { @Override public void onStatus(String message) { - if (!this.isEnabled()) { + if (!enabled) { return; } System.out.println(message); @@ -55,17 +55,23 @@ public void onStatus(String message) { @Override protected void printProgress() { - if (!isEnabled()) { + if (!enabled) { return; } - if (isVerbose()) { + if (verbose) { /* newlines to counter the return carriage from printProgBar() */ System.out.println(); System.out.println(this.currentFile); System.out.println(); } - this.printProgBar(this.getProgress()); + this.printProgBar(this.progress); + } + + // TODO: remove this when https://github.com/manifold-systems/manifold/issues/642 is fixed + public CLIFileIndexerProgress verbose(boolean verbose) { + super.verbose(verbose); + return this; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java index 6820adbf..f70b2216 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java @@ -1,33 +1,31 @@ package org.lodder.subtools.multisubdownloader.cli.progress; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; +import static manifold.ext.props.rt.api.PropOption.*; -@Getter(value = AccessLevel.PROTECTED) -@Setter(value = AccessLevel.PROTECTED) -abstract class CLIProgress> { +import manifold.ext.props.rt.api.var; +import manifold.ext.rt.api.Self; - private int progress; - private boolean isEnabled; - private boolean isVerbose; +abstract class CLIProgress { + + @var(Protected) int progress; + @var(Protected) boolean enabled; + @var(Protected) boolean verbose; protected CLIProgress() { - isEnabled = true; - isVerbose = false; + enabled = true; + verbose = false; progress = 0; } public void disable() { - this.isEnabled = false; + this.enabled = false; /* Print a line */ System.out.println(); } - @SuppressWarnings("unchecked") - public T verbose(boolean isVerbose) { - this.isVerbose = isVerbose; - return (T) this; + public @Self CLIProgress verbose(boolean verbose) { + this.verbose = verbose; + return this; } protected abstract void printProgress(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java index 13a2c0f7..2e30165f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java @@ -1,14 +1,13 @@ package org.lodder.subtools.multisubdownloader.cli.progress; +import dnl.utils.text.table.TextTable; import org.lodder.subtools.multisubdownloader.actions.ActionException; import org.lodder.subtools.multisubdownloader.gui.dialog.progress.search.SearchProgressTableModel; import org.lodder.subtools.multisubdownloader.listeners.SearchProgressListener; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.model.Release; -import dnl.utils.text.table.TextTable; - -public class CLISearchProgress extends CLIProgress implements SearchProgressListener { +public class CLISearchProgress extends CLIProgress implements SearchProgressListener { private final TextTable table; private final SearchProgressTableModel tableModel; @@ -20,19 +19,19 @@ public CLISearchProgress() { @Override public void progress(SubtitleProvider provider, int jobsLeft, Release release) { - this.tableModel.update(provider.getName(), jobsLeft, release == null ? "Done" : release.getFileName()); + this.tableModel.update(provider.getName(), jobsLeft, release == null ? "Done" : release.fileName); this.printProgress(); } @Override public void progress(int progress) { - setProgress(progress); + this.progress = progress; this.printProgress(); } @Override public void completed() { - if (!this.isEnabled()) { + if (!this.enabled) { return; } this.disable(); @@ -40,12 +39,12 @@ public void completed() { @Override public void reset() { - this.setEnabled(true); + this.enabled = true; } @Override public void onError(ActionException exception) { - if (!isEnabled()) { + if (!enabled) { return; } System.out.println("Error: " + exception.getMessage()); @@ -53,7 +52,7 @@ public void onError(ActionException exception) { @Override public void onStatus(String message) { - if (!isEnabled()) { + if (!enabled) { return; } System.out.println(message); @@ -61,17 +60,23 @@ public void onStatus(String message) { @Override protected void printProgress() { - if (!isEnabled()) { + if (!enabled) { return; } /* print table */ - if (isVerbose()) { + if (verbose) { System.out.println(); table.printTable(); } /* print progressbar */ - this.printProgBar(this.getProgress()); + this.printProgBar(this.progress); + } + + // TODO: remove this when https://github.com/manifold-systems/manifold/issues/642 is fixed + public CLISearchProgress verbose(boolean verbose) { + super.verbose(verbose); + return this; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java index c96f28eb..6d9c2344 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java @@ -2,9 +2,8 @@ import java.io.Serial; -import org.lodder.subtools.multisubdownloader.actions.ActionException; - import lombok.experimental.StandardException; +import org.lodder.subtools.multisubdownloader.actions.ActionException; @StandardException public class SearchSetupException extends ActionException { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java index a5656e57..b649d364 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java @@ -47,7 +47,7 @@ public void initialize(UserInteractionHandler userInteractionHandler) { this.registerProviders(providers, userInteractionHandler); } - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({ "rawtypes", "unchecked" }) public List getProviders() { Reflections reflections = new Reflections("org.lodder.subtools.multisubdownloader"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java index 9f7e24b2..77445b9d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.framework.service.providers; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; @@ -7,10 +9,7 @@ public class EventServiceProvider implements ServiceProvider { - @Override - public int getPriority() { - return 0; - } + @val @override int priority = 0; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java index 288bfe6e..87c6a642 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java @@ -1,10 +1,11 @@ package org.lodder.subtools.multisubdownloader.framework.service.providers; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; public interface ServiceProvider { - int getPriority(); + @val int priority; void register(Container app, UserInteractionHandler userInteractionHandler); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java index 7e5c0ae7..24c07a50 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java @@ -10,6 +10,6 @@ public class ServiceProviderComparator implements Comparator, S @Override public int compare(ServiceProvider a, ServiceProvider b) { - return Integer.compare(a.getPriority(), b.getPriority()); + return Integer.compare(a.priority, b.priority); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java index 5336d78b..db3b3d7f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java @@ -1,20 +1,16 @@ package org.lodder.subtools.multisubdownloader.gui; -import java.io.Serial; - -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; - -import org.lodder.subtools.multisubdownloader.Messages; +import static org.lodder.subtools.multisubdownloader.Messages.*; +import javax.swing.*; import java.awt.event.ActionListener; +import java.io.Serial; public class Menu extends JMenuBar { @Serial private static final long serialVersionUID = -7384297314593169280L; + private JMenu mnFile; private JMenuItem mntmQuit; private JMenu mnView; @@ -47,31 +43,31 @@ public Menu() { } private void createComponents() { - mnFile = new JMenu(Messages.getString("Menu.Path")); - mntmQuit = new JMenuItem(Messages.getString("App.Close")); - mnView = new JMenu(Messages.getString("Menu.View")); - mnSearchResults = new JMenu(Messages.getString("Menu.SearchResults")); - chckbxmntmFileName = new JCheckBoxMenuItem(Messages.getString("Menu.Filename")); - chckbxmntmType = new JCheckBoxMenuItem(Messages.getString("Menu.Type")); - chckbxmntmTitle = new JCheckBoxMenuItem(Messages.getString("Menu.Title")); - chckbxmntmSeason = new JCheckBoxMenuItem(Messages.getString("App.Season")); - chckbxmntmEpisode = new JCheckBoxMenuItem(Messages.getString("App.Episode")); - chckbxmntmShowOnlyFound = new JCheckBoxMenuItem(Messages.getString("Menu.OnlyShowFound")); - mntmClearLog = new JMenuItem(Messages.getString("Menu.EraseLogging")); - mntmRenameSerieFiles = new JMenuItem(Messages.getString("Menu.RenameSerie")); - mntmRenameMovieFiles = new JMenuItem(Messages.getString("Menu.RenameMovie")); - mntmPreferences = new JMenuItem(Messages.getString("Menu.Preferences")); - mnSerieNames = new JMenu(Messages.getString("Menu.SerieNames")); - mntmTranslateShowNames = new JMenuItem(Messages.getString("Menu.MappingTvdbScene")); - mnImportExport = new JMenu(Messages.getString("Menu.ImportExport")); - mnEdit = new JMenu(Messages.getString("App.Edit")); - mnHelp = new JMenu(Messages.getString("Menu.Help")); - mntmExportTranslate = new JMenuItem(Messages.getString("Menu.ExportMappingTvdbScene")); - mntmImportTranslate = new JMenuItem(Messages.getString("Menu.ImportMappingTvdbScene")); - mntmExportPreferences = new JMenuItem(Messages.getString("Menu.ExportPreferences")); - mntmImportPreferences = new JMenuItem(Messages.getString("Menu.ImportPreferences")); - mntmAbout = new JMenuItem(Messages.getString("Menu.About")); - mntmCheckForUpdate = new JMenuItem(Messages.getString("Menu.CheckForUpdate")); + mnFile = new JMenu(getText("Menu.Path")); + mntmQuit = new JMenuItem(getText("App.Close")); + mnView = new JMenu(getText("Menu.View")); + mnSearchResults = new JMenu(getText("Menu.SearchResults")); + chckbxmntmFileName = new JCheckBoxMenuItem(getText("Menu.Filename")); + chckbxmntmType = new JCheckBoxMenuItem(getText("Menu.Type")); + chckbxmntmTitle = new JCheckBoxMenuItem(getText("Menu.Title")); + chckbxmntmSeason = new JCheckBoxMenuItem(getText("App.Season")); + chckbxmntmEpisode = new JCheckBoxMenuItem(getText("App.Episode")); + chckbxmntmShowOnlyFound = new JCheckBoxMenuItem(getText("Menu.OnlyShowFound")); + mntmClearLog = new JMenuItem(getText("Menu.EraseLogging")); + mntmRenameSerieFiles = new JMenuItem(getText("Menu.RenameSerie")); + mntmRenameMovieFiles = new JMenuItem(getText("Menu.RenameMovie")); + mntmPreferences = new JMenuItem(getText("Menu.Preferences")); + mnSerieNames = new JMenu(getText("Menu.SerieNames")); + mntmTranslateShowNames = new JMenuItem(getText("Menu.MappingTvdbScene")); + mnImportExport = new JMenu(getText("Menu.ImportExport")); + mnEdit = new JMenu(getText("App.Edit")); + mnHelp = new JMenu(getText("Menu.Help")); + mntmExportTranslate = new JMenuItem(getText("Menu.ExportMappingTvdbScene")); + mntmImportTranslate = new JMenuItem(getText("Menu.ImportMappingTvdbScene")); + mntmExportPreferences = new JMenuItem(getText("Menu.ExportPreferences")); + mntmImportPreferences = new JMenuItem(getText("Menu.ImportPreferences")); + mntmAbout = new JMenuItem(getText("Menu.About")); + mntmCheckForUpdate = new JMenuItem(getText("Menu.CheckForUpdate")); } private void addComponentsToMenu() { @@ -209,7 +205,7 @@ public Menu withExportTranslationsAction(Runnable exportTranslationsAction) { } public Menu withAboutAction(Runnable aboutAction) { - addActionListener(mntmAbout, arg -> aboutAction.run()); + addActionListener(mntmAbout, _ -> aboutAction.run()); return this; } @@ -254,7 +250,7 @@ public Menu withViewSeasonAction(Runnable viewSeasonAction) { } private void addActionListener(JMenuItem menuItem, Runnable actionListener) { - addActionListener(menuItem, arg -> actionListener.run()); + addActionListener(menuItem, _ -> actionListener.run()); } private void addActionListener(JMenuItem menuItem, ActionListener actionListener) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java index 1be85ac1..f8ce1043 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java @@ -1,11 +1,12 @@ package org.lodder.subtools.multisubdownloader.gui; +import static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.*; import java.awt.*; import java.io.Serial; import net.miginfocom.swing.MigLayout; -import org.lodder.subtools.multisubdownloader.Messages; public class Splash extends JWindow { @@ -14,14 +15,14 @@ public class Splash extends JWindow { private JProgressBar progressBar; public Splash() { - initialize_ui(); + initializeUi(); } - public void initialize_ui() { + public void initializeUi() { setBounds(100, 100, 501, 100); getContentPane().setLayout(new MigLayout("", "[][475px,center][]", "[][40px:n]")); - JLabel label = new JLabel(Messages.getString("Splash.starting")); + JLabel label = new JLabel(getText("Splash.starting")); getContentPane().add(label, "cell 1 0 2 1,alignx left"); progressBar = new JProgressBar(0, 100); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java index ec474374..895f47d7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java @@ -1,33 +1,34 @@ package org.lodder.subtools.multisubdownloader.gui; +import javax.swing.*; +import java.awt.*; import java.util.function.Function; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -import org.lodder.subtools.multisubdownloader.Messages; - -import java.awt.Component; - import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import org.lodder.subtools.multisubdownloader.Messages; @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ToStringListCellRenderer implements ListCellRenderer { + private final ListCellRenderer originalRenderer; private final Function toStringMapper; - public static ToStringListCellRenderer of(ListCellRenderer originalRenderer, Function toStringMapper) { + public static ToStringListCellRenderer of(ListCellRenderer originalRenderer, + Function toStringMapper) { return new ToStringListCellRenderer<>(originalRenderer, toStringMapper); } - public static ToStringListCellRenderer ofMessage(ListCellRenderer originalRenderer, Function toStringMapper) { - return of(originalRenderer, item -> Messages.getString(toStringMapper.apply(item))); + public static ToStringListCellRenderer ofMessage(ListCellRenderer originalRenderer, + Function toStringMapper) { + return of(originalRenderer, item -> Messages.getText(toStringMapper.apply(item))); } @Override - public Component getListCellRendererComponent(JList list, T value, int index, boolean isSelected, boolean cellHasFocus) { - return originalRenderer.getListCellRendererComponent(list, toStringMapper.apply(value), index, isSelected, cellHasFocus); + public Component getListCellRendererComponent(JList list, T value, int index, boolean isSelected, + boolean cellHasFocus) { + return originalRenderer.getListCellRendererComponent(list, toStringMapper.apply(value), index, isSelected, + cellHasFocus); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java index 8fd79245..2c9c186c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java @@ -19,7 +19,6 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; @@ -27,10 +26,6 @@ public class FileGuiSearchAction extends GuiSearchAction { private final @NonNull FileListAction filelistAction; - public interface FileGuiSearchActionBuilderManager { - FileGuiSearchActionBuilderSubtitleProviderStore manager(Manager manager); - } - public interface FileGuiSearchActionBuilderSubtitleProviderStore { FileGuiSearchActionBuilderGUI subtitleProviderStore(SubtitleProviderStore subtitleProviderStore); } @@ -51,7 +46,7 @@ public interface FileGuiSearchActionBuilderBuild { FileGuiSearchAction build(); } - public static FileGuiSearchActionBuilderManager createWithSettings(Settings settings) { + public static FileGuiSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { return new FileGuiSearchActionBuilder(settings); } @@ -61,9 +56,8 @@ public static FileGuiSearchActionBuilderManager createWithSettings(Settings sett public static class FileGuiSearchActionBuilder implements FileGuiSearchActionBuilderBuild, FileGuiSearchActionBuilderReleaseFactory, FileGuiSearchActionBuilderSearchPanel, FileGuiSearchActionBuilderGUI, - FileGuiSearchActionBuilderSubtitleProviderStore, FileGuiSearchActionBuilderManager { + FileGuiSearchActionBuilderSubtitleProviderStore { private final Settings settings; - private Manager manager; private SubtitleProviderStore subtitleProviderStore; private GUI mainWindow; private SearchPanel searchPanel; @@ -71,34 +65,34 @@ public static class FileGuiSearchActionBuilder @Override public FileGuiSearchAction build() { - return new FileGuiSearchAction(manager, settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); + return new FileGuiSearchAction(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); } } - private FileGuiSearchAction(Manager manager, Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, + private FileGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel searchPanel, ReleaseFactory releaseFactory) { - super(manager, settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); + super(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); this.filelistAction = new FileListAction(settings); } @Override protected void validate() throws SearchSetupException { String path = getInputPanel().getIncomingPath(); - if ("".equals(path) && !this.getSettings().hasDefaultFolders()) { - throw new SearchSetupException(Messages.getString("App.NoFolderSelected")); + if ("".equals(path) && !this.settings.hasDefaultFolders()) { + throw new SearchSetupException(Messages.getText("App.NoFolderSelected")); } } @Override public void onFound(Release release, List subtitles) { - VideoTableModel model = (VideoTableModel) this.getSearchPanel().getResultPanel().getTable().getModel(); + VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); - List filteredSubtitles = - getFiltering() != null ? subtitles.stream().filter(subtitle -> getFiltering().useSubtitle(subtitle, release)).toList() : subtitles; + List filteredSubtitles = filtering != null ? + subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).toList() : subtitles; filteredSubtitles.forEach(release::addMatchingSub); model.addRow(release); - getMainWindow().repaint(); + mainWindow.repaint(); /* Let GuiSearchAction also make some decisions */ super.onFound(release, filteredSubtitles); @@ -112,7 +106,7 @@ protected List createReleases() { boolean recursive = inputPanel.isRecursiveSelected(); boolean overwriteExistingSubtitles = inputPanel.isForceOverwrite(); - VideoTableModel model = (VideoTableModel) this.getSearchPanel().getResultPanel().getTable().getModel(); + VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); model.clearTable(); /* get a list of video files */ @@ -130,38 +124,40 @@ private List createReleases(List files) { int index = 0; int progress = 0; - this.getIndexingProgressListener().progress(progress); + this.indexingProgressListener.progress(progress); for (Path file : files) { index++; progress = (int) Math.floor((float) index / total * 100); /* Tell progressListener which file we are processing */ - this.getIndexingProgressListener().progress(file.getFileName().toString()); + this.indexingProgressListener.progress(file.getFileName().toString()); - Release r = getReleaseFactory().createRelease(file, getUserInteractionHandler()); + Release r = releaseFactory.createRelease(file, userInteractionHandler); if (r != null) { releases.add(r); } /* Update progressListener */ - this.getIndexingProgressListener().progress(progress); + this.indexingProgressListener.progress(progress); } return releases; } - private List getFiles(String filePath, Language language, boolean recursive, boolean overwriteExistingSubtitles) { + private List getFiles(String filePath, Language language, boolean recursive, + boolean overwriteExistingSubtitles) { /* Get a list of selected directories */ - List dirs = !filePath.isEmpty() ? List.of(Path.of(filePath)) : this.getSettings().getDefaultFolders(); + List dirs = !filePath.isEmpty() ? List.of(Path.of(filePath)) : this.settings.defaultFolders; /* Scan directories for video files */ /* Tell Action where to send progressUpdates */ - this.filelistAction.setIndexingProgressListener(this.getIndexingProgressListener()); + this.filelistAction.indexingProgressListener = this.indexingProgressListener; /* Start the getFileListing Action */ return dirs.stream() - .flatMap(dir -> this.filelistAction.getFileListing(dir, recursive, language, overwriteExistingSubtitles).stream()) + .flatMap(dir -> this.filelistAction.getFileListing(dir, recursive, language, overwriteExistingSubtitles) + .stream()) .toList(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java index 8990188b..bc75aea9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java @@ -1,7 +1,12 @@ package org.lodder.subtools.multisubdownloader.gui.actions.search; +import static manifold.ext.props.rt.api.PropOption.*; + import java.util.List; +import lombok.NonNull; +import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.override; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; import org.lodder.subtools.multisubdownloader.actions.SearchAction; @@ -15,28 +20,22 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NonNull; - -@Getter(value = AccessLevel.PROTECTED) public abstract class GuiSearchAction

extends SearchAction { - private final @NonNull GUI mainWindow; - private final @NonNull SearchPanel

searchPanel; - private final SubtitleFiltering filtering; - private final @NonNull ReleaseFactory releaseFactory; - private final IndexingProgressListener indexingProgressListener; - private final SearchProgressListener searchProgressListener; - private final UserInteractionHandlerGUI userInteractionHandler; + @get(Protected) @NonNull GUI mainWindow; + @get(Protected) @NonNull SearchPanel

searchPanel; + @get(Protected) SubtitleFiltering filtering; + @get(Protected) @NonNull ReleaseFactory releaseFactory; + @get(Protected) @override IndexingProgressListener indexingProgressListener; + @get(Protected) @override SearchProgressListener searchProgressListener; + @get(Protected) @override UserInteractionHandlerGUI userInteractionHandler; - public GuiSearchAction(Manager manager, Settings settings, SubtitleProviderStore subtitleProviderStore, + GuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel

searchPanel, ReleaseFactory releaseFactory) { - super(manager, settings, subtitleProviderStore); + super(settings, subtitleProviderStore); this.mainWindow = mainWindow; this.searchPanel = searchPanel; this.filtering = new SubtitleFiltering(settings); @@ -53,12 +52,12 @@ public GuiSearchAction(Manager manager, Settings settings, SubtitleProviderStore } protected P getInputPanel() { - return this.getSearchPanel().getInputPanel(); + return this.searchPanel.inputPanel; } @Override protected Language getLanguage() { - return this.searchPanel.getInputPanel().getSelectedLanguage(); + return this.searchPanel.inputPanel.selectedLanguage; } @Override @@ -67,15 +66,15 @@ public void onFound(Release release, List subtitles) { return; } - VideoTableModel model = (VideoTableModel) this.searchPanel.getResultPanel().getTable().getModel(); + VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); if (model.getRowCount() > 0) { - searchPanel.getResultPanel().enableButtons(); + searchPanel.resultPanel.enableButtons(); } - if (this.getSearchManager().getProgress() == 100) { - this.getSearchProgressListener().completed(); - searchPanel.getInputPanel().enableSearchButton(); + if (this.searchManager.progress == 100) { + this.searchProgressListener.completed(); + searchPanel.inputPanel.enableSearchButton(); } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java index 8a15c8fe..4d2f15c1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java @@ -15,7 +15,6 @@ import org.lodder.subtools.multisubdownloader.lib.ReleaseFactory; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; -import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; @@ -24,10 +23,6 @@ public class TextGuiSearchAction extends GuiSearchAction { - public interface FileGuiSearchActionBuilderManager { - TextGuiSearchActionBuilderSubtitleProviderStore manager(Manager manager); - } - public interface TextGuiSearchActionBuilderSubtitleProviderStore { TextGuiSearchActionBuilderGUI subtitleProviderStore(SubtitleProviderStore subtitleProviderStore); } @@ -48,7 +43,7 @@ public interface TextGuiSearchActionBuilderBuild { TextGuiSearchAction build(); } - public static FileGuiSearchActionBuilderManager createWithSettings(Settings settings) { + public static TextGuiSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { return new TextGuiSearchActionBuilder(settings); } @@ -56,10 +51,10 @@ public static FileGuiSearchActionBuilderManager createWithSettings(Settings sett @Setter @Accessors(chain = true, fluent = true) public static class TextGuiSearchActionBuilder - implements TextGuiSearchActionBuilderBuild, TextGuiSearchActionBuilderReleaseFactory, TextGuiSearchActionBuilderSearchPanel, - TextGuiSearchActionBuilderGUI, TextGuiSearchActionBuilderSubtitleProviderStore, FileGuiSearchActionBuilderManager { + implements TextGuiSearchActionBuilderBuild, TextGuiSearchActionBuilderReleaseFactory, + TextGuiSearchActionBuilderSearchPanel, TextGuiSearchActionBuilderGUI, + TextGuiSearchActionBuilderSubtitleProviderStore { private final Settings settings; - private Manager manager; private SubtitleProviderStore subtitleProviderStore; private GUI mainWindow; private SearchPanel searchPanel; @@ -67,19 +62,19 @@ public static class TextGuiSearchActionBuilder @Override public TextGuiSearchAction build() { - return new TextGuiSearchAction(manager, settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); + return new TextGuiSearchAction(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); } } - private TextGuiSearchAction(Manager manager, Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, + private TextGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel searchPanel, ReleaseFactory releaseFactory) { - super(manager, settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); + super(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); } @Override protected void validate() throws SearchSetupException { if (getInputPanel().getReleaseName().isEmpty()) { - throw new SearchSetupException(Messages.getString("App.NoReleaseEntered")); + throw new SearchSetupException(Messages.getText("App.NoReleaseEntered")); } } @@ -88,36 +83,33 @@ protected List createReleases() { String name = getInputPanel().getReleaseName(); VideoSearchType type = getInputPanel().getType(); - VideoTableModel model = (VideoTableModel) this.getSearchPanel().getResultPanel().getTable().getModel(); + VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); model.clearTable(); // TODO: Redefine what a "release" is. Release release = switch (type) { case EPISODE -> TvRelease.builder() .name(name) - .season(getInputPanel().getSeason()) - .episode(getInputPanel().getEpisode()) - .quality(getInputPanel().getQuality()) - .build(); - case MOVIE -> MovieRelease.builder() - .name(name) - .quality(getInputPanel().getQuality()) + .season(inputPanel.season) + .episode(inputPanel.episode) + .quality(inputPanel.quality) .build(); - default -> getReleaseFactory().createRelease(Path.of(name), getUserInteractionHandler()); + case MOVIE -> MovieRelease.builder().name(name).quality(inputPanel.quality).build(); + default -> releaseFactory.createRelease(Path.of(name), userInteractionHandler); }; return release != null ? List.of(release) : List.of(); } @Override public void onFound(Release release, List subtitles) { - VideoTableModel model = (VideoTableModel) this.getSearchPanel().getResultPanel().getTable().getModel(); + VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); - List subtitlesFiltered = - getFiltering() != null ? subtitles.stream().filter(subtitle -> getFiltering().useSubtitle(subtitle, release)).toList() : subtitles; + List subtitlesFiltered = filtering != null ? + subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).toList() : subtitles; subtitlesFiltered.forEach(release::addMatchingSub); // use automatic selection to reduce the selection for the user - List subtitlesFilteredAutomatic = getUserInteractionHandler().getAutomaticSelection(subtitlesFiltered); + List subtitlesFilteredAutomatic = userInteractionHandler.getAutomaticSelection(subtitlesFiltered); subtitlesFilteredAutomatic.forEach(model::addRow); /* Let GuiSearchAction also make some decisions */ diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java index df12e22a..7dfdb0ba 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java @@ -1,5 +1,10 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import javax.swing.*; +import javax.swing.RowSorter.*; +import javax.swing.border.*; +import javax.swing.table.*; +import java.awt.*; import java.io.Serial; import java.util.Arrays; import java.util.Comparator; @@ -9,27 +14,13 @@ import java.util.function.BiFunction; import java.util.function.Function; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.RowSorter; -import javax.swing.RowSorter.SortKey; -import javax.swing.border.EmptyBorder; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableModel; -import javax.swing.table.TableRowSorter; - +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; +import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.tuple.Pair; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.JButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; @@ -39,68 +30,54 @@ import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JButtonExtension.class, AbstractButtonExtension.class, JComponentExtension.class }) public class MappingEpisodeNameDialog extends MultiSubDialog { - @Serial - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; + private final JPanel contentPanel = new JPanel(); - private JTable table; private final Manager manager; private final MappingTableModel mappingTableModel; private final SubtitleProviderStore subtitleProviderStore; private final UserInteractionHandlerGUI userInteractionHandler; - private Optional selectedSubtitleProvider; private final JButton btnAddCustomMapping; + private JTable table; + private Optional selectedSubtitleProvider; private MappingType selectedMappingType; /** * Create the dialog. */ - public MappingEpisodeNameDialog(JFrame frame, final SettingsControl prefCtrl, Manager manager, SubtitleProviderStore subtitleProviderStore, - UserInteractionHandlerGUI userInteractionHandler) { - super(frame, Messages.getString("MappingEpisodeNameDialog.Title"), true); + public MappingEpisodeNameDialog(JFrame frame, final SettingsControl prefCtrl, Manager manager, + SubtitleProviderStore subtitleProviderStore, UserInteractionHandlerGUI userInteractionHandler) { + super(frame, Messages.getText("MappingEpisodeNameDialog.Title"), true); this.manager = manager; this.subtitleProviderStore = subtitleProviderStore; this.userInteractionHandler = userInteractionHandler; - this.btnAddCustomMapping = new JButton(Messages.getString("MappingEpisodeNameDialog.ChangeMapping")); + this.btnAddCustomMapping = new JButton(Messages.getText("MappingEpisodeNameDialog.ChangeMapping")); this.mappingTableModel = new MappingTableModel(manager); initialize(); } private void selectMappingType(MappingType mappingType) { this.selectedMappingType = mappingType; - this.selectedSubtitleProvider = subtitleProviderStore.getAllProviders().stream() - .filter(subtitleProvider -> subtitleProvider.getProviderName().equals(mappingType.getProviderName())) + this.selectedSubtitleProvider = subtitleProviderStore.getAllProviders() + .stream() + .filter(subtitleProvider -> subtitleProvider.providerName.equals(mappingType.providerName)) .findAny(); btnAddCustomMapping.setEnabled(selectedSubtitleProvider.isPresent()); mappingTableModel.setMappingType(mappingType); repaint(); } - @Getter public enum MappingType { TVDB("TVDB", "TVDB", new SelectionForKeyPrefix("", "TVDB-serieId-", k -> k.replace("-serieId-", "-tvdbSerie-"))), - ADDIC7ED("Addic7ed", SubtitleSource.ADDIC7ED, - new SelectionForKeyPrefix("", "ADDIC7ED-serieName-name:"), + ADDIC7ED("Addic7ed", SubtitleSource.ADDIC7ED, new SelectionForKeyPrefix("", "ADDIC7ED-serieName-name:"), new SelectionForKeyPrefix("", "ADDIC7ED-serieName-tvdbId:")), ADDIC7ED_PROXY("Addic7ed (Proxy)", SubtitleSource.ADDIC7ED.name() + "-GESTDOWN", new SelectionForKeyPrefix("", "ADDIC7ED-GESTDOWN-serieName-name:"), new SelectionForKeyPrefix("", "ADDIC7ED-GESTDOWN-serieName-tvdbId:")), - SUBSCENE("Subscene", SubtitleSource.SUBSCENE, - new SelectionForKeyPrefix("", "SUBSCENE-serieName-name:"), + SUBSCENE("Subscene", SubtitleSource.SUBSCENE, new SelectionForKeyPrefix("", "SUBSCENE-serieName-name:"), new SelectionForKeyPrefix("", "SUBSCENE-serieName-tvdbId:")), TV_SUBTITLES("TVSubtitles", SubtitleSource.TVSUBTITLES, new SelectionForKeyPrefix("", "TVSUBTITLES-serieName-name:"), @@ -108,17 +85,17 @@ public enum MappingType { OPEN_SUBTITLES("OpenSubtitles", SubtitleSource.OPENSUBTITLES, new SelectionForKeyPrefix("", "OPENSUBTITLES-serieName-name:"), new SelectionForKeyPrefix("", "OPENSUBTITLES-serieName-tvdbId:")), - PODNAPISI("Podnapisi", SubtitleSource.PODNAPISI, - new SelectionForKeyPrefix("", "PODNAPISI-serieName-name:"), + PODNAPISI("Podnapisi", SubtitleSource.PODNAPISI, new SelectionForKeyPrefix("", "PODNAPISI-serieName-name:"), new SelectionForKeyPrefix("", "PODNAPISI-serieName-tvdbId:")); - public static final BiFunction>> MAPPING_SUPPLIER; - private final String name; - private final String providerName; - private final String nameColumn; - private final String mappingColumn; - private final String providerNameColumn; - private final SelectionForKeyPrefix[] selectionForKeyPrefixList; + public static final BiFunction>> + MAPPING_SUPPLIER; + @val String name; + @val String providerName; + @val String nameColumn; + @val String mappingColumn; + @val String providerNameColumn; + @val SelectionForKeyPrefix[] selectionForKeyPrefixList; @Override public String toString() { @@ -140,9 +117,9 @@ public String toString() { MappingType(String name, String providerName, SelectionForKeyPrefix... selectionForKeyPrefixList) { this.name = name; this.providerName = providerName; - this.nameColumn = Messages.getString("MappingEpisodeNameDialog.SceneShowName"); - this.mappingColumn = Messages.getString("MappingEpisodeNameDialog.ProviderId"); - this.providerNameColumn = Messages.getString("MappingEpisodeNameDialog.ProviderName"); + this.nameColumn = Messages.getText("MappingEpisodeNameDialog.SceneShowName"); + this.mappingColumn = Messages.getText("MappingEpisodeNameDialog.ProviderId"); + this.providerNameColumn = Messages.getText("MappingEpisodeNameDialog.ProviderName"); this.selectionForKeyPrefixList = selectionForKeyPrefixList; } } @@ -153,15 +130,11 @@ public SelectionForKeyPrefix(String name, String keyPrefix) { } } - @Getter - @Setter - @RequiredArgsConstructor private static class Row extends Vector { - @Serial - private static final long serialVersionUID = 8620670431074648999L; - private final String key; - private SerieMapping serieMapping; - private final SelectionForKeyPrefix selectionForKeyPrefix; + @Serial private static final long serialVersionUID = 8620670431074648999L; + @val String key; + @val SelectionForKeyPrefix selectionForKeyPrefix; + @var SerieMapping serieMapping; public Row(String key, String name, String providerId, String providerName, SerieMapping serieMapping, SelectionForKeyPrefix selectionForKeyPrefix) { @@ -174,29 +147,30 @@ public Row(String key, String name, String providerId, String providerName, Seri } } - @RequiredArgsConstructor + @AllArgsConstructor private static class MappingTableModel extends DefaultTableModel { - @Serial - private static final long serialVersionUID = 7860605766969472980L; - private final Manager manager; + @Serial private static final long serialVersionUID = 7860605766969472980L; + @val Manager manager; void setMappingType(MappingType mappingType) { - setDataVector(null, new String[] { mappingType.getNameColumn(), mappingType.getMappingColumn(), mappingType.getProviderNameColumn() }); - Arrays.stream(mappingType.getSelectionForKeyPrefixList()) - .flatMap(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix).stream() + setDataVector(null, new String[]{ mappingType.nameColumn, mappingType.mappingColumn, + mappingType.providerNameColumn }); + Arrays.stream(mappingType.selectionForKeyPrefixList) + .flatMap(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix) + .stream() .map(serieMappingPair -> { SerieMapping serieMapping = serieMappingPair.getValue(); - String name = serieMapping.getName(); - String providerId = serieMapping.getProviderId() == null ? "" : serieMapping.getProviderId(); - String providerName = serieMapping.getProviderName(); + String providerId = serieMapping.providerId == null ? "" : serieMapping.providerId; if (providerId.contains("/")) { providerId = providerId.substring(providerId.lastIndexOf("/") + 1); } providerId = providerId.replace(".html", ""); - return new Row(serieMappingPair.getKey(), name, providerId, providerName, serieMapping, selectionForKeyPrefix); + return new Row(serieMappingPair.getKey(), serieMapping.name, providerId, + serieMapping.providerName, serieMapping, selectionForKeyPrefix); })) - .sorted(Comparator.comparing(row -> row.getSerieMapping() == null || row.getSerieMapping().getProviderName() == null ? "zzz" - : row.getSerieMapping().getName())) + .sorted(Comparator.comparing( + row -> row.serieMapping == null || row.serieMapping.providerName == null ? "zzz" : + row.serieMapping.name)) .forEach(this::addRow); } @@ -212,17 +186,17 @@ private void initialize() { getContentPane().setLayout(new BorderLayout()); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); getContentPane().add(contentPanel, BorderLayout.CENTER); - GridBagLayout gbl_contentPanel = new GridBagLayout(); - gbl_contentPanel.columnWidths = new int[] { 0, 0 }; - gbl_contentPanel.rowHeights = new int[] { 0, 40, 0 }; - gbl_contentPanel.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gbl_contentPanel.rowWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; - contentPanel.setLayout(gbl_contentPanel); + GridBagLayout gblContentPanel = new GridBagLayout(); + gblContentPanel.columnWidths = new int[]{ 0, 0 }; + gblContentPanel.rowHeights = new int[]{ 0, 40, 0 }; + gblContentPanel.columnWeights = new double[]{ 1.0, Double.MIN_VALUE }; + gblContentPanel.rowWeights = new double[]{ 0.0, 1.0, Double.MIN_VALUE }; + contentPanel.setLayout(gblContentPanel); { JPanel selectionPane = new JPanel(); contentPanel.add(selectionPane); - JLabel lblDefaultIncomingFolder = new JLabel(Messages.getString("MappingEpisodeNameDialog.SelectProvider")); + JLabel lblDefaultIncomingFolder = new JLabel(Messages.getText("MappingEpisodeNameDialog.SelectProvider")); selectionPane.add(lblDefaultIncomingFolder); JComboBox mappingTypeList = new JComboBox<>(); @@ -233,20 +207,20 @@ private void initialize() { } { JPanel pnlButtons = new JPanel(); - GridBagConstraints gbc_pnlButtons = new GridBagConstraints(); - gbc_pnlButtons.insets = new Insets(0, 0, 5, 0); - gbc_pnlButtons.fill = GridBagConstraints.BOTH; - gbc_pnlButtons.gridx = 0; - gbc_pnlButtons.gridy = 0; - contentPanel.add(pnlButtons, gbc_pnlButtons); + GridBagConstraints gbcPnlButtons = new GridBagConstraints(); + gbcPnlButtons.insets = new Insets(0, 0, 5, 0); + gbcPnlButtons.fill = GridBagConstraints.BOTH; + gbcPnlButtons.gridx = 0; + gbcPnlButtons.gridy = 0; + contentPanel.add(pnlButtons, gbcPnlButtons); } { JScrollPane scrollPane = new JScrollPane(); - GridBagConstraints gbc_scrollPane = new GridBagConstraints(); - gbc_scrollPane.fill = GridBagConstraints.BOTH; - gbc_scrollPane.gridx = 0; - gbc_scrollPane.gridy = 1; - contentPanel.add(scrollPane, gbc_scrollPane); + GridBagConstraints gbcScrollPane = new GridBagConstraints(); + gbcScrollPane.fill = GridBagConstraints.BOTH; + gbcScrollPane.gridx = 0; + gbcScrollPane.gridy = 1; + contentPanel.add(scrollPane, gbcScrollPane); { table = new JTable(); @@ -262,21 +236,18 @@ private void initialize() { buttonPane.setLayout(new MigLayout("", "[25px][50px][grow][50px][grow][50px][25px]", "[][25px,grow,fill]")); { - JButton btnDeleteSelectedRow = new JButton(Messages.getString("MappingEpisodeNameDialog.DeleteRow")); - btnDeleteSelectedRow.addActionListener(arg0 -> { + JButton btnDeleteSelectedRow = new JButton(Messages.getText("MappingEpisodeNameDialog.DeleteRow")); + btnDeleteSelectedRow.addActionListener(_ -> { int rowNbr = table.convertRowIndexToModel(table.getSelectedRow()); MappingTableModel model = (MappingTableModel) table.getModel(); Row row = (Row) model.getDataVector().get(rowNbr); - String key = row.getKey(); - manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(key) - .remove(); - if (row.getSelectionForKeyPrefix().deleteOtherFunction() != null) { + String key = row.key; + manager.valueBuilder().cacheType(CacheType.DISK).key(key).remove(); + if (row.selectionForKeyPrefix.deleteOtherFunction() != null) { manager.valueBuilder() .cacheType(CacheType.DISK) - .key(row.getSelectionForKeyPrefix().deleteOtherFunction().apply(key)) + .key(row.selectionForKeyPrefix.deleteOtherFunction().apply(key)) .remove(); } model.removeRow(rowNbr); @@ -285,49 +256,49 @@ private void initialize() { } { - btnAddCustomMapping.withActionListener(() -> { + btnAddCustomMapping.actionListener(() -> { int rowNbr = table.convertRowIndexToModel(table.getSelectedRow()); MappingTableModel model = (MappingTableModel) table.getModel(); Row row = (Row) model.getDataVector().get(rowNbr); - String currentName = row.getSerieMapping().getName(); + String currentName = row.serieMapping.name; - String message = Messages.getString("MappingEpisodeNameDialog.enterNewNameForSerie", currentName); - selectedSubtitleProvider.ifPresent(provider -> { - userInteractionHandler.enter(message, message).ifPresent(newName -> { - TvRelease tvRelease = TvRelease.builder() - .name(currentName) - .season(row.getSerieMapping().getSeason()) - .episode(1) - .originalName(currentName) - .customName(newName).build(); - try { - provider.getProviderSerieId(tvRelease).ifPresentOrElse(providerSerieId -> { - SerieMapping newSerieMapping = - new SerieMapping(currentName, providerSerieId.getProviderId(), providerSerieId.getProviderName(), - providerSerieId.getSeason()); - row.setSerieMapping(newSerieMapping); - List sortKeys = table.getRowSorter().getSortKeys(); - selectMappingType(selectedMappingType); - table.getRowSorter().setSortKeys(sortKeys); - }, () -> userInteractionHandler.message( - Messages.getString("MappingEpisodeNameDialog.NoResultsFoundForSerieName", newName), - Messages.getString("App.Info"))); - } catch (Exception e) { - userInteractionHandler.message( - Messages.getString("App.ErrorOccurred", e.getMessage()), Messages.getString("App.Error")); - } - }); - }); + String message = Messages.getText("MappingEpisodeNameDialog.enterNewNameForSerie", currentName); + selectedSubtitleProvider.ifPresent( + provider -> userInteractionHandler.enter(message, message).ifPresent(newName -> { + TvRelease tvRelease = TvRelease.builder() + .name(currentName) + .season(row.serieMapping.season) + .episode(1) + .originalName(currentName) + .customName(newName) + .build(); + try { + provider.getProviderSerieId(tvRelease).ifPresentOrElse(providerSerieId -> { + SerieMapping newSerieMapping = + new SerieMapping(currentName, providerSerieId.providerId, + providerSerieId.providerName, providerSerieId.season); + row.serieMapping = newSerieMapping; + List sortKeys = table.getRowSorter().getSortKeys(); + selectMappingType(selectedMappingType); + table.getRowSorter().setSortKeys(sortKeys); + }, () -> userInteractionHandler.message( + Messages.getText("MappingEpisodeNameDialog.NoResultsFoundForSerieName", + newName), Messages.getText("App.Info"))); + } catch (Exception e) { + userInteractionHandler.message( + Messages.getText("App.ErrorOccurred", e.getMessage()), + Messages.getText("App.Error")); + } + })); }); buttonPane.add(btnAddCustomMapping, "skip"); } { - new JButton(Messages.getString("App.Close")) - .defaultButtonFor(getRootPane()) - .withActionListener(() -> setVisible(false)) - .withActionCommand(Messages.getString("App.Close")) + new JButton(Messages.getText("App.Close")).defaultButtonFor(getRootPane()) + .actionListener(() -> setVisible(false)) + .actionCommand(Messages.getText("App.Close")) .addTo(buttonPane, "skip"); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java index 93057c77..5c963352 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java @@ -1,10 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; -import javax.swing.JDialog; -import javax.swing.JFrame; - -import java.awt.Frame; -import java.awt.Rectangle; +import javax.swing.*; +import java.awt.*; import java.io.Serial; public class MultiSubDialog extends JDialog { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java index 01a1be45..5aa95821 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java @@ -1,23 +1,15 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; import java.io.Serial; import java.util.concurrent.atomic.AtomicInteger; -import javax.swing.JButton; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTabbedPane; -import javax.swing.SwingConstants; -import javax.swing.border.EmptyBorder; - import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.multisubdownloader.framework.event.Event; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.JButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.container.ContainerExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; import org.lodder.subtools.multisubdownloader.gui.panels.preference.EpisodeLibraryPanel; import org.lodder.subtools.multisubdownloader.gui.panels.preference.GeneralPanel; import org.lodder.subtools.multisubdownloader.gui.panels.preference.MovieLibraryPanel; @@ -28,29 +20,21 @@ import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import java.awt.BorderLayout; -import java.awt.FlowLayout; - -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ JComponentExtension.class, ContainerExtension.class, AbstractButtonExtension.class, JButtonExtension.class }) public class PreferenceDialog extends MultiSubDialog { - @Serial - private static final long serialVersionUID = -4910124272966075979L; + @Serial private static final long serialVersionUID = -4910124272966075979L; private final SettingsControl settingsCtrl; private final Emitter eventEmitter; - private final GeneralPanel pnlGeneral; private final EpisodeLibraryPanel pnlEpisodeLibrary; private final MovieLibraryPanel pnlMovieLibrary; private final OptionsPanel pnlOptions; private final SerieProvidersPanel pnlSerieSources; - public PreferenceDialog(GUI gui, final SettingsControl settingsCtrl, Emitter eventEmitter, - Manager manager, UserInteractionHandler userInteractionHandler) { - super(gui, Messages.getString("PreferenceDialog.Title"), true); + public PreferenceDialog(GUI gui, final SettingsControl settingsCtrl, Emitter eventEmitter, Manager manager, + UserInteractionHandler userInteractionHandler) { + super(gui, Messages.getText("PreferenceDialog.Title"), true); this.settingsCtrl = settingsCtrl; this.eventEmitter = eventEmitter; @@ -68,10 +52,12 @@ public PreferenceDialog(GUI gui, final SettingsControl settingsCtrl, Emitter eve tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); tabbedPane.addChangeListener(l -> { if (tabbedPane.getSelectedIndex() != selectedIndex.get()) { - PreferencePanelIntf sourcePanel = (PreferencePanelIntf) tabbedPane.getComponentAt(selectedIndex.get()); + PreferencePanelIntf sourcePanel = + (PreferencePanelIntf) tabbedPane.getComponentAt(selectedIndex.get()); if (!sourcePanel.hasValidSettings()) { tabbedPane.setSelectedIndex(selectedIndex.get()); - JOptionPane.showMessageDialog(this, Messages.getString("PreferenceDialog.invalidInput"), "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, Messages.getText("PreferenceDialog.invalidInput"), + "Error", JOptionPane.ERROR_MESSAGE); } else { selectedIndex.set(tabbedPane.getSelectedIndex()); } @@ -80,42 +66,39 @@ public PreferenceDialog(GUI gui, final SettingsControl settingsCtrl, Emitter eve contentPanel.add(tabbedPane); this.pnlGeneral = new GeneralPanel(gui, settingsCtrl); - tabbedPane.addTab(Messages.getString("PreferenceDialog.TabGeneral"), null, pnlGeneral, null); + tabbedPane.addTab(Messages.getText("PreferenceDialog.TabGeneral"), null, pnlGeneral, null); this.pnlEpisodeLibrary = - new EpisodeLibraryPanel(settingsCtrl.getSettings().getEpisodeLibrarySettings(), manager, false, userInteractionHandler); - tabbedPane.addTab(Messages.getString("PreferenceDialog.SerieLibrary"), null, pnlEpisodeLibrary, null); + new EpisodeLibraryPanel(settingsCtrl.settings.episodeLibrarySettings, manager, false, + userInteractionHandler); + tabbedPane.addTab(Messages.getText("PreferenceDialog.SerieLibrary"), null, pnlEpisodeLibrary, null); - this.pnlMovieLibrary = - new MovieLibraryPanel(settingsCtrl.getSettings().getMovieLibrarySettings(), manager, false, userInteractionHandler); - tabbedPane.addTab(Messages.getString("PreferenceDialog.MovieLibrary"), null, pnlMovieLibrary, null); + this.pnlMovieLibrary = new MovieLibraryPanel(settingsCtrl.settings.movieLibrarySettings, manager, false, + userInteractionHandler); + tabbedPane.addTab(Messages.getText("PreferenceDialog.MovieLibrary"), null, pnlMovieLibrary, null); this.pnlOptions = new OptionsPanel(settingsCtrl); - tabbedPane.addTab(Messages.getString("PreferenceDialog.Options"), null, pnlOptions, null); + tabbedPane.addTab(Messages.getText("PreferenceDialog.Options"), null, pnlOptions, null); this.pnlSerieSources = new SerieProvidersPanel(settingsCtrl); - tabbedPane.addTab(Messages.getString("PreferenceDialog.SerieSources"), null, pnlSerieSources, null); + tabbedPane.addTab(Messages.getText("PreferenceDialog.SerieSources"), null, pnlSerieSources, null); } { - new JPanel().layout(new FlowLayout(FlowLayout.RIGHT)).addTo(getContentPane(), BorderLayout.SOUTH) - .addComponent( - new JButton(Messages.getString("App.OK")) - .defaultButtonFor(getRootPane()) - .withActionListener(this::testAndSaveValues) - .actionCommand(Messages.getString("App.OK"))) + new JPanel().layout(new FlowLayout(FlowLayout.RIGHT)) + .addTo(getContentPane(), BorderLayout.SOUTH) + .addComponent(new JButton(Messages.getText("App.OK")).defaultButtonFor(getRootPane()) + .actionListener(this::testAndSaveValues) + .actionCommand(Messages.getText("App.OK"))) .addComponent( - new JButton(Messages.getString("App.Cancel")) - .withActionListener(() -> setVisible(false)) + new JButton(Messages.getText("App.Cancel")).actionListener(() -> setVisible(false)) .actionCommand("Cancel")); } } private void testAndSaveValues() { - if (pnlGeneral.hasValidSettings() && - pnlEpisodeLibrary.hasValidSettings() && - pnlMovieLibrary.hasValidSettings() && - pnlOptions.hasValidSettings() && + if (pnlGeneral.hasValidSettings() && pnlEpisodeLibrary.hasValidSettings() && + pnlMovieLibrary.hasValidSettings() && pnlOptions.hasValidSettings() && pnlSerieSources.hasValidSettings()) { pnlGeneral.savePreferenceSettings(); pnlEpisodeLibrary.savePreferenceSettings(); @@ -126,7 +109,8 @@ private void testAndSaveValues() { settingsCtrl.store(); this.eventEmitter.fire(new Event("providers.settings.change")); } else { - JOptionPane.showMessageDialog(this, Messages.getString("PreferenceDialog.invalidInput"), "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, Messages.getText("PreferenceDialog.invalidInput"), "Error", + JOptionPane.ERROR_MESSAGE); } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java index 129000c0..db82034a 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java @@ -5,7 +5,6 @@ import java.awt.event.WindowEvent; import java.io.Serial; -import lombok.Getter; import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.gui.extra.progress.Messenger; @@ -15,29 +14,29 @@ public class ProgressDialog extends MultiSubDialog implements Messenger { @Serial private static final long serialVersionUID = -2320149791421648965L; - @Getter + + private final Cancelable worker; private JProgressBar progressBar; private JLabel label; - private final Cancelable worker; public ProgressDialog(JFrame frame, Cancelable sft) { - super(frame, Messages.getString("ProgressDialog.Title"), false); + super(frame, Messages.getText("ProgressDialog.Title"), false); worker = sft; StatusMessenger.instance.addListener(this); - initialize_ui(); + initializeUi(); setDialogLocation(frame); repaint(); } public ProgressDialog(Cancelable sft) { - super(Messages.getString("ProgressDialog.Title"), false); + super(Messages.getText("ProgressDialog.Title"), false); worker = sft; StatusMessenger.instance.addListener(this); - initialize_ui(); + initializeUi(); repaint(); } - private void initialize_ui() { + private void initializeUi() { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { @@ -55,7 +54,7 @@ public void windowClosing(WindowEvent e) { getContentPane().add(progressBar, "cell 1 1,grow"); JButton btnStop = new JButton("Stop!"); - btnStop.addActionListener(arg0 -> worker.cancel(true)); + btnStop.addActionListener(_ -> worker.cancel(true)); getContentPane().add(btnStop, "cell 1 2 1 2,alignx left"); } @@ -75,11 +74,11 @@ public void message(String message) { public void updateProgress(int progress) { if (progress == 0) { - getProgressBar().setIndeterminate(true); + progressBar.setIndeterminate(true); } else { - getProgressBar().setIndeterminate(false); - getProgressBar().setValue(progress); - getProgressBar().setString(Integer.toString(progress)); + progressBar.setIndeterminate(false); + progressBar.setValue(progress); + progressBar.setString(Integer.toString(progress)); } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java index 0fcefca6..d2ad1105 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import javax.swing.*; +import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; @@ -11,24 +13,15 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingWorker; - +import com.google.common.collect.Streams; +import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.set; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.RenameAction; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.JButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.container.ContainerExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldPath; import org.lodder.subtools.multisubdownloader.gui.panels.preference.EpisodeLibraryPanel; import org.lodder.subtools.multisubdownloader.gui.panels.preference.MovieLibraryPanel; @@ -41,27 +34,15 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.FileUtils; -import org.lodder.subtools.sublibrary.util.StreamExtension; - -import com.google.common.collect.Streams; - -import java.awt.BorderLayout; -import java.awt.FlowLayout; -import lombok.Setter; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JTextFieldExtension.class, ContainerExtension.class, JButtonExtension.class, AbstractButtonExtension.class, - JComponentExtension.class }) public class RenameDialog extends MultiSubDialog implements PropertyChangeListener { - @Serial - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; + private final VideoLibraryPanel pnlLibrary; private final MyTextFieldPath txtFolder; private final JCheckBox chkRecursive; + private ProgressDialog progressDialog; public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String title, Manager manager, @@ -70,35 +51,38 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String setResizable(false); setBounds(100, 100, 650, 680); getContentPane().setLayout(new MigLayout("fill, nogrid", "[]", "[][]20:push[]")); - TitlePanel.title(Messages.getString("PreferenceDialog.Settings")) - .padding(0).paddingLeft(20).fillContents(true).addTo(getContentPane(), "span, grow, wrap") - .addComponent("shrink", new JLabel(Messages.getString("PreferenceDialog.Location"))) - .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().withColumns(20)) - .addComponent("shrink, wrap", new JButton(Messages.getString("App.Browse")) - .withActionListener(() -> MemoryFolderChooser.getInstance() - .selectDirectory(getContentPane(), Messages.getString("PreferenceDialog.SelectFolderForRenameReplace")) + + TitlePanel.title(Messages.getText("PreferenceDialog.Settings")) + .padding(0) + .paddingLeft(20) + .fillContents(true) + .addTo(getContentPane(), "span, grow, wrap") + .addComponent("shrink", new JLabel(Messages.getText("PreferenceDialog.Location"))) + .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().columns(20)) + .addComponent("shrink, wrap", new JButton(Messages.getText("App.Browse")).actionListener( + () -> MemoryFolderChooser.getInstance() + .selectDirectory(getContentPane(), + Messages.getText("PreferenceDialog.SelectFolderForRenameReplace")) .ifPresent(txtFolder::setObject))) - .addComponent("wrap", this.chkRecursive = new JCheckBox(Messages.getString("RenameDialog.RecursiveSearch"))); + .addComponent("wrap", + this.chkRecursive = new JCheckBox(Messages.getText("RenameDialog.RecursiveSearch"))); if (videoType == VideoType.EPISODE) { - pnlLibrary = new EpisodeLibraryPanel(settings.getEpisodeLibrarySettings(), manager, true, userInteractionHandler) - .addTo(getContentPane(), "grow"); + pnlLibrary = new EpisodeLibraryPanel(settings.episodeLibrarySettings, manager, true, + userInteractionHandler).addTo(getContentPane(), "grow"); } else { - pnlLibrary = new MovieLibraryPanel(settings.getMovieLibrarySettings(), manager, true, userInteractionHandler) - .addTo(getContentPane(), "grow"); + pnlLibrary = + new MovieLibraryPanel(settings.movieLibrarySettings, manager, true, userInteractionHandler).addTo( + getContentPane(), "grow"); } - new JPanel().layout(new FlowLayout(FlowLayout.RIGHT)).addTo(getContentPane(), BorderLayout.SOUTH) - .addComponent( - new JButton(Messages.getString("RenameDialog.Rename")) - .defaultButtonFor(getRootPane()) - .withActionListener(() -> rename(videoType, settings, manager, userInteractionHandler)) - .withActionCommand("Rename")) - .addComponent( - new JButton(Messages.getString("App.Cancel")) - .withActionListener(() -> setVisible(false)) - .actionCommand("Cancel")); - + new JPanel().layout(new FlowLayout(FlowLayout.RIGHT)) + .addTo(getContentPane(), BorderLayout.SOUTH) + .addComponent(new JButton(Messages.getText("RenameDialog.Rename")).defaultButtonFor(getRootPane()) + .actionListener(() -> rename(videoType, settings, manager, userInteractionHandler)) + .actionCommand("Rename")) + .addComponent(new JButton(Messages.getText("App.Cancel")).actionListener(() -> setVisible(false)) + .actionCommand("Cancel")); } private boolean hasValidSettings() { @@ -109,15 +93,17 @@ private void rename(VideoType videoType, Settings settings, Manager manager, UserInteractionHandler userInteractionHandler) { if (!hasValidSettings()) { - JOptionPane.showMessageDialog(this, Messages.getString("PreferenceDialog.invalidInput"), "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, Messages.getText("PreferenceDialog.invalidInput"), "Error", + JOptionPane.ERROR_MESSAGE); return; } setVisible(false); pnlLibrary.savePreferenceSettings(); - TypedRenameWorker renameWorker = new TypedRenameWorker(txtFolder.getObject(), pnlLibrary.getLibrarySettings(), videoType, - this.chkRecursive.isSelected(), manager, userInteractionHandler); + TypedRenameWorker renameWorker = + new TypedRenameWorker(txtFolder.getObject(), pnlLibrary.librarySettings, videoType, + this.chkRecursive.isSelected(), manager, userInteractionHandler); renameWorker.addPropertyChangeListener(this); - renameWorker.setReleaseFactory(new ReleaseFactory(settings, manager)); + renameWorker.releaseFactory = new ReleaseFactory(settings, manager); progressDialog = new ProgressDialog(renameWorker); progressDialog.setVisible(true); renameWorker.execute(); @@ -131,12 +117,12 @@ public void propertyChange(PropertyChangeEvent event) { } else { final int progress = renameWorker.getProgress(); progressDialog.updateProgress(progress); - StatusMessenger.instance.message(Messages.getString("RenameDialog.StatusRename")); + StatusMessenger.instance.message(Messages.getText("RenameDialog.StatusRename")); } } } - @ExtensionMethod({ FileUtils.class, Files.class, StreamExtension.class }) + @ExtensionMethod({ Files.class }) private static class TypedRenameWorker extends SwingWorker implements Cancelable { private final UserInteractionHandler userInteractionHandler; @@ -144,14 +130,14 @@ private static class TypedRenameWorker extends SwingWorker impleme private final VideoType videoType; private final Set extensions; private final boolean isRecursive; - @Setter - private ReleaseFactory releaseFactory; private final RenameAction renameAction; + @set ReleaseFactory releaseFactory; - public TypedRenameWorker(Path dir, LibrarySettings librarySettings, VideoType videoType, - boolean isRecursive, Manager manager, UserInteractionHandler userInteractionHandler) { + public TypedRenameWorker(Path dir, LibrarySettings librarySettings, VideoType videoType, boolean isRecursive, + Manager manager, UserInteractionHandler userInteractionHandler) { this.userInteractionHandler = userInteractionHandler; - this.extensions = Streams.concat(VideoPatterns.EXTENSIONS.stream(), Stream.of("srt")).collect(Collectors.toUnmodifiableSet()); + this.extensions = Streams.concat(VideoPatterns.EXTENSIONS.stream(), Stream.of("srt")) + .collect(Collectors.toUnmodifiableSet()); this.dir = dir; this.videoType = videoType; this.isRecursive = isRecursive; @@ -170,8 +156,8 @@ private void rename(Path dir) throws IOException { if (!file.fileNameContainsIgnoreCase("sample") && extensions.contains(file.getExtension())) { Release release = releaseFactory.createRelease(file, userInteractionHandler); if (release != null) { - publish(release.getFileName()); - if (release.getVideoType() == videoType) { + publish(release.fileName); + if (release.videoType == videoType) { renameAction.rename(file, release); } } @@ -184,7 +170,7 @@ private void rename(Path dir) throws IOException { @Override protected void process(List data) { - data.forEach(s -> StatusMessenger.instance.message(Messages.getString("MainWindow.RenamingFile", s))); + data.forEach(s -> StatusMessenger.instance.message(Messages.getText("MainWindow.RenamingFile", s))); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java index 3442170c..a9415f8e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java @@ -8,34 +8,31 @@ import java.util.List; import java.util.stream.IntStream; -import lombok.experimental.ExtensionMethod; import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.gui.extra.table.CustomTable; import org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName; import org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableModel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.JButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -@ExtensionMethod({ JButtonExtension.class, AbstractButtonExtension.class, JComponentExtension.class }) public class SelectDialog extends MultiSubDialog { - @Serial - private static final long serialVersionUID = -4092909537478305235L; - private List selectedSubtitleIdxs; + @Serial private static final long serialVersionUID = -4092909537478305235L; + private final List subtitles; private final Release release; + + private List selectedSubtitleIdxs; private CustomTable customTable; /** * Create the dialog. */ public SelectDialog(JFrame frame, List subtitles, Release release) { - super(frame, Messages.getString("SelectDialog.SelectCorrectSubtitle"), true); - this.subtitles = subtitles.stream().distinct().sorted(Comparator.comparing(Subtitle::getScore).reversed()).toList(); + super(frame, Messages.getText("SelectDialog.SelectCorrectSubtitle"), true); + this.subtitles = + subtitles.stream().distinct().sorted(Comparator.comparing(Subtitle::getScore).reversed()).toList(); this.release = release; initialize(); pack(); @@ -46,8 +43,7 @@ public SelectDialog(JFrame frame, List subtitles, Release release) { private void initialize() { getContentPane().setLayout(new MigLayout("", "[1000px:n,grow,fill]", "[][::100px,fill][grow]")); JLabel lblNewLabel = - new JLabel(Messages.getString("SelectDialog.SelectCorrectSubtitleThisRelease") - + release.getFileName()); + new JLabel(Messages.getText("SelectDialog.SelectCorrectSubtitleThisRelease") + release.fileName); getContentPane().add(lblNewLabel, "cell 0 0"); { JScrollPane scrollPane = new JScrollPane(); @@ -58,30 +54,20 @@ private void initialize() { buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); getContentPane().add(buttonPane, "cell 0 2,grow"); - new JButton(Messages.getString("App.OK")) - .defaultButtonFor(getRootPane()) - .withActionListener(() -> { - selectedSubtitleIdxs = getSelectedIdxs(); - setVisible(false); - }) - .withActionCommand(Messages.getString("App.OK")) - .addTo(buttonPane); - - new JButton(Messages.getString("SelectDialog.Everything")) - .withActionListener(() -> { - selectedSubtitleIdxs = IntStream.range(0, release.getMatchingSubs().size()).boxed().toList(); - setVisible(false); - }) - .withActionCommand(Messages.getString("App.All")) - .addTo(buttonPane); - - new JButton(Messages.getString("App.Cancel")) - .withActionListener(() -> { - selectedSubtitleIdxs = List.of(); - setVisible(false); - }) - .withActionCommand(Messages.getString("App.Cancel")) - .addTo(buttonPane); + new JButton(Messages.getText("App.OK")).defaultButtonFor(getRootPane()).actionListener(() -> { + selectedSubtitleIdxs = getSelectedIdxs(); + setVisible(false); + }).actionCommand(Messages.getText("App.OK")).addTo(buttonPane); + + new JButton(Messages.getText("SelectDialog.Everything")).actionListener(() -> { + selectedSubtitleIdxs = IntStream.range(0, release.getMatchingSubs().size()).boxed().toList(); + setVisible(false); + }).actionCommand(Messages.getText("App.All")).addTo(buttonPane); + + new JButton(Messages.getText("App.Cancel")).actionListener(() -> { + selectedSubtitleIdxs = List.of(); + setVisible(false); + }).actionCommand(Messages.getText("App.Cancel")).addTo(buttonPane); } } @@ -117,8 +103,10 @@ private CustomTable createCustomTable() { private List getSelectedIdxs() { return IntStream.range(0, customTable.getModel().getRowCount()) - .filter(i -> (boolean) customTable.getModel().getValueAt(i, customTable.getColumnIdByName(SubtitleTableColumnName.SELECT))) - .boxed().toList(); + .filter(i -> (boolean) customTable.getModel() + .getValueAt(i, customTable.getColumnIdByName(SubtitleTableColumnName.SELECT))) + .boxed() + .toList(); } public List getSelection() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java index 7906d5f7..d30f9dcd 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java @@ -8,18 +8,10 @@ import java.awt.event.MouseListener; import java.io.Serial; import java.nio.file.Path; -import java.util.Arrays; import java.util.function.Function; -import lombok.experimental.ExtensionMethod; import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.Messages; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.JButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.component.ComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.container.ContainerExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.lib.ReleaseFactory; import org.lodder.subtools.multisubdownloader.lib.library.LibraryBuilder; import org.lodder.subtools.multisubdownloader.settings.model.Settings; @@ -34,17 +26,16 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -@ExtensionMethod({ JButtonExtension.class, AbstractButtonExtension.class, JComponentExtension.class, ContainerExtension.class, - ComponentExtension.class, JTextFieldExtension.class }) public class StructureBuilderDialog extends MultiSubDialog implements DocumentListener { - @Serial - private static final long serialVersionUID = -5174968778375028124L; + @Serial private static final long serialVersionUID = -5174968778375028124L; + private final VideoType videoType; private final StructureType structureType; private final Manager manager; private final UserInteractionHandler userInteractionHandler; private final Function libraryBuilder; + private JTextField txtStructure; private JLabel lblPreview; private TvRelease tvRelease; @@ -65,16 +56,16 @@ public StructureBuilderDialog(JFrame frame, String title, boolean modal, VideoTy this.manager = manager; this.userInteractionHandler = userInteractionHandler; this.libraryBuilder = filenameLibraryBuilder; - initializeUi(); + initializeUI(); generateVideoFiles(); } - private void initializeUi() { + private void initializeUI() { setBounds(100, 100, 600, 300); setMinimumSize(new Dimension(600, 300)); Container panel = getContentPane().layout(new MigLayout("insets 10, nogrid")); - new JLabel(Messages.getString("StructureBuilderDialog.AvailableTagsClickToAdd")).addTo(panel, "wrap"); + new JLabel(Messages.getText("StructureBuilderDialog.AvailableTagsClickToAdd")).addTo(panel, "wrap"); this.tagPanel = new JPanel(new MigLayout("flowy, wrap 5", "[150px][150px][150px]")).addTo(panel, "grow, wrap"); { @@ -90,52 +81,44 @@ private void initializeUi() { } } - new JLabel(Messages.getString("StructureBuilderDialog.Structure")).addTo(panel); - this.txtStructure = new JTextField().withColumns(100).addTo(panel, "span, wrap"); + new JLabel(Messages.getText("StructureBuilderDialog.Structure")).addTo(panel); + this.txtStructure = new JTextField().columns(100).addTo(panel, "span, wrap"); this.txtStructure.getDocument().addDocumentListener(this); - new JLabel(Messages.getString("StructureBuilderDialog.Preview")).addTo(panel); + new JLabel(Messages.getText("StructureBuilderDialog.Preview")).addTo(panel); this.lblPreview = new JLabel("").addTo(panel); new JPanel(new FlowLayout(FlowLayout.RIGHT)).addTo(panel, BorderLayout.SOUTH) .addComponent( - new JButton(Messages.getString("App.OK")) - .defaultButtonFor(getRootPane()) - .withActionListener(e -> { - setVisible(false); - dispose(); // this is needed to dispose the dialog and return the control to the window - }) - .withActionCommand("OK")) - .addComponent(new JButton(Messages.getString("App.Cancel")) - .withActionListener(e -> { + new JButton(Messages.getText("App.OK")).defaultButtonFor(getRootPane()).actionListener(_ -> { setVisible(false); - txtStructure.setText(oldStructure); dispose(); // this is needed to dispose the dialog and return the control to the window - }) - .withActionCommand("Cancel")); + }).actionCommand("OK")) + .addComponent(new JButton(Messages.getText("App.Cancel")).actionListener(_ -> { + setVisible(false); + txtStructure.setText(oldStructure); + dispose(); // this is needed to dispose the dialog and return the control to the window + }).actionCommand("Cancel")); } private void generateVideoFiles() { ReleaseFactory releaseFactory = new ReleaseFactory(new Settings(), manager); - if (videoType == VideoType.EPISODE) { - tvRelease = (TvRelease) releaseFactory.createRelease( - Path.of("Terra.Nova.S01E01E02.Genesis.720p.HDTV.x264-ORENJI.mkv"), - userInteractionHandler); - } else if (videoType == VideoType.MOVIE) { - movieRelease = (MovieRelease) releaseFactory.createRelease(Path.of("Final.Destination.5.720p.Bluray.x264-TWiZTED"), - userInteractionHandler); + switch (videoType) { + case EPISODE -> tvRelease = (TvRelease) releaseFactory.createRelease( + Path.of("Terra.Nova.S01E01E02.Genesis.720p.HDTV.x264-ORENJI.mkv"), userInteractionHandler); + case MOVIE -> movieRelease = (MovieRelease) releaseFactory.createRelease( + Path.of("Final.Destination.5.720p.Bluray.x264-TWiZTED"), userInteractionHandler); } } private void buildLabelTable(StructureTag[] structureTags) { - Arrays.stream(structureTags).forEach(this::addTag); + structureTags.forEach(this::addTag); } private void addTag(StructureTag structureTag) { - new JLabel(structureTag.getLabel()) - .withToolTipText(structureTag.getDescription()) + new JLabel(structureTag.label).withToolTipText(structureTag.description) .addTo(tagPanel) - .withMouseListener(new InsertTag()); + .mouseListener(new InsertTag()); } public String showDialog(String structure) { @@ -192,7 +175,7 @@ public void mouseClicked(MouseEvent e) { beforeCaret = txtStructure.getText(); afterCaret = ""; } - txtStructure.setText(String.format("%s%s%s", beforeCaret, clickedTag, afterCaret)); + txtStructure.setText("%s%s%s".formatted(beforeCaret, clickedTag, afterCaret)); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java index 6df020aa..948cc5dd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java @@ -1,10 +1,11 @@ package org.lodder.subtools.multisubdownloader.gui.dialog.progress.search; -import javax.swing.JButton; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTable; +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.Serial; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.ActionException; @@ -14,12 +15,6 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.model.Release; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.Serial; - -import net.miginfocom.swing.MigLayout; - public class SearchProgressDialog extends MultiSubDialog implements SearchProgressListener { @Serial @@ -31,12 +26,12 @@ public class SearchProgressDialog extends MultiSubDialog implements SearchProgre private boolean completed; public SearchProgressDialog(GUI window, Cancelable searchAction) { - super(window, Messages.getString("SearchProgressDialog.Title"), false); + super(window, Messages.getText("SearchProgressDialog.Title"), false); this.searchAction = searchAction; this.window = window; this.completed = false; - initialize_ui(); + initializeUi(); setDialogLocation(window); repaint(); } @@ -44,8 +39,7 @@ public SearchProgressDialog(GUI window, Cancelable searchAction) { @Override public void progress(SubtitleProvider provider, int jobsLeft, Release release) { this.setVisible(); - this.tableModel.update(provider.getName(), jobsLeft, - release == null ? "Done" : release.getFileName()); + this.tableModel.update(provider.getName(), jobsLeft, release == null ? "Done" : release.fileName); } @Override @@ -82,7 +76,7 @@ public void onStatus(String message) { this.window.setStatusMessage(message); } - private void initialize_ui() { + private void initializeUi() { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { @@ -108,8 +102,8 @@ public void windowClosing(WindowEvent e) { progressBar.setIndeterminate(true); getContentPane().add(progressBar, "cell 0 1 2 1,grow"); - JButton btnStop = new JButton(Messages.getString("SearchProgressDialog.Stop")); - btnStop.addActionListener(arg0 -> searchAction.cancel(true)); + JButton btnStop = new JButton(Messages.getText("SearchProgressDialog.Stop")); + btnStop.addActionListener(_ -> searchAction.cancel(true)); getContentPane().add(btnStop, "cell 1 2,alignx left"); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java index 63a5ab1a..4a241fac 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java @@ -17,9 +17,9 @@ public SearchProgressTableModel() { super(); this.setColumnCount(3); this.setColumnIdentifiers(new String[]{ - Messages.getString("SearchProgressTableModel.Source"), - Messages.getString("SearchProgressTableModel.Queue"), - Messages.getString("SearchProgressTableModel.Release")}); + Messages.getText("SearchProgressTableModel.Source"), + Messages.getText("SearchProgressTableModel.Queue"), + Messages.getText("SearchProgressTableModel.Release") }); } public void update(String source, int queue, String release) { @@ -42,7 +42,7 @@ private void updateRow(int rowNr, int queue, String release) { private void createRow(String source, int queue, String release) { this.rowMap.put(source, this.getRowCount()); - Object[] row = {source, queue, release}; + Object[] row = { source, queue, release }; this.addRow(row); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java index fad3084e..538b5cea 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java @@ -4,25 +4,19 @@ import java.awt.*; import java.io.Serial; -import lombok.Getter; -import lombok.Setter; +import manifold.ext.props.rt.api.var; -@Getter -@Setter public class ArrowButton extends JButton { - @Serial - private static final long serialVersionUID = -4630720317499130016L; + @Serial private static final long serialVersionUID = -4630720317499130016L; /** - * The cardinal direction of the arrow(s), - * any of {@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or {@link SwingConstants#EAST} + * The cardinal direction of the arrow(s), any of {@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}, + * {@link SwingConstants#WEST} or {@link SwingConstants#EAST} */ - private int direction; - - private int arrowCount; - - private int arrowSize; + @var int direction; + @var int arrowCount; + @var int arrowSize; public ArrowButton(int direction, int arrowCount, int arrowSize) { setMargin(new Insets(0, 2, 0, 2)); @@ -39,13 +33,10 @@ public Dimension getPreferredSize() { @Override public Dimension getMinimumSize() { - return new Dimension(arrowSize - * (direction == SwingConstants.EAST || direction == SwingConstants.WEST ? arrowCount : 3) - + getBorder().getBorderInsets(this).left + getBorder().getBorderInsets(this).right, - arrowSize - * (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH ? arrowCount : 3) - + getBorder().getBorderInsets(this).top - + getBorder().getBorderInsets(this).bottom); + return new Dimension(arrowSize * (direction == EAST || direction == WEST ? arrowCount : 3) + + getBorder().getBorderInsets(this).left + getBorder().getBorderInsets(this).right, + arrowSize * (direction == NORTH || direction == SOUTH ? arrowCount : 3) + + getBorder().getBorderInsets(this).top + getBorder().getBorderInsets(this).bottom); } @Override @@ -65,18 +56,10 @@ protected void paintComponent(Graphics g) { int w = getSize().width; int h = getSize().height; for (int i = 0; i < arrowCount; i++) { - paintArrow(g, - (w - arrowSize - * (direction == SwingConstants.EAST || direction == SwingConstants.WEST ? arrowCount : 1)) - / 2 - + arrowSize - * (direction == SwingConstants.EAST || direction == SwingConstants.WEST ? i : 0), - (h - arrowSize - * (direction == SwingConstants.EAST || direction == SwingConstants.WEST ? 1 : arrowCount)) - / 2 - + arrowSize - * (direction == SwingConstants.EAST || direction == SwingConstants.WEST ? 0 : i), - g.getColor()); + paintArrow(g, (w - arrowSize * (direction == EAST || direction == WEST ? arrowCount : 1)) / 2 + + arrowSize * (direction == EAST || direction == WEST ? i : 0), + (h - arrowSize * (direction == EAST || direction == WEST ? 1 : arrowCount)) / 2 + + arrowSize * (direction == EAST || direction == WEST ? 0 : i), g.getColor()); } g.setColor(oldColor); @@ -86,7 +69,7 @@ private void paintArrow(Graphics g, int x, int y, Color highlight) { int mid, i, j; Color oldColor = g.getColor(); - boolean isEnabled = isEnabled(); + boolean enabled = isEnabled(); j = 0; arrowSize = Math.max(arrowSize, 2); @@ -99,13 +82,13 @@ private void paintArrow(Graphics g, int x, int y, Color highlight) { for (i = 0; i < arrowSize; i++) { g.drawLine(mid - i, i, mid + i, i); } - if (!isEnabled) { + if (!enabled) { g.setColor(highlight); g.drawLine(mid - i + 2, i, mid + i, i); } } case SOUTH -> { - if (!isEnabled) { + if (!enabled) { g.translate(1, 1); g.setColor(highlight); for (i = arrowSize - 1; i >= 0; i--) { @@ -125,13 +108,13 @@ private void paintArrow(Graphics g, int x, int y, Color highlight) { for (i = 0; i < arrowSize; i++) { g.drawLine(i, mid - i, i, mid + i); } - if (!isEnabled) { + if (!enabled) { g.setColor(highlight); g.drawLine(i, mid - i + 2, i, mid + i); } } case EAST -> { - if (!isEnabled) { + if (!enabled) { g.translate(1, 1); g.setColor(highlight); for (i = arrowSize - 1; i >= 0; i--) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java index 30e7c4e7..81d8b515 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java @@ -13,26 +13,22 @@ public class ImageListCellRenderer extends JLabel implements ListCellRendererSource: - *

- * Return a component that has been configured to display the specified value. - * That component's paint method is then called to "render" the cell. - * If it is necessary to compute the dimensions of a list because the list cells do not have a fixed size, - * this method is called to generate a component on which getPreferredSize can be invoked. - *

- * jlist - the jlist we're painting - * value - the value returned by list.getModel().getElementAt(index). - * cellIndex - the cell index - * isSelected - true if the specified cell is currently selected - * cellHasFocus - true if the cell has focus - */ - @Override - public Component getListCellRendererComponent(JList jlist, Object value, int cellIndex, - boolean isSelected, boolean cellHasFocus) { - if (value instanceof JPanel) { - Component component = (Component) value; - if (isSelected) { + /** + * Source: + *

+ * Return a component that has been configured to display the specified value. That component's paint method is then + * called to "render" the cell. If it is necessary to compute the dimensions of a list because the list cells do not + * have a fixed size, this method is called to generate a component on which getPreferredSize can be invoked. + *

+ * jlist - the jlist we're painting value - the value returned by list.getModel().getElementAt(index). cellIndex - + * the cell index selected - true if the specified cell is currently selected cellHasFocus - true if the cell has + * focus + */ + @Override + public Component getListCellRendererComponent(JList jlist, Object value, int cellIndex, + boolean selected, boolean cellHasFocus) { + if (value instanceof JPanel component) { + if (selected) { component.setBackground(jlist.getSelectionBackground()); component.setForeground(jlist.getSelectionForeground()); } else { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java index db84b9f7..c23cfc62 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java @@ -10,13 +10,10 @@ import java.util.stream.Stream; import com.google.common.base.Objects; -import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; -import lombok.experimental.ExtensionMethod; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; +import manifold.ext.props.rt.api.val; -@ExtensionMethod({ JComponentExtension.class }) public class JListWithImages extends JList> { @Serial @@ -61,7 +58,8 @@ public void addItems(Image image, Collection values) { public void addItem(Image image, T value) { if (!distinctValues || !contains(value)) { - ((DefaultListModel>) getModel()).addElement(new LabelPanel<>(image, value, toStringMapper, SwingConstants.LEFT)); + ((DefaultListModel>) getModel()).addElement( + new LabelPanel<>(image, value, toStringMapper, SwingConstants.LEFT)); } } @@ -93,11 +91,10 @@ private Optional> getLabelPanel(int index) { return Optional.ofNullable(getModel().getElementAt(index)); } - @Getter public static class LabelPanel extends JPanel { private static final long serialVersionUID = 1L; - private final Label label; + @val Label label; LabelPanel(Image image, T object, Function toStringMapper, int horizontalAlignment) { this.label = new Label<>(image, object, toStringMapper, horizontalAlignment); @@ -106,19 +103,18 @@ public static class LabelPanel extends JPanel { } public T getObject() { - return label.getObject(); + return label.object; } public Image getImage() { - return label.getImage(); + return label.image; } } - @Getter private static class Label extends JLabel { private static final long serialVersionUID = 1L; - private final T object; - private final Image image; + @val T object; + @val Image image; Label(Image image, T object, Function toStringMapper, int horizontalAlignment) { super(toStringMapper.apply(object), getImageIcon(image), horizontalAlignment); @@ -131,7 +127,7 @@ private static ImageIcon getImageIcon(Image image) { } private static ImageIcon resizeIcon(ImageIcon icon, int width, int height) { - return new ImageIcon( icon.getImage().getScaledInstance(width, height, java.awt.Image.SCALE_SMOOTH)); + return new ImageIcon(icon.getImage().getScaledInstance(width, height, java.awt.Image.SCALE_SMOOTH)); } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java index fcb428d8..71e6bf63 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java @@ -1,20 +1,17 @@ package org.lodder.subtools.multisubdownloader.gui.extra; +import javax.swing.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import javax.swing.JTextArea; -import javax.swing.SwingUtilities; - -import org.slf4j.LoggerFactory; - import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.encoder.Encoder; +import org.slf4j.LoggerFactory; public class LogTextAppender extends AppenderBase { private final Encoder encoder = new EchoEncoder<>(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java index bda17fb2..43639260 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java @@ -11,8 +11,8 @@ public class MemoryFolderChooser { - private final JFileChooser chooser; private static MemoryFolderChooser instance; + private final JFileChooser chooser; private File memory; private MemoryFolderChooser() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java index dc7db710..63ec8b32 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java @@ -1,39 +1,26 @@ package org.lodder.subtools.multisubdownloader.gui.extra; -import java.io.Serial; - -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import javax.swing.SwingConstants; - -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; - -import java.awt.Component; -import java.awt.Container; -import java.awt.LayoutManager; +import javax.swing.*; +import java.awt.*; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; +import java.io.Serial; -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; -import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; -@ExtensionMethod({ JComponentExtension.class, JCheckBoxExtension.class }) public class PanelCheckBox extends JPanel { @Serial private static final long serialVersionUID = 1L; private final JCheckBox checkbox; - @Getter - private final JPanel panel; + @val JPanel panel; - private PanelCheckBox(JCheckBox checkbox, boolean panelOnNewLine, LayoutManager panelLayout, boolean addVerticalSeparator, int leftGap) { + private PanelCheckBox(JCheckBox checkbox, boolean panelOnNewLine, LayoutManager panelLayout, + boolean addVerticalSeparator, int leftGap) { super(new MigLayout("insets 0, novisualpadding, fillx")); this.checkbox = checkbox; this.panel = new JPanel(panelLayout) { @@ -52,7 +39,7 @@ protected void addImpl(Component comp, Object constraints, int index) { super.addImpl(checkbox, panelOnNewLine ? "span" : "", -1); super.addImpl(panel, "span, growx, " + (addVerticalSeparator ? "" : "gapx " + leftGap), -1); checkbox.addCheckedChangeListener(selected -> setEnabledChildren(panel, selected)); - JComponentExtension.setRecursive(this, this::addContainerListener); + this.setRecursive(this::addContainerListener); setEnabledChildren(panel, isSelected()); } @@ -144,7 +131,7 @@ public JPanel addTo(JComponent component) { public JPanel addTo(JComponent component, Object constraints) { PanelCheckBox panelCheckBox = build(); component.add(panelCheckBox, constraints); - return panelCheckBox.getPanel(); + return panelCheckBox.panel; } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java index 10bcfa4d..81bd7b4e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java @@ -10,7 +10,6 @@ import java.util.stream.IntStream; /** - * * @author author */ public class PartialDisableComboBox extends JComboBox { @@ -27,7 +26,8 @@ public PartialDisableComboBox(T[] items) { private static final long serialVersionUID = -2774241371293899669L; @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); boolean disabled = index >= 0 && index < itemsState.size() && !itemsState.get(index); c.setEnabled(!disabled); @@ -102,4 +102,8 @@ private int requireValidIndex(int index) { } return index; } + + public T getSelectedItem() { + return (T) super.getSelectedItem(); + } } \ No newline at end of file diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java index 2e3174e6..4f96a4e1 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java @@ -24,8 +24,8 @@ public void mouseReleased(MouseEvent e) { private synchronized void showPopup(MouseEvent e) { if (e.isPopupTrigger() - && e.getComponent() instanceof CustomTable customTable - && customTable.getModel().getRowCount() > 0) { + && e.getComponent() instanceof CustomTable customTable + && customTable.getModel().getRowCount() > 0) { popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java index 6c981d25..9ddd22ee 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java @@ -1,32 +1,22 @@ package org.lodder.subtools.multisubdownloader.gui.extra; +import javax.swing.*; +import java.awt.*; import java.io.Serial; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSeparator; - -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; - -import java.awt.Container; -import java.awt.LayoutManager; - -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; -import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.var; import net.miginfocom.swing.MigLayout; -@ExtensionMethod({ JComponentExtension.class, JCheckBoxExtension.class }) public class TitlePanel extends JPanel { @Serial private static final long serialVersionUID = 1L; - @Getter - private final JPanel panel; + @var JPanel panel; - private TitlePanel(String title, LayoutManager panelLayout, int marginTop, int marginLeft, int marginBottom, int marginRight) { + private TitlePanel(String title, LayoutManager panelLayout, int marginTop, int marginLeft, int marginBottom, + int marginRight) { super(new MigLayout("fillx, nogrid, insets %s %s %s %s".formatted(getPadding(marginTop), getPadding(marginLeft), getPadding(marginBottom), getPadding(marginRight)))); super.add(new JLabel(title)); @@ -148,13 +138,15 @@ public JPanel addTo(Container component) { public JPanel addTo(Container component, Object constraints) { if (panelLayout == null) { panelLayout = new MigLayout( - (fillContents ? "fill," : "") + (useGrid ? "" : "nogrid,") + "insets %s %s %s %s".formatted(getPadding(paddingTop), + (fillContents ? "fill," : "") + (useGrid ? "" : "nogrid,") + + "insets %s %s %s %s".formatted(getPadding(paddingTop), getPadding(paddingLeft), getPadding(paddingBottom), getPadding(paddingRight)), panelColumnConstraints); } - TitlePanel titlePanel = new TitlePanel(title, panelLayout, marginTop, marginLeft, marginBottom, marginRight); + TitlePanel titlePanel = + new TitlePanel(title, panelLayout, marginTop, marginLeft, marginBottom, marginRight); component.add(titlePanel, constraints); - return titlePanel.getPanel(); + return titlePanel.panel; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java index 03ab0cca..cee0d2b1 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java @@ -1,6 +1,6 @@ package org.lodder.subtools.multisubdownloader.gui.extra.progress; -import javax.swing.JLabel; +import javax.swing.*; import java.io.Serial; public class StatusLabel extends JLabel implements Messenger { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java index 769aa2f6..d4aeffa0 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java @@ -23,7 +23,7 @@ public void removeListener(Messenger sm) { @Override public void message(String message) { synchronized (statusMessengers) { - statusMessengers.forEach(sm -> sm.message(message)); + statusMessengers.forEach(sm -> sm.message(message)); } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java index 160cce7b..08e657df 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java @@ -1,10 +1,10 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; -public interface CustomColumnName { - - String getColumnName(); +import manifold.ext.props.rt.api.val; - boolean isEditable(); +public interface CustomColumnName { - Class getC(); + @val String columnName; + @val boolean editable; + @val Class clazz; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java index 63ab5120..48f92656 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java @@ -1,24 +1,24 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; +import java.awt.event.MouseEvent; import java.io.Serial; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import java.util.stream.IntStream; -import java.awt.event.MouseEvent; - public class CustomTable extends ZebraJTable { @Serial private static final long serialVersionUID = -3889524906608098585L; - private final Map columnSettings = new HashMap<>(); private static final int MAX_WIDTH = 2147483647; private static final int MIN_WIDTH = 15; private static final int PREFERRED_WIDTH = 75; + private final Map columnSettings = new EnumMap<>(SearchColumnName.class); + public int getColumnIdByName(CustomColumnName customColumnName) { return IntStream.range(0, this.getColumnCount()) - .filter(i -> this.getColumnName(i).equals(customColumnName.getColumnName())).findFirst() + .filter(i -> this.getColumnName(i).equals(customColumnName.columnName)).findFirst() .orElse(-1); } @@ -33,7 +33,7 @@ public void setColumnVisibility(SearchColumnName searchColumnName, boolean visib public void hideColumn(SearchColumnName searchColumnName) { int columnId = getColumnIdByName(searchColumnName); if (columnId > -1) { - columnSettings.put(searchColumnName, new int[] { + columnSettings.put(searchColumnName, new int[]{ getColumnModel().getColumn(columnId).getMaxWidth(), getColumnModel().getColumn(columnId).getMinWidth(), getColumnModel().getColumn(columnId).getPreferredWidth() }); @@ -63,7 +63,7 @@ public boolean isHideColumn(SearchColumnName searchColumnName) { int columnId = getColumnIdByName(searchColumnName); if (columnId > -1) { return getColumnModel().getColumn(columnId).getMinWidth() == 0 - && getColumnModel().getColumn(columnId).getPreferredWidth() == 0; + && getColumnModel().getColumn(columnId).getPreferredWidth() == 0; } return true; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java index 576603ba..3139f1b7 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java @@ -1,17 +1,14 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; -import java.util.Arrays; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor public enum SearchColumnName implements CustomColumnName { RELEASE("App.Release", String.class, false), @@ -27,17 +24,17 @@ public enum SearchColumnName implements CustomColumnName { SCORE("SearchColumnName.Score", Integer.class, false); private static final Map MAP = - Arrays.stream(SearchColumnName.values()).collect(Collectors.toMap(SearchColumnName::getColumnName, Function.identity())); + SearchColumnName.values().stream() + .collect(Collectors.toMap(SearchColumnName::getColumnName, Function.identity())); - private final String columnNameCode; - @Getter - private final Class c; - @Getter - private final boolean editable; + @val @override String columnName; + @val @override Class clazz; + @val @override boolean editable; - @Override - public String getColumnName() { - return Messages.getString(columnNameCode); + SearchColumnName(String columnNameCode, Class clazz, boolean editable) { + this.columnName = Messages.getText(columnNameCode); + this.clazz = clazz; + this.editable = editable; } public static Optional getForColumnName(String columnName) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java index d50b005f..00ba4652 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java @@ -1,40 +1,41 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; -import java.util.Arrays; import java.util.function.Function; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.sublibrary.model.Subtitle; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor public enum SubtitleTableColumnName implements CustomColumnName { - SELECT("App.Select", Boolean.class, true, subtitle -> false), + SELECT("App.Select", Boolean.class, true, _ -> false), SCORE("SubtitleTableColumnName.Score", Integer.class, false, Subtitle::getScore), FILENAME("SubtitleTableColumnName.Filename", String.class, false, Subtitle::getFileName), SOURCE("SubtitleTableColumnName.Source", String.class, false, Subtitle::getSubtitleSource), UPLOADER("SubtitleTableColumnName.Uploader", String.class, false, Subtitle::getUploader), - HEARINGIMPAIRED("SubtitleTableColumnName.hearingimpaired", Boolean.class, false, Subtitle::isHearingImpaired), + HEARINGIMPAIRED("SubtitleTableColumnName.hearingImpaired", Boolean.class, false, Subtitle::isHearingImpaired), QUALITY("SubtitleTableColumnName.Quality", String.class, false, Subtitle::getQuality), RELEASEGROUP("SubtitleTableColumnName.Releasegroup", String.class, false, Subtitle::getReleaseGroup); - private final String columnNameCode; - @Getter - private final Class c; - @Getter - private final boolean editable; - @Getter - private final Function valueFunction; - - @Override - public String getColumnName() { - return Messages.getString(columnNameCode); + + @val @override String columnName; + @val @override Class clazz; + @val @override boolean editable; + @val Function valueFunction; + + SubtitleTableColumnName(String columnNameCode, Class clazz, boolean editable, + Function valueFunction) { + this.columnName = Messages.getText(columnNameCode); + this.clazz = clazz; + this.editable = editable; + this.valueFunction = valueFunction; } public static SubtitleTableColumnName forColumnName(String columnName) { - return Arrays.stream(SubtitleTableColumnName.values()).filter(stcn -> stcn.getColumnName().equals(columnName)).findAny().orElseThrow(); + return SubtitleTableColumnName.values().stream() + .filter(stcn -> stcn.columnName.equals(columnName)) + .findAny() + .orElseThrow(); } public Object getValue(Subtitle subtitle) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java index 8a591737..babe1d33 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java @@ -2,48 +2,47 @@ import static org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName.*; +import javax.swing.table.*; import java.io.Serial; -import java.util.Arrays; import java.util.stream.IntStream; import java.util.stream.Stream; -import javax.swing.table.DefaultTableModel; - import org.lodder.subtools.sublibrary.model.Subtitle; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ Arrays.class }) public class SubtitleTableModel extends DefaultTableModel { @Serial private static final long serialVersionUID = 4205143311042280620L; - private final static SubtitleTableColumnName[] COLUMNS = - Stream.of(SELECT, SCORE, FILENAME, RELEASEGROUP, QUALITY, SOURCE, UPLOADER, HEARINGIMPAIRED).toArray(SubtitleTableColumnName[]::new); + private static final SubtitleTableColumnName[] COLUMNS = + Stream.of(SELECT, SCORE, FILENAME, RELEASEGROUP, QUALITY, SOURCE, UPLOADER, HEARINGIMPAIRED) + .toArray(SubtitleTableColumnName[]::new); public SubtitleTableModel(Object[][] data, String[] columnNames) { super(data, columnNames); } public static SubtitleTableModel getDefaultSubtitleTableModel() { - String[] columnNames = Arrays.stream(COLUMNS).map(SubtitleTableColumnName::getColumnName).toArray(String[]::new); - return new SubtitleTableModel(new Object[][] {}, columnNames); + String[] columnNames = COLUMNS.stream().map(SubtitleTableColumnName::getColumnName).toArray(String[]::new); + return new SubtitleTableModel(new Object[][]{}, columnNames); } public void addRow(Subtitle subtitle) { - Object[] row = IntStream.range(0, getColumnCount()).mapToObj(this::getColumnName).map(SubtitleTableColumnName::forColumnName) - .map(stcn -> stcn.getValue(subtitle)).toArray(Object[]::new); + Object[] row = IntStream.range(0, getColumnCount()) + .mapToObj(this::getColumnName) + .map(SubtitleTableColumnName::forColumnName) + .map(stcn -> stcn.getValue(subtitle)) + .toArray(Object[]::new); this.addRow(row); } @Override public Class getColumnClass(int columnIndex) { - return COLUMNS[columnIndex].getC(); + return COLUMNS[columnIndex].clazz; } @Override public boolean isCellEditable(int row, int column) { - return COLUMNS[column].isEditable(); + return COLUMNS[column].editable; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java index bed205f3..ab5bb654 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; +import static org.lodder.subtools.multisubdownloader.gui.extra.table.SearchColumnName.*; + import javax.swing.table.*; import java.io.Serial; import java.util.ArrayList; @@ -13,8 +15,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import lombok.Getter; -import lombok.Setter; +import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.var; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; @@ -23,31 +25,29 @@ public class VideoTableModel extends DefaultTableModel { - @Serial - private static final long serialVersionUID = 4205143311042280620L; + @Serial private static final long serialVersionUID = 4205143311042280620L; private static final List SHOW_COLUMNS = - List.of(SearchColumnName.TYPE, SearchColumnName.RELEASE, SearchColumnName.FILENAME, SearchColumnName.TITLE, SearchColumnName.SEASON, - SearchColumnName.EPISODE, SearchColumnName.FOUND, SearchColumnName.SELECT, SearchColumnName.OBJECT); + List.of(TYPE, RELEASE, FILENAME, TITLE, SEASON, EPISODE, FOUND, SELECT, OBJECT); - private static final List SUBTITLE_COLUMNS = - List.of(SearchColumnName.FILENAME, SearchColumnName.SOURCE, SearchColumnName.SCORE, SearchColumnName.SELECT, SearchColumnName.OBJECT); + private static final List SUBTITLE_COLUMNS = List.of(FILENAME, SOURCE, SCORE, SELECT, OBJECT); private static final Map SHOW_COLUMNS_INDEX = IntStream.range(0, SHOW_COLUMNS.size()) - .collect(() -> new EnumMap<>(SearchColumnName.class), (map, i) -> map.put(SHOW_COLUMNS.get(i), i), (l, r) -> { - throw new IllegalArgumentException("Duplicate keys [%s] and [%s]".formatted(l, r)); - }); + .collect(() -> new EnumMap<>(SearchColumnName.class), (map, i) -> map.put(SHOW_COLUMNS.get(i), i), + (l, r) -> { + throw new IllegalArgumentException("Duplicate keys [$l] and [$r]"); + }); private final Class[] columnTypes; private final Boolean[] columnEditables; private final Map rowMap = new LinkedHashMap<>(); + private boolean showOnlyFound = false; - @Setter - private UserInteractionHandler userInteractionHandler; + @var UserInteractionHandler userInteractionHandler; private VideoTableModel(List searchColumnNames) { - super(new Object[][] {}, searchColumnNames.stream().map(SearchColumnName::getColumnName).toArray(String[]::new)); - this.columnTypes = searchColumnNames.stream().map(SearchColumnName::getC).toArray(Class[]::new); + super(new Object[][]{}, searchColumnNames.stream().map(SearchColumnName::getColumnName).toArray(String[]::new)); + this.columnTypes = searchColumnNames.stream().map(SearchColumnName::getClazz).toArray(Class[]::new); this.columnEditables = searchColumnNames.stream().map(SearchColumnName::isEditable).toArray(Boolean[]::new); } @@ -77,7 +77,7 @@ public void addRow(Release release) { if (!showOnlyFound || release.getMatchingSubCount() != 0) { Row row = createRow(release); rowMap.put(release, row); - this.addRow(row.getRowObject()); + this.addRow(row.rowObject); } } } @@ -89,38 +89,32 @@ private Row createRow(Release release) { private static class Row { private final Release release; private final UserInteractionHandler userInteractionHandler; - @Getter - public final Vector rowObject; + @get Vector rowObject; public Row(Release release, UserInteractionHandler userInteractionHandler) { this.release = release; this.userInteractionHandler = userInteractionHandler; this.rowObject = SHOW_COLUMNS.stream().map(searchColumn -> switch (searchColumn) { - case RELEASE -> { - if (release instanceof TvRelease tvRelease) { - yield tvRelease.getOriginalName(); - } else if (release instanceof MovieRelease movieRelease) { - yield movieRelease.getName(); - } else { - throw new IllegalArgumentException("Unexpected release type: " + release.getClass()); - } - } - case FILENAME -> release.getFileName(); + case RELEASE -> switch (release) { + case TvRelease tvRelease -> tvRelease.originalName; + case MovieRelease movieRelease -> movieRelease.name; + }; + case FILENAME -> release.fileName; case FOUND -> calculateSubsFound(); case SELECT -> false; case OBJECT -> release; - case SEASON -> release instanceof TvRelease tvRelease ? tvRelease.getSeason() : null; - case EPISODE -> release instanceof TvRelease tvRelease ? tvRelease.getEpisodeNumbers().get(0) : null; - case TYPE -> release.getVideoType(); - case TITLE -> release instanceof TvRelease tvRelease ? tvRelease.getTitle() : null; + case SEASON -> release instanceof TvRelease tvRelease ? tvRelease.season : null; + case EPISODE -> release instanceof TvRelease tvRelease ? tvRelease.firstEpisodeNumber : null; + case TYPE -> release.videoType; + case TITLE -> release instanceof TvRelease tvRelease ? tvRelease.title : null; default -> throw new IllegalArgumentException("Unexpected value: " + searchColumn); }).collect(Collectors.toCollection(Vector::new)); } private int calculateSubsFound() { - return userInteractionHandler != null - ? userInteractionHandler.getAutomaticSelection(release.getMatchingSubs()).size() - : release.getMatchingSubCount(); + return userInteractionHandler != null ? + userInteractionHandler.getAutomaticSelection(release.getMatchingSubs()).size() : + release.getMatchingSubCount(); } public int updateSubsFound() { @@ -139,11 +133,11 @@ public boolean isSelected() { public void addRow(Subtitle subtitle) { synchronized (this) { Vector row = SUBTITLE_COLUMNS.stream().map(searchColumn -> switch (searchColumn) { - case FILENAME -> subtitle.getFileName(); + case FILENAME -> subtitle.fileName; case SELECT -> false; case OBJECT -> subtitle; - case SOURCE -> subtitle.getSubtitleSource(); - case SCORE -> subtitle.getScore(); + case SOURCE -> subtitle.subtitleSource; + case SCORE -> subtitle.score; default -> throw new IllegalArgumentException("Unexpected value: " + searchColumn); }).collect(Collectors.toCollection(Vector::new)); this.addRow(row); @@ -207,10 +201,6 @@ public void setShowOnlyFound(boolean showOnlyFound) { updateTable(); } - public boolean isShowOnlyFound() { - return this.showOnlyFound; - } - public void executedSynchronized(Runnable runnable) { synchronized (this) { runnable.run(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java index 20f41050..d648c4ca 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java @@ -1,6 +1,6 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; -import java.awt.Dimension; +import java.awt.*; import java.io.Serial; /** @@ -10,6 +10,7 @@ public class ZebraJTable extends javax.swing.JTable { @Serial private static final long serialVersionUID = -6943213333291518652L; + private final java.awt.Color[] rowColors = new java.awt.Color[2]; private boolean drawStripes = false; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/AbstractButtonExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/AbstractButtonExtension.java deleted file mode 100644 index b27f527f..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/AbstractButtonExtension.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.button; - -import javax.swing.*; -import java.awt.event.ActionListener; -import java.util.function.Consumer; - -import lombok.experimental.UtilityClass; -import org.lodder.subtools.sublibrary.util.BooleanConsumer; - -@UtilityClass -public class AbstractButtonExtension { - - public T withActionListener(T abstractButton, ActionListener listener) { - abstractButton.addActionListener(listener); - return abstractButton; - } - - public T withActionListener(T abstractButton, Runnable listener) { - return withActionListener(abstractButton, arg -> listener.run()); - } - - public T withActionListenerSelf(T abstractButton, Consumer selfConsumerListener) { - return withActionListener(abstractButton, arg -> selfConsumerListener.accept(abstractButton)); - } - - public T withSelectedListener(T abstractButton, BooleanConsumer selectedConsumer) { - return withActionListener(abstractButton, arg -> selectedConsumer.accept(abstractButton.isSelected())); - } - - public T actionCommand(T abstractButton, String actionCommand) { - abstractButton.setActionCommand(actionCommand); - return abstractButton; - } - - public T withActionCommand(T abstractButton, String actionCommand) { - abstractButton.getModel().setActionCommand(actionCommand); - return abstractButton; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/JButtonExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/JButtonExtension.java deleted file mode 100644 index 92828e54..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/JButtonExtension.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.button; - -import javax.swing.JButton; -import javax.swing.JRootPane; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JButtonExtension { - - public T defaultButtonFor(T abstractButton, JRootPane rootPane) { - rootPane.setDefaultButton(abstractButton); - return abstractButton; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/component/ComponentExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/component/ComponentExtension.java deleted file mode 100644 index 4c15e8a6..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/component/ComponentExtension.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.component; - -import java.awt.Component; -import java.awt.event.MouseListener; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class ComponentExtension { - - public T withMouseListener(T component, MouseListener listener) { - component.addMouseListener(listener); - return component; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/container/ContainerExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/container/ContainerExtension.java deleted file mode 100644 index 04e60aac..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/container/ContainerExtension.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.container; - -import java.awt.Container; -import java.awt.LayoutManager; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class ContainerExtension { - - public T layout(T container, LayoutManager mgr) { - container.setLayout(mgr); - return container; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcheckbox/JCheckBoxExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcheckbox/JCheckBoxExtension.java deleted file mode 100644 index cb49d75f..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcheckbox/JCheckBoxExtension.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox; - -import javax.swing.*; -import java.util.Arrays; - -import lombok.experimental.UtilityClass; -import org.lodder.subtools.sublibrary.util.BooleanConsumer; - -@UtilityClass -public class JCheckBoxExtension { - - public T addCheckedChangeListener(T checkBox, BooleanConsumer... listeners) { - checkBox.addItemListener(e -> Arrays.stream(listeners).forEach(listener -> listener.accept(((JCheckBox) e.getSource()).isSelected()))); - return checkBox; - } - - public T visible(T checkBox, boolean visible) { - checkBox.setVisible(visible); - return checkBox; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcombobox/MyComboBox.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcombobox/MyComboBox.java deleted file mode 100644 index b524fe3f..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcombobox/MyComboBox.java +++ /dev/null @@ -1,179 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox; - -import javax.swing.*; -import javax.swing.border.*; -import java.awt.*; -import java.awt.event.ActionListener; -import java.awt.event.ItemListener; -import java.io.Serial; -import java.util.Collection; -import java.util.Vector; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; - -import com.google.common.collect.Iterables; -import org.lodder.subtools.multisubdownloader.gui.ToStringListCellRenderer; - -public class MyComboBox extends JComboBox { - - private static final Border ERROR_BORDER = new LineBorder(Color.RED, 1); - private Border defaultBorder; - private Predicate selectedValueVerifier; - - @Serial - private static final long serialVersionUID = -8449456978689044914L; - - /** - * Creates a MyComboBox that takes its items from an - * existing ComboBoxModel. Since the - * ComboBoxModel is provided, a combo box created using - * this constructor does not create a default combo box model and - * may impact how the insert, remove and add methods behave. - * - * @param aModel the ComboBoxModel that provides the - * displayed list of items - * @see DefaultComboBoxModel - */ - public MyComboBox(ComboBoxModel aModel) { - super(aModel); - this.defaultBorder = getBorder(); - } - - /** - * Creates a MyComboBox that contains the elements - * in the specified array. By default, the first item in the array - * (and therefore the data model) becomes selected. - * - * @param items an array of objects to insert into the combo box - * @see DefaultComboBoxModel - */ - public MyComboBox(E[] items) { - super(items); - this.defaultBorder = getBorder(); - } - - /** - * Creates a MyComboBox that contains the elements - * in the specified collection. By default, the first item in the - * collection (and therefore the data model) becomes selected. - * - * @param items a collection of objects to insert into the combo box - * @param elementType the type of elements in the list - * @see DefaultComboBoxModel - */ - public MyComboBox(Collection items, Class elementType) { - super(Iterables.toArray(items, elementType)); - this.defaultBorder = getBorder(); - } - - /** - * Creates a MyComboBox that contains the elements - * in the specified Vector. By default, the first item in the vector - * (and therefore the data model) becomes selected. - * - * @param items an array of vectors to insert into the combo box - * @see DefaultComboBoxModel - */ - public MyComboBox(Vector items) { - super(items); - this.defaultBorder = getBorder(); - } - - /** - * Creates a MyComboBox with a default data model. - * The default data model is an empty list of objects. - * Use addItem to add items. By default, the first item - * in the data model becomes selected. - * - * @see DefaultComboBoxModel - */ - public MyComboBox() { - super(); - this.defaultBorder = getBorder(); - } - - public static MyComboBox ofValues(E... values) { - return new MyComboBox<>(values); - } - - public MyComboBox withModel(ComboBoxModel model) { - setModel(model); - return this; - } - - public MyComboBox withRenderer(ListCellRenderer renderer) { - setRenderer(renderer); - return this; - } - - public MyComboBox withToStringRenderer(Function toStringRenderer) { - return withRenderer(ToStringListCellRenderer.of(getRenderer(), toStringRenderer)); - } - - public MyComboBox withToMessageStringRenderer(Function toStringRenderer) { - return withRenderer(ToStringListCellRenderer.ofMessage(getRenderer(), toStringRenderer)); - } - - public MyComboBox withItemListener(ItemListener itemListener) { - this.addItemListener(itemListener); - return this; - } - - public MyComboBox withItemListener(Runnable itemListener) { - return withItemListener(arg -> itemListener.run()); - } - - public MyComboBox withSelectedItem(E item) { - setSelectedItem(item); - return this; - } - - public MyComboBox withActionListener(ActionListener actionListener) { - addActionListener(actionListener); - return this; - } - - @SuppressWarnings("unchecked") - public MyComboBox withEventConsumer(Consumer> actionListener) { - addActionListener(event -> actionListener.accept((MyComboBox) (event.getSource()))); - return this; - } - - @SuppressWarnings("unchecked") - public MyComboBox withSelectedItemConsumer(Consumer actionListener) { - addActionListener(event -> actionListener.accept(((MyComboBox) (event.getSource())).getSelectedItem())); - return this; - } - - @SuppressWarnings("unchecked") - @Override - public E getSelectedItem() { - return (E) super.getSelectedItem(); - } - - public MyComboBox withSelectedValueVerifier(Predicate valueVerifier) { - this.selectedValueVerifier = valueVerifier; - return this; - } - - @Override - public void setBorder(Border border) { - super.setBorder(border); - defaultBorder = border; - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - refreshState(); - } - - public void refreshState() { - if (!isEnabled()) { - super.setBorder(defaultBorder); - } else if (selectedValueVerifier != null && !selectedValueVerifier.test(getSelectedItem())) { - super.setBorder(ERROR_BORDER); - } - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcomponent/JComponentExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcomponent/JComponentExtension.java deleted file mode 100644 index b227d8e5..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcomponent/JComponentExtension.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent; - -import java.util.Arrays; -import java.util.function.Consumer; -import java.util.function.Predicate; - -import javax.swing.JComponent; -import javax.swing.JScrollPane; - -import java.awt.Component; -import java.awt.Container; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JComponentExtension { - - public T withEnabled(T component, boolean enabled) { - component.setEnabled(enabled); - return component; - } - - public T withEnabled(T component) { - return withEnabled(component, true); - } - - public T withDisabled(T component) { - return withEnabled(component, false); - } - - public T addComponent(T component, S child) { - component.add(child); - return component; - } - - public T addComponent(T component, S child, Object constraints) { - component.add(child, constraints); - return component; - } - - public S addTo(S child, T parent) { - parent.add(child); - return child; - } - - public S addTo(S child, T parent, Object constraints) { - parent.add(child, constraints); - return child; - } - - public T addComponent(T component, Object constraints, S child) { - component.add(child, constraints); - return component; - } - - public void setEnabledRecursive(Component component, boolean enabled) { - setRecursive(component, c -> c.setEnabled(enabled)); - } - - public void setRecursive(Component component, Consumer consumer) { - setRecursive(component, consumer, c -> true); - } - - public void setRecursive(Component component, Consumer consumer, Predicate condition) { - if (component != null) { - consumer.accept(component); - if (component instanceof Container container && condition.test(container)) { - Arrays.stream(container.getComponents()).forEach(child -> setRecursive(child, consumer, condition)); - } - } - } - - public T enabledRecursive(T component, boolean enabled) { - setEnabledRecursive(component, enabled); - return component; - } - - public JScrollPane scrollPane(JScrollPane scrollPane, Component view) { - scrollPane.setViewportView(view); - return scrollPane; - } - - public T withToolTipText(T component, String text) { - component.setToolTipText(text); - return component; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java index f1e66ded..84bcbaab 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java @@ -1,29 +1,21 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jpopupmenu; -import javax.swing.JPopupMenu; - -import java.awt.Component; -import java.awt.Point; +import javax.swing.*; +import java.awt.*; import java.io.Serial; +import manifold.ext.props.rt.api.var; + public class MyPopupMenu extends JPopupMenu { @Serial private static final long serialVersionUID = 1084650376633196066L; - private Point clickLocation; + @var Point clickLocation; @Override public void show(Component invoker, int x, int y) { super.show(invoker, x, y); - setClickLocation(new Point(x, y)); - } - - public void setClickLocation(Point clickLocation) { - this.clickLocation = clickLocation; - } - - public Point getClickLocation() { - return clickLocation; + clickLocation = new Point(x, y); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jscrollpane/JScrollPaneExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jscrollpane/JScrollPaneExtension.java deleted file mode 100644 index aa8b90d5..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jscrollpane/JScrollPaneExtension.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jscrollpane; - -import javax.swing.JScrollPane; - -import java.awt.Component; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JScrollPaneExtension { - - public JScrollPane withViewportView(JScrollPane scrollPane, Component view) { - scrollPane.setViewportView(view); - return scrollPane; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jslider/JSliderExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jslider/JSliderExtension.java deleted file mode 100644 index 9f6d05fe..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jslider/JSliderExtension.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jslider; - -import javax.swing.JSlider; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JSliderExtension { - - public T withMinimum(T slider, int minimum) { - slider.setMinimum(minimum); - return slider; - } - - public T withMaximum(T slider, int maximum) { - slider.setMaximum(maximum); - return slider; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextarea/JTextAreaExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextarea/JTextAreaExtension.java deleted file mode 100644 index 43270147..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextarea/JTextAreaExtension.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextarea; - -import javax.swing.JTextArea; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JTextAreaExtension { - - public T autoscrolls(T textArea, boolean autoscrolls) { - textArea.setAutoscrolls(autoscrolls); - return textArea; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextcomponent/JTextComponentExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextcomponent/JTextComponentExtension.java deleted file mode 100644 index 4105f1f4..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextcomponent/JTextComponentExtension.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextcomponent; - -import javax.swing.text.JTextComponent; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JTextComponentExtension { - - public T editable(T textComponent, boolean editable) { - textComponent.setEditable(editable); - return textComponent; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/JTextFieldExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/JTextFieldExtension.java deleted file mode 100644 index 7d981a75..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/JTextFieldExtension.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import javax.swing.JTextField; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JTextFieldExtension { - - public T withColumns(T textField, int columns) { - textField.setColumns(columns); - return textField; - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java index ff73cc8d..f47df983 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java @@ -5,16 +5,14 @@ import javax.swing.event.*; import java.awt.*; import java.io.Serial; -import java.util.Arrays; -import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; -import lombok.Getter; +import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.sublibrary.util.BooleanConsumer; -public class MyPasswordField extends JPasswordField implements MypasswordFieldOthersIntf { +public class MyPasswordField extends JPasswordField implements MyPasswordFieldOthersIntf { @Serial private static final long serialVersionUID = -3002009544577141751L; @@ -24,8 +22,8 @@ public class MyPasswordField extends JPasswordField implements MypasswordFieldOt public Predicate valueVerifier = StringUtils::isNotEmpty; private boolean requireValue; - private Consumer valueChangedCalbackListener; - private BooleanConsumer[] validityChangedCalbackListeners; + private Consumer valueChangedCallbackListener; + private BooleanConsumer[] validityChangedCallbackListeners; private final ObjectWrapper valueWrapper = new ObjectWrapper<>(); private final ObjectWrapper validWrapper = new ObjectWrapper<>(); @@ -67,26 +65,19 @@ public MyPasswordField requireValue(boolean requireValue) { } @Override - public MyPasswordField withValueChangedCallback(Consumer valueChangedCalbackListener) { - this.valueChangedCalbackListener = valueChangedCalbackListener; + public MyPasswordField withValueChangedCallback(Consumer valueChangedCallbackListener) { + this.valueChangedCallbackListener = valueChangedCallbackListener; return this; } @Override - public MyPasswordField withValidityChangedCallback(BooleanConsumer... validityChangedCalbackListeners) { - this.validityChangedCalbackListeners = validityChangedCalbackListeners; + public MyPasswordField withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners) { + this.validityChangedCallbackListeners = validityChangedCallbackListeners; return this; } private static class ObjectWrapper { - @Getter - private S value; - - public boolean setValue(S value) { - boolean changed = this.value != value; - this.value = value; - return changed; - } + @var S value; } @Override @@ -116,10 +107,11 @@ public MyPasswordField build() { } else if (requireValue) { completeValueVerifier = StringUtils::isNotEmpty; } else { - completeValueVerifier = t -> true; + completeValueVerifier = _ -> true; } - if (valueVerifier != null || requireValue || valueChangedCalbackListener != null || validityChangedCalbackListeners != null) { + if (valueVerifier != null || requireValue || valueChangedCallbackListener != null || + validityChangedCallbackListeners != null) { checkValidity(getRawText()); getDocument().addDocumentListener(new DocumentListener() { @@ -147,15 +139,17 @@ private void checkValidity(String text) { boolean valid = completeValueVerifier.test(text); setSuperBorder(valid ? MyPasswordField.getDefaultBorder(this) : ERROR_BORDER); - boolean changedValidity = validWrapper.setValue(valid); - if (changedValidity && validityChangedCalbackListeners != null) { - Arrays.stream(validityChangedCalbackListeners).forEach(listener -> listener.accept(valid)); + boolean changedValidity = validWrapper.value != valid; + validWrapper.value = valid; + if (changedValidity && validityChangedCallbackListeners != null) { + validityChangedCallbackListeners.forEach(listener -> listener.accept(valid)); } - if (valueChangedCalbackListener != null) { - boolean valueChanged = valueWrapper.setValue(text); + if (valueChangedCallbackListener != null) { + boolean valueChanged = !StringUtils.equals(valueWrapper.value, text); + valueWrapper.value = text; if (valueChanged) { - valueChangedCalbackListener.accept(text); + valueChangedCallbackListener.accept(text); } } } @@ -170,16 +164,11 @@ public String getText() { return completeValueVerifier.test(text) ? text : null; } - - public Optional getOptionalObject() { - return Optional.ofNullable(getText()); - } - @Override public void setText(String password) { super.setText(password); - valueWrapper.setValue(password); - validWrapper.setValue(completeValueVerifier.test(password)); + valueWrapper.value = password; + validWrapper.value = completeValueVerifier.test(password); } public boolean hasValidValue() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java new file mode 100644 index 00000000..18f7d0f3 --- /dev/null +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java @@ -0,0 +1,22 @@ +package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +import org.lodder.subtools.sublibrary.util.BooleanConsumer; + +public interface MyPasswordFieldOthersIntf { + MyPasswordFieldOthersIntf withValueVerifier(Predicate verifier); + + default MyPasswordFieldOthersIntf requireValue() { + return requireValue(true); + } + + MyPasswordFieldOthersIntf requireValue(boolean requireValue); + + MyPasswordFieldOthersIntf withValueChangedCallback(Consumer valueChangedCallbackListener); + + MyPasswordFieldOthersIntf withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners); + + MyPasswordField build(); +} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextField.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextField.java deleted file mode 100644 index 95e92d95..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextField.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import java.io.Serial; - -public class MyTextField extends MyTextFieldCommon> { - - @Serial - private static final long serialVersionUID = 1580566911085697756L; - - private MyTextField() { - super(); - } - - public static MyTextFieldToStringMapperIntf> buildForType(Class type) { - return new MyTextField<>(); - } -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java index 82fc249d..80893d08 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java @@ -28,8 +28,8 @@ public abstract class MyTextFieldCommon> ex private Function toObjectMapper; private Predicate valueVerifier; private boolean requireValue; - private Consumer valueChangedCalbackListener; - private BooleanConsumer[] validityChangedCalbackListeners; + private Consumer valueChangedCallbackListener; + private BooleanConsumer[] validityChangedCallbackListeners; private final ObjectWrapper valueWrapper = new ObjectWrapper<>(); private final ObjectWrapper validWrapper = new ObjectWrapper<>(); @@ -78,14 +78,14 @@ public R requireValue(boolean requireValue) { } @Override - public R withValueChangedCallback(Consumer valueChangedCalbackListener) { - this.valueChangedCalbackListener = valueChangedCalbackListener; + public R withValueChangedCallback(Consumer valueChangedCallbackListener) { + this.valueChangedCallbackListener = valueChangedCallbackListener; return self(); } @Override - public final R withValidityChangedCallback(BooleanConsumer... validityChangedCalbackListeners) { - this.validityChangedCalbackListeners = validityChangedCalbackListeners; + public final R withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners) { + this.validityChangedCallbackListeners = validityChangedCallbackListeners; return self(); } @@ -133,7 +133,8 @@ public R build() { completeValueVerifier = t -> true; } - if (valueVerifier != null || requireValue || valueChangedCalbackListener != null || validityChangedCalbackListeners != null) { + if (valueVerifier != null || requireValue || valueChangedCallbackListener != null || + validityChangedCallbackListeners != null) { checkValidity(getText()); getDocument().addDocumentListener(new DocumentListener() { @@ -162,15 +163,15 @@ private void checkValidity(String text) { setSuperBorder(valid ? MyTextFieldCommon.getDefaultBorder(self()) : ERROR_BORDER); boolean changedValidity = validWrapper.setValue(valid); - if (changedValidity && validityChangedCalbackListeners != null) { - Arrays.stream(validityChangedCalbackListeners).forEach(listener -> listener.accept(valid)); + if (changedValidity && validityChangedCallbackListeners != null) { + Arrays.stream(validityChangedCallbackListeners).forEach(listener -> listener.accept(valid)); } - if (valueChangedCalbackListener != null) { + if (valueChangedCallbackListener != null) { T value = toObjectMapper.apply(text); boolean valueChanged = valueWrapper.setValue(toObjectMapper.apply(text)); if (valueChanged) { - valueChangedCalbackListener.accept(value); + valueChangedCallbackListener.accept(value); } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java index 8affd69a..47fdf823 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java @@ -14,9 +14,9 @@ default MyTextFieldOthersIntf requireValue() { MyTextFieldOthersIntf requireValue(boolean requireValue); - MyTextFieldOthersIntf withValueChangedCallback(Consumer valueChangedCalbackListener); + MyTextFieldOthersIntf withValueChangedCallback(Consumer valueChangedCallbackListener); - MyTextFieldOthersIntf withValidityChangedCallback(BooleanConsumer... validityChangedCalbackListeners); + MyTextFieldOthersIntf withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners); R build(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java index 96b4b3d7..629eb78c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java @@ -10,20 +10,20 @@ public class MyTextFieldString extends MyTextFieldCommon TO_STRING_MAPPER = Function.identity(); private static final Function TO_OBJECT_MAPPER = Function.identity(); - public static final Predicate VERIFIER = text -> true; + public static final Predicate VERIFIER = _ -> true; private MyTextFieldString() { } - public static MyTextFieldOthersIntf builder() { + public static MyTextFieldOthersIntf builder() { return new MyTextFieldString() .withToStringMapper(TO_STRING_MAPPER) .withToObjectMapper(TO_OBJECT_MAPPER) .withValueVerifier(VERIFIER); } - public static MyTextFieldOthersIntf create() { + public static MyTextFieldOthersIntf create() { return builder().build(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MypasswordFieldOthersIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MypasswordFieldOthersIntf.java deleted file mode 100644 index 33b2bc1b..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MypasswordFieldOthersIntf.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import java.util.function.Consumer; -import java.util.function.Predicate; - -import org.lodder.subtools.sublibrary.util.BooleanConsumer; - -public interface MypasswordFieldOthersIntf { - MypasswordFieldOthersIntf withValueVerifier(Predicate verifier); - - default MypasswordFieldOthersIntf requireValue() { - return requireValue(true); - } - - MypasswordFieldOthersIntf requireValue(boolean requireValue); - - MypasswordFieldOthersIntf withValueChangedCallback(Consumer valueChangedCalbackListener); - - MypasswordFieldOthersIntf withValidityChangedCallback(BooleanConsumer... validityChangedCalbackListeners); - - MyPasswordField build(); -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java index 2d5efec8..3968a77a 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java @@ -1,13 +1,10 @@ package org.lodder.subtools.multisubdownloader.gui.panels; +import javax.swing.*; import java.io.Serial; -import javax.swing.JButton; -import javax.swing.JPanel; - import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.SearchAction; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; import org.lodder.subtools.sublibrary.Language; public abstract class InputPanel extends JPanel { @@ -15,17 +12,17 @@ public abstract class InputPanel extends JPanel { @Serial private static final long serialVersionUID = 7753220002440733463L; private JButton btnSearch; - private MyComboBox cbxLanguage; + private JComboBox cbxLanguage; - public InputPanel() { + InputPanel() { createComponents(); } public Language getSelectedLanguage() { - return cbxLanguage.getSelectedItem(); + return cbxLanguage.getSelectedValue(); } - public void setSelectedlanguage(Language language) { + public void setSelectedLanguage(Language language) { cbxLanguage.setSelectedItem(language); } @@ -47,14 +44,13 @@ protected JButton getSearchButton() { return this.btnSearch; } - protected MyComboBox getLanguageCbx() { + protected JComboBox getLanguageCbx() { return this.cbxLanguage; } private void createComponents() { - cbxLanguage = new MyComboBox<>(Language.values()) - .withToMessageStringRenderer(Language::getMsgCode); + cbxLanguage = new JComboBox<>(Language.values()).toMessageStringRenderer(Language::getMsgCode); - btnSearch = new JButton(Messages.getString("InputPanel.SearchForSubtitles")); + btnSearch = new JButton(Messages.getText("InputPanel.SearchForSubtitles")); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java index 35b6909a..03dd5e62 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java @@ -1,48 +1,40 @@ package org.lodder.subtools.multisubdownloader.gui.panels; -import java.io.Serial; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSeparator; -import javax.swing.JTextArea; +import static org.lodder.subtools.multisubdownloader.Messages.*; -import org.lodder.subtools.multisubdownloader.Messages; -import org.lodder.subtools.multisubdownloader.gui.extra.LogTextAppender; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextarea.JTextAreaExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextcomponent.JTextComponentExtension; +import javax.swing.*; +import java.io.Serial; import ch.qos.logback.classic.Level; -import lombok.experimental.ExtensionMethod; import net.miginfocom.swing.MigLayout; +import org.lodder.subtools.multisubdownloader.gui.extra.LogTextAppender; -@ExtensionMethod({ JTextComponentExtension.class, JTextAreaExtension.class }) public class LoggingPanel extends JPanel { @Serial private static final long serialVersionUID = 1578326761175927376L; + private final JTextArea txtLogging; - private final ch.qos.logback.classic.Logger root = - (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); + private final ch.qos.logback.classic.Logger ROOT = + (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger( + ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); public LoggingPanel() { this.setLayout(new MigLayout("", "[698px,grow][]", "[][70px,grow]")); - JScrollPane scrollPane_1 = new JScrollPane(); - this.add(new JLabel(Messages.getString("App.Logging")), "cell 0 0,alignx right,gaptop 5"); + JScrollPane scrollPane = new JScrollPane(); + this.add(new JLabel(getText("App.Logging")), "cell 0 0,alignx right,gaptop 5"); this.add(new JSeparator(), "cell 0 0,growx,gaptop 5"); Level[] logLevels = { Level.ALL, Level.TRACE, Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR }; - MyComboBox cbxLogLevel = new MyComboBox<>(logLevels) - .withSelectedItem(root.getLevel()) - .withSelectedItemConsumer(root::setLevel); + JComboBox cbxLogLevel = new JComboBox<>(logLevels) + .selectedValue(ROOT.getLevel()) + .selectedItemConsumer(ROOT::setLevel); this.add(cbxLogLevel, "cell 1 0,alignx right"); - this.add(scrollPane_1, "cell 0 1 2 1,grow"); + this.add(scrollPane, "cell 0 1 2 1,grow"); - txtLogging = new JTextArea().autoscrolls(true).editable(false); - scrollPane_1.setViewportView(txtLogging); + txtLogging = new JTextArea().autoScrolls(true).editable(false); + scrollPane.setViewportView(txtLogging); new LogTextAppender(txtLogging); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java index 762b232b..a6e17d5b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java @@ -1,27 +1,22 @@ package org.lodder.subtools.multisubdownloader.gui.panels; -import java.io.Serial; +import static org.lodder.subtools.multisubdownloader.Messages.*; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSeparator; -import javax.swing.table.DefaultTableModel; +import javax.swing.*; +import javax.swing.table.*; +import java.awt.event.ActionListener; +import java.io.Serial; -import org.lodder.subtools.multisubdownloader.Messages; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.table.CustomTable; import org.lodder.subtools.multisubdownloader.gui.extra.table.SearchColumnName; import org.lodder.subtools.multisubdownloader.gui.extra.table.VideoTableModel; -import java.awt.event.ActionListener; - -import net.miginfocom.swing.MigLayout; - public class ResultPanel extends JPanel { @Serial private static final long serialVersionUID = 2368028332402129899L; + private JButton btnMove; private JButton btnSelectAll; private JScrollPane scrollPane; @@ -44,7 +39,7 @@ private void addComponentsToPanel() { actionButtonsPanel.add(btnDownload, "cell 0 0,alignx right,aligny top"); actionButtonsPanel.add(btnMove, "cell 1 0,alignx left,aligny top"); - add(new JLabel(Messages.getString("ResultPanel.SearchResults")), "cell 0 0 2 1,gapy 5"); + add(new JLabel(getText("ResultPanel.SearchResults")), "cell 0 0 2 1,gapy 5"); add(new JSeparator(), "cell 0 0 2 1,growx,gaptop 5"); add(scrollPane, "cell 0 1 1 4,grow"); add(btnSelectNone, "cell 1 1,aligny bottom"); @@ -103,18 +98,18 @@ public void setMoveAction(ActionListener moveAction) { } private void setupListeners() { - btnSelectNone.addActionListener(e -> deselectAllRows()); - btnSelectFound.addActionListener(e -> selectRowsWithFoundSubtitles()); - btnSelectAll.addActionListener(e -> selectAllRows()); + btnSelectNone.addActionListener(_ -> deselectAllRows()); + btnSelectFound.addActionListener(_ -> selectRowsWithFoundSubtitles()); + btnSelectAll.addActionListener(_ -> selectAllRows()); } private void createComponents() { scrollPane = new JScrollPane(); - btnSelectNone = new JButton(Messages.getString("ResultPanel.SelectNothing")); - btnSelectFound = new JButton(Messages.getString("ResultPanel.SelectFound")); - btnSelectAll = new JButton(Messages.getString("ResultPanel.SelectEverything")); - btnDownload = new JButton(Messages.getString("ResultPanel.DownloadSelected")); - btnMove = new JButton(Messages.getString("ResultPanel.MoveSelected")); + btnSelectNone = new JButton(getText("ResultPanel.SelectNothing")); + btnSelectFound = new JButton(getText("ResultPanel.SelectFound")); + btnSelectAll = new JButton(getText("ResultPanel.SelectEverything")); + btnDownload = new JButton(getText("ResultPanel.DownloadSelected")); + btnMove = new JButton(getText("ResultPanel.MoveSelected")); } private void setEnableButtons(boolean enabled) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java index b5a8f0ce..6ac1b135 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java @@ -1,21 +1,13 @@ package org.lodder.subtools.multisubdownloader.gui.panels; -import java.io.Serial; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JTextField; - -import org.lodder.subtools.multisubdownloader.Messages; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; +import static org.lodder.subtools.multisubdownloader.Messages.*; +import javax.swing.*; import java.awt.event.ActionListener; +import java.io.Serial; -import lombok.experimental.ExtensionMethod; import net.miginfocom.swing.MigLayout; -@ExtensionMethod({ JTextFieldExtension.class }) public class SearchFileInputPanel extends InputPanel { @Serial @@ -34,23 +26,23 @@ public SearchFileInputPanel() { } private void addComponentsToPanel() { - add(new JLabel(Messages.getString("MainWindow.LocationNewEpisodes")), "cell 1 0,alignx trailing"); + add(new JLabel(getText("MainWindow.LocationNewEpisodes")), "cell 1 0,alignx trailing"); add(txtIncomingPath, "cell 2 0,alignx leading"); add(btnBrowse, "cell 3 0"); add(chkRecursive, "cell 2 1 2 1"); add(chkForceSubtitleOverwrite, "cell 2 3 2 1"); add(getSearchButton(), "cell 0 5 3 1,alignx center"); - add(new JLabel(Messages.getString("MainWindow.SelectSubtitleLanguage")), "cell 2 2"); + add(new JLabel(getText("MainWindow.SelectSubtitleLanguage")), "cell 2 2"); add(getLanguageCbx(), "cell 3 2"); } private void createComponents() { - txtIncomingPath = new JTextField().withColumns(20); + txtIncomingPath = new JTextField().columns(20); - chkRecursive = new JCheckBox(Messages.getString("MainWindow.RecursiveSearch")); - chkForceSubtitleOverwrite = new JCheckBox(Messages.getString("MainWindow.ignoreExistingSubtitles")); + chkRecursive = new JCheckBox(getText("MainWindow.RecursiveSearch")); + chkForceSubtitleOverwrite = new JCheckBox(getText("MainWindow.ignoreExistingSubtitles")); - btnBrowse = new JButton(Messages.getString("App.Browse")); + btnBrowse = new JButton(getText("App.Browse")); } public void setRecursiveSelected(boolean selected) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java index 4c244197..9b895af8 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java @@ -1,19 +1,18 @@ package org.lodder.subtools.multisubdownloader.gui.panels; -import javax.swing.JPanel; - +import javax.swing.*; import java.io.Serial; -import lombok.Getter; +import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; -@Getter public class SearchPanel extends JPanel { @Serial private static final long serialVersionUID = -7602822323779710089L; - private final ResultPanel resultPanel; - private final I inputPanel; + + @val ResultPanel resultPanel; + @val I inputPanel; public SearchPanel(I inputPanel, ResultPanel resultPanel) { this.inputPanel = inputPanel; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java index 950f7130..e05b5c84 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java @@ -1,30 +1,24 @@ package org.lodder.subtools.multisubdownloader.gui.panels; -import java.io.Serial; - -import javax.swing.JLabel; -import javax.swing.JTextField; +import static org.lodder.subtools.multisubdownloader.Messages.*; +import static org.lodder.subtools.sublibrary.model.VideoSearchType.*; -import org.lodder.subtools.multisubdownloader.Messages; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextcomponent.JTextComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; -import org.lodder.subtools.sublibrary.model.VideoSearchType; +import javax.swing.*; +import java.io.Serial; -import lombok.experimental.ExtensionMethod; import net.miginfocom.swing.MigLayout; +import org.lodder.subtools.sublibrary.model.VideoSearchType; -@ExtensionMethod({ JTextFieldExtension.class, JComponentExtension.class, JTextComponentExtension.class }) public class SearchTextInputPanel extends InputPanel { @Serial private static final long serialVersionUID = 7030171360517948253L; - private MyComboBox cbxVideoType; + + private JComboBox cbxVideoType; + private JTextField txtInputVideoName; protected JTextField txtInputSeason; protected JTextField txtInputEpisode; protected JTextField txtQualityVersion; - private JTextField txtInputVideoName; public SearchTextInputPanel() { super(); @@ -38,54 +32,38 @@ public SearchTextInputPanel() { private void addComponentsToPanel() { this.add(cbxVideoType, "cell 1 0,growx"); this.add(txtInputVideoName, "cell 2 0 5 1,growx"); - this.add(new JLabel(Messages.getString("MainWindow.QualityVersion")), "cell 1 1,alignx trailing"); + this.add(new JLabel(getText("MainWindow.QualityVersion")), "cell 1 1,alignx trailing"); this.add(txtQualityVersion, "cell 2 1,growx"); - this.add(new JLabel(Messages.getString("App.Season")), "cell 3 1,alignx trailing"); + this.add(new JLabel(getText("App.Season")), "cell 3 1,alignx trailing"); this.add(txtInputSeason, "cell 4 1,alignx left"); - this.add(new JLabel(Messages.getString("App.Episode")), "cell 5 1,alignx trailing"); + this.add(new JLabel(getText("App.Episode")), "cell 5 1,alignx trailing"); this.add(txtInputEpisode, "cell 6 1,growx"); - this.add(new JLabel(Messages.getString("MainWindow.SelectSubtitleLanguage")), "cell 1 2 3 1,alignx trailing"); + this.add(new JLabel(getText("MainWindow.SelectSubtitleLanguage")), "cell 1 2 3 1,alignx trailing"); this.add(getLanguageCbx(), "cell 4 2 2 1,growx"); this.add(getSearchButton(), "cell 2 4 2 1"); } private void setupListeners() { - cbxVideoType.addItemListener(arg0 -> videoTypeChanged()); + cbxVideoType.addItemListener(_ -> videoTypeChanged()); } private void createComponents() { - cbxVideoType = new MyComboBox<>(VideoSearchType.values()) - .withToMessageStringRenderer(VideoSearchType::getMsgCode); - - txtInputVideoName = new JTextField().withColumns(10); - - txtQualityVersion = new JTextField().withColumns(10); - - txtInputSeason = new JTextField().withColumns(10); - - txtInputEpisode = new JTextField().withColumns(10); + cbxVideoType = new JComboBox<>(values()).toStringRenderer(VideoSearchType::getMsgCode); + txtInputVideoName = new JTextField().columns(10); + txtQualityVersion = new JTextField().columns(10); + txtInputSeason = new JTextField().columns(10); + txtInputEpisode = new JTextField().columns(10); } private void videoTypeChanged() { - VideoSearchType videoTypeChoice = cbxVideoType.getSelectedItem(); - if (VideoSearchType.EPISODE == videoTypeChoice) { - txtInputSeason.editable(true).withEnabled(true); - txtInputEpisode.editable(true).withEnabled(true); - } else { - txtInputSeason.editable(false).withEnabled(false); - txtInputEpisode.editable(false).withEnabled(false); - } - if (VideoSearchType.RELEASE == videoTypeChoice) { - txtQualityVersion.editable(false).withEnabled(false); - txtQualityVersion.editable(false).withEnabled(false); - } else { - txtQualityVersion.editable(true).withEnabled(true); - txtQualityVersion.editable(true).withEnabled(true); - } + VideoSearchType videoTypeChoice = cbxVideoType.getSelectedValue(); + txtInputSeason.editable(videoTypeChoice == EPISODE).enabled(videoTypeChoice == EPISODE); + txtInputEpisode.editable(videoTypeChoice == EPISODE).enabled(videoTypeChoice == EPISODE); + txtQualityVersion.editable(videoTypeChoice == RELEASE).enabled(videoTypeChoice == RELEASE); } public VideoSearchType getType() { - return cbxVideoType.getSelectedItem(); + return cbxVideoType.getSelectedValue(); } public int getSeason() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java index f98a8e29..33391d9b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java @@ -2,37 +2,24 @@ import static java.util.function.Predicate.*; -import java.util.Arrays; +import javax.swing.*; +import javax.swing.table.*; +import java.awt.*; import java.util.Collection; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.SwingConstants; -import javax.swing.table.DefaultTableModel; - +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.gui.extra.ArrowButton; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jscrollpane.JScrollPaneExtension; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; -import java.awt.Component; -import java.awt.Container; - -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ Arrays.class, JComponentExtension.class, AbstractButtonExtension.class, JScrollPaneExtension.class }) public class DefaultSelectionPanel extends JPanel implements PreferencePanelIntf { private static final long serialVersionUID = 1L; + private final SettingsControl settingsCtrl; private final ScrollTable unusedPatternsTable; private final ScrollTable usedPatternsTable; @@ -41,13 +28,16 @@ public DefaultSelectionPanel(SettingsControl settingsCtrl) { super(new MigLayout("fill, bottom, insets 0", "[grow][][grow][]", "[grow, bottom][grow, top]")); this.settingsCtrl = settingsCtrl; - unusedPatternsTable = ScrollTable.create(Messages.getString("PreferenceDialog.DefaultSelectionUnused"), Source.class).add(this, "spany 2"); - new ArrowButton(SwingConstants.EAST, 1, 10).withActionListener(this::addPattern).addTo(this); - usedPatternsTable = ScrollTable.create(Messages.getString("PreferenceDialog.DefaultSelectionUsed"), Source.class).add(this, "spany 2"); - new ArrowButton(SwingConstants.NORTH, 1, 10).withActionListener(this::moveRuleRowUp).addTo(this, "wrap"); + unusedPatternsTable = + ScrollTable.create(Messages.getText("PreferenceDialog.DefaultSelectionUnused"), Source.class) + .add(this, "spany 2"); + new ArrowButton(SwingConstants.EAST, 1, 10).actionListener(this::addPattern).addTo(this); + usedPatternsTable = ScrollTable.create(Messages.getText("PreferenceDialog.DefaultSelectionUsed"), Source.class) + .add(this, "spany 2"); + new ArrowButton(SwingConstants.NORTH, 1, 10).actionListener(this::moveRuleRowUp).addTo(this, "wrap"); - new ArrowButton(SwingConstants.WEST, 1, 10).withActionListener(this::removePattern).addTo(this, "skip"); - new ArrowButton(SwingConstants.SOUTH, 1, 10).withActionListener(this::moveRuleRowDown).addTo(this, "skip"); + new ArrowButton(SwingConstants.WEST, 1, 10).actionListener(this::removePattern).addTo(this, "skip"); + new ArrowButton(SwingConstants.SOUTH, 1, 10).actionListener(this::moveRuleRowDown).addTo(this, "skip"); loadPreferenceSettings(); } @@ -73,8 +63,8 @@ public ScrollTable(String header, Collection items) { } private ScrollTable(String header, Stream items) { - this.table = new JTable(new DefaultTableModel(new String[] { header }, 1)); - this.scrollPane = new JScrollPane().withViewportView(table); + this.table = new JTable(new DefaultTableModel(new String[]{ header }, 1)); + this.scrollPane = new JScrollPane().viewportView(table); this.model = (DefaultTableModel) table.getModel(); model.removeRow(0); if (items != null) { @@ -84,7 +74,7 @@ private ScrollTable(String header, Stream items) { } public void addItem(E item) { - model.addRow(new Object[] { item }); + model.addRow(new Object[]{ item }); } public int getSelectedRow() { @@ -145,7 +135,7 @@ public List getItems() { @Override public Component[] getComponents() { - return new Component[] { scrollPane, table }; + return new Component[]{ scrollPane, table }; } @Override @@ -188,13 +178,13 @@ protected void moveRuleRowUp() { } public void loadPreferenceSettings() { - Source.values().stream().filter(not(settingsCtrl.getSettings().getOptionsDefaultSelectionQualityList()::contains)) + Source.values().stream().filter(not(settingsCtrl.settings.optionsDefaultSelectionQualityList::contains)) .forEach(unusedPatternsTable::addItem); - settingsCtrl.getSettings().getOptionsDefaultSelectionQualityList().forEach(usedPatternsTable::addItem); + settingsCtrl.settings.optionsDefaultSelectionQualityList.forEach(usedPatternsTable::addItem); } public void savePreferenceSettings() { - settingsCtrl.getSettings().setOptionsDefaultSelectionQualityList(usedPatternsTable.getItems()); + settingsCtrl.settings.optionsDefaultSelectionQualityList = usedPatternsTable.getItems(); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java index b5abd356..0e42f6fc 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java @@ -7,7 +7,7 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -public class EpisodeLibraryPanel extends VideoLibraryPanel { +public final class EpisodeLibraryPanel extends VideoLibraryPanel { @Serial private static final long serialVersionUID = -9175813173306481849L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java index ec589949..32b08c91 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java @@ -1,17 +1,14 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import javax.swing.*; import java.io.Serial; import java.nio.file.Path; import java.util.List; import java.util.function.Consumer; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; - +import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; @@ -20,11 +17,6 @@ import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldInteger; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldString; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; @@ -34,23 +26,17 @@ import org.lodder.subtools.multisubdownloader.settings.model.UpdateType; import org.lodder.subtools.sublibrary.Language; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JTextFieldExtension.class, JCheckBoxExtension.class, JComponentExtension.class, AbstractButtonExtension.class }) public class GeneralPanel extends JPanel implements PreferencePanelIntf { - @Serial - private static final long serialVersionUID = -5458593307643063563L; + @Serial private static final long serialVersionUID = -5458593307643063563L; private final GUI gui; private final SettingsControl settingsCtrl; - - private final MyComboBox cbxLanguage; + private final JComboBox cbxLanguage; private final JListWithImages defaultIncomingFoldersList; private final JListWithImages excludeList; - private final MyComboBox cbxUpdateCheckPeriod; - private final MyComboBox cbxUpdateType; + private final JComboBox cbxUpdateCheckPeriod; + private final JComboBox cbxUpdateType; private final JCheckBox chkUseProxy; private final MyTextFieldString txtProxyHost; private final MyTextFieldInteger txtProxyPort; @@ -60,129 +46,148 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { this.gui = gui; this.settingsCtrl = settingsCtrl; - JPanel settingsPanel = TitlePanel.title(Messages.getString("PreferenceDialog.Settings")) - .padding(0).paddingLeft(20).useGrid().fillContents(false).addTo(this, "span, grow, wrap"); + JPanel settingsPanel = TitlePanel.title(getText("PreferenceDialog.Settings")) + .padding(0).paddingLeft(20).useGrid().fillContents(false) + .addTo(this, "span, grow, wrap"); { - { - new JLabel(Messages.getString("PreferenceDialog.Language")).addTo(settingsPanel); - - this.cbxLanguage = new MyComboBox<>(Messages.getAvailableLanguages(), Language.class) - .withToMessageStringRenderer(Language::getMsgCode) - .addTo(settingsPanel, "wrap"); - } - - { - new JLabel(Messages.getString("PreferenceDialog.DefaultIncomingFolder")).addTo(settingsPanel, "aligny center, span 1 2"); - - new JScrollPane().addTo(settingsPanel, "growx, span, wrap") - .setViewportView(this.defaultIncomingFoldersList = JListWithImages.createForType(Path.class).distinctValues().build()); - - new JButton(Messages.getString("PreferenceDialog.AddFolder")) - .withActionListener( - () -> MemoryFolderChooser.getInstance() - .selectDirectory(settingsPanel, Messages.getString("PreferenceDialog.SelectFolder")) - .map(Path::toAbsolutePath) - .filter(path -> !defaultIncomingFoldersList.contains(path)) - .ifPresent(path -> defaultIncomingFoldersList.addItem(PathMatchType.FOLDER.getImage(), path))) - .addTo(settingsPanel, "span, split 2"); - - new JButton(Messages.getString("PreferenceDialog.DeleteFolder")) - .withActionListener(defaultIncomingFoldersList::removeSelectedItem) - .addTo(settingsPanel, "wrap, gapbottom 10px"); - } - { - new JLabel(Messages.getString("PreferenceDialog.ExcludeList")).addTo(settingsPanel, "aligny center, span 1 2"); - - new JScrollPane().addTo(settingsPanel, "growx, span, wrap") - .setViewportView(this.excludeList = JListWithImages.createForType(PathOrRegex.class).distinctValues().build()); - - Consumer addExcludeItemConsumer = type -> { - if (type == PathMatchType.FOLDER) { - MemoryFolderChooser.getInstance().selectDirectory(settingsPanel, Messages.getString("PreferenceDialog.SelectExcludeFolder")) - .map(Path::toAbsolutePath).map(PathOrRegex::new) - .ifPresent(pathOrRegex -> excludeList.addItem(pathOrRegex.getImage(), pathOrRegex)); - } else if (type == PathMatchType.REGEX) { - String regex = JOptionPane.showInputDialog(Messages.getString("PreferenceDialog.EnterRegex")); - if (StringUtils.isNotBlank(regex)) { - excludeList.addItem(PathMatchType.REGEX.getImage(), new PathOrRegex(regex)); - } + // Language \\ + + new JLabel(getText("PreferenceDialog.Language")).addTo(settingsPanel); + this.cbxLanguage = JComboBox.create(Messages.getAvailableLanguages()) + .toMessageStringRenderer(Language::getMsgCode) + .addTo(settingsPanel, "wrap"); + + // Default Incoming Folder \\ + + new JLabel(getText("PreferenceDialog.DefaultIncomingFolder")).addTo(settingsPanel, + "aligny center, span 1 2"); + + new JScrollPane() + .viewportView(this.defaultIncomingFoldersList = + JListWithImages.createForType(Path.class).distinctValues().build()) + .addTo(settingsPanel, "growx, span, wrap"); + + new JButton(getText("PreferenceDialog.AddFolder")) + .actionListener(() -> MemoryFolderChooser.getInstance() + .selectDirectory(settingsPanel, getText("PreferenceDialog.SelectFolder")) + .map(Path::toAbsolutePath) + .filter(path -> !defaultIncomingFoldersList.contains(path)) + .ifPresent(p -> defaultIncomingFoldersList.addItem(PathMatchType.FOLDER.image, p))) + .addTo(settingsPanel, "span, split 2"); + + new JButton(getText("PreferenceDialog.DeleteFolder")) + .actionListener(defaultIncomingFoldersList::removeSelectedItem) + .addTo(settingsPanel, "wrap, gapbottom 10px"); + + // Exclude List \\ + + new JLabel(getText("PreferenceDialog.ExcludeList")) + .addTo(settingsPanel, "aligny center, span 1 2"); + + new JScrollPane() + .viewportView(this.excludeList = + JListWithImages.createForType(PathOrRegex.class).distinctValues().build()) + .addTo(settingsPanel, "growx, span, wrap"); + + Consumer addExcludeItemConsumer = type -> { + if (type == PathMatchType.FOLDER) { + MemoryFolderChooser.getInstance() + .selectDirectory(settingsPanel, getText("PreferenceDialog.SelectExcludeFolder")) + .map(Path::toAbsolutePath) + .map(PathOrRegex::new) + .ifPresent(pathOrRegex -> excludeList.addItem(pathOrRegex.image, pathOrRegex)); + } else if (type == PathMatchType.REGEX) { + String regex = JOptionPane.showInputDialog(getText("PreferenceDialog.EnterRegex")); + if (StringUtils.isNotBlank(regex)) { + excludeList.addItem(PathMatchType.REGEX.image, new PathOrRegex(regex)); } - }; + } + }; - new JButton(Messages.getString("PreferenceDialog.AddFolder")) - .withActionListener(() -> addExcludeItemConsumer.accept(PathMatchType.FOLDER)) - .addTo(settingsPanel, "span, split 3"); + new JButton(getText("PreferenceDialog.AddFolder")) + .actionListener(() -> addExcludeItemConsumer.accept(PathMatchType.FOLDER)) + .addTo(settingsPanel, "span, split 3"); - new JButton(Messages.getString("PreferenceDialog.DeleteFolder")) - .withActionListener(excludeList::removeSelectedItem) - .addTo(settingsPanel); + new JButton(getText("PreferenceDialog.DeleteFolder")) + .actionListener(excludeList::removeSelectedItem) + .addTo(settingsPanel); - new JButton(Messages.getString("PreferenceDialog.RegexToevoegen")) - .withActionListener(() -> addExcludeItemConsumer.accept(PathMatchType.REGEX)) - .addTo(settingsPanel); - } + new JButton(getText("PreferenceDialog.RegexToevoegen")) + .actionListener(() -> addExcludeItemConsumer.accept(PathMatchType.REGEX)) + .addTo(settingsPanel); } { - JPanel updatePanel = TitlePanel.title(Messages.getString("PreferenceDialog.Update")) - .padding(0).paddingLeft(20).useGrid().fillContents(false).addTo(this, "span, grow, wrap"); - { - new JLabel(Messages.getString("PreferenceDialog.NewUpdateCheck")).addTo(updatePanel); - this.cbxUpdateCheckPeriod = new MyComboBox<>(UpdateCheckPeriod.values()) - .withToMessageStringRenderer(UpdateCheckPeriod::getLangCode) - .addTo(updatePanel, "wrap"); - new JLabel(Messages.getString("PreferenceDialog.UpdateType")).addTo(updatePanel); - this.cbxUpdateType = new MyComboBox<>(UpdateType.values()) - .withToMessageStringRenderer(UpdateType::getMsgCode).addTo(updatePanel); - } + JPanel updatePanel = TitlePanel.title(getText("PreferenceDialog.Update")) + .padding(0) + .paddingLeft(20) + .useGrid() + .fillContents(false) + .addTo(this, "span, grow, wrap"); + + new JLabel(getText("PreferenceDialog.NewUpdateCheck")).addTo(updatePanel); + this.cbxUpdateCheckPeriod = new JComboBox<>(UpdateCheckPeriod.values()) + .toMessageStringRenderer(UpdateCheckPeriod::getLangCode) + .addTo(updatePanel, "wrap"); + new JLabel(getText("PreferenceDialog.UpdateType")).addTo(updatePanel); + this.cbxUpdateType = new JComboBox<>(UpdateType.values()) + .toMessageStringRenderer(UpdateType::getMsgCode) + .addTo(updatePanel); } { - JPanel proxyPanel = TitlePanel.title(Messages.getString("PreferenceDialog.ConfigureProxy")) - .padding(0).paddingLeft(20).fillContents(false).addTo(this, "span, grow"); - - PanelCheckBox.checkbox(this.chkUseProxy = new JCheckBox(Messages.getString("PreferenceDialog.UseProxyServer"))) - .panelOnSameLine().panelLayout(new MigLayout("insets 0, fill")).leftGap(0).addTo(proxyPanel) - .addComponent(new JLabel(Messages.getString("PreferenceDialog.Hostname"))) - .addComponent("wrap", this.txtProxyHost = MyTextFieldString.builder().requireValue().build().withColumns(30)) - .addComponent(new JLabel(Messages.getString("PreferenceDialog.Port"))) - .addComponent(this.txtProxyPort = MyTextFieldInteger.builder().requireValue().build().withColumns(5)); + JPanel proxyPanel = TitlePanel.title(getText("PreferenceDialog.ConfigureProxy")) + .padding(0) + .paddingLeft(20) + .fillContents(false) + .addTo(this, "span, grow"); + + PanelCheckBox.checkbox( + this.chkUseProxy = new JCheckBox(getText("PreferenceDialog.UseProxyServer"))) + .panelOnSameLine() + .panelLayout(new MigLayout("insets 0, fill")) + .leftGap(0) + .addTo(proxyPanel) + .addComponent(new JLabel(getText("PreferenceDialog.Hostname"))) + .addComponent("wrap", + this.txtProxyHost = MyTextFieldString.builder().requireValue().build().columns(30)) + .addComponent(new JLabel(getText("PreferenceDialog.Port"))) + .addComponent(this.txtProxyPort = MyTextFieldInteger.builder().requireValue().build().columns(5)); } loadPreferenceSettings(); } public void loadPreferenceSettings() { - cbxLanguage.setSelectedItem(settingsCtrl.getSettings().getLanguage()); - defaultIncomingFoldersList.addItems(PathMatchType.FOLDER.getImage(), settingsCtrl.getSettings().getDefaultIncomingFolders()); - settingsCtrl.getSettings().getExcludeList().forEach(pathOrRegex -> excludeList.addItem(pathOrRegex.getImage(), pathOrRegex)); - cbxUpdateCheckPeriod.setSelectedItem(settingsCtrl.getSettings().getUpdateCheckPeriod()); - cbxUpdateType.setSelectedItem(settingsCtrl.getSettings().getUpdateType()); - chkUseProxy.setSelected(settingsCtrl.getSettings().isGeneralProxyEnabled()); - txtProxyHost.setText(settingsCtrl.getSettings().getGeneralProxyHost()); - txtProxyPort.setObject(settingsCtrl.getSettings().getGeneralProxyPort()); + cbxLanguage.setSelectedItem(settingsCtrl.settings.language); + defaultIncomingFoldersList.addItems(PathMatchType.FOLDER.image, settingsCtrl.settings.defaultIncomingFolders); + settingsCtrl.settings.excludeList.forEach(pathOrRegex -> excludeList.addItem(pathOrRegex.image, pathOrRegex)); + cbxUpdateCheckPeriod.setSelectedItem(settingsCtrl.settings.updateCheckPeriod); + cbxUpdateType.setSelectedItem(settingsCtrl.settings.updateType); + chkUseProxy.setSelected(settingsCtrl.settings.generalProxyEnabled); + txtProxyHost.setText(settingsCtrl.settings.generalProxyHost); + txtProxyPort.setObject(settingsCtrl.settings.generalProxyPort); } public void savePreferenceSettings() { - if (Messages.getLanguage() != cbxLanguage.getSelectedItem()) { - Messages.setLanguage(cbxLanguage.getSelectedItem()); + if (Messages.language != cbxLanguage.getSelectedValue()) { + Messages.language = cbxLanguage.getSelectedValue(); gui.redraw(); } List defaultIncomingFolders = defaultIncomingFoldersList.stream().map(LabelPanel::getObject).toList(); - List exclList = excludeList.stream().map(labelPanel -> new PathOrRegex(labelPanel.getObject().getValue())).toList(); - settingsCtrl.getSettings() - .setLanguage(cbxLanguage.getSelectedItem()) - .setDefaultIncomingFolders(defaultIncomingFolders) - .setExcludeList(exclList) - .setUpdateCheckPeriod(cbxUpdateCheckPeriod.getSelectedItem()) - .setUpdateType(cbxUpdateType.getSelectedItem()) - .setGeneralProxyEnabled(chkUseProxy.isSelected()) - .setGeneralProxyHost(txtProxyHost.getText()) - .setGeneralProxyPort(txtProxyPort.getOptionalObject().orElse(80)); - + List exclList = + excludeList.stream().map(labelPanel -> new PathOrRegex(labelPanel.getObject().value)).toList(); + settingsCtrl.settings.language = cbxLanguage.getSelectedValue(); + settingsCtrl.settings.defaultIncomingFolders = defaultIncomingFolders; + settingsCtrl.settings.replaceExcludeList(exclList); + settingsCtrl.settings.updateCheckPeriod = cbxUpdateCheckPeriod.getSelectedValue(); + settingsCtrl.settings.updateType = cbxUpdateType.getSelectedValue(); + settingsCtrl.settings.generalProxyEnabled = chkUseProxy.isSelected(); + settingsCtrl.settings.generalProxyHost = txtProxyHost.getText(); + settingsCtrl.settings.generalProxyPort = txtProxyPort.getOptionalObject().orElse(80); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java index ed27d4a2..5dcef703 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java @@ -7,7 +7,7 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -public class MovieLibraryPanel extends VideoLibraryPanel { +public final class MovieLibraryPanel extends VideoLibraryPanel { @Serial private static final long serialVersionUID = -9175813173306481849L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java index afdf65e9..776998b3 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java @@ -1,33 +1,21 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; -import java.io.Serial; +import static org.lodder.subtools.multisubdownloader.Messages.*; -import javax.swing.JCheckBox; -import javax.swing.JPanel; -import javax.swing.JSlider; +import javax.swing.*; +import java.io.Serial; -import org.lodder.subtools.multisubdownloader.Messages; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jslider.JSliderExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.SettingsProcessEpisodeSource; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JTextFieldExtension.class, JCheckBoxExtension.class, JComponentExtension.class, JSliderExtension.class }) public class OptionsPanel extends JPanel implements PreferencePanelIntf { - @Serial - private static final long serialVersionUID = -5458593307643063563L; + @Serial private static final long serialVersionUID = -5458593307643063563L; private final SettingsControl settingsCtrl; - private final JCheckBox chkAlwaysConfirm; private final JCheckBox chkMinScoreSelection; private final JSlider sldMinScoreSelection; @@ -38,74 +26,98 @@ public class OptionsPanel extends JPanel implements PreferencePanelIntf { private final JCheckBox chkExcludeHearingImpaired; private final JCheckBox chkOnlyFound; private final JCheckBox chkStopOnSearchError; - private final MyComboBox cbxEpisodeProcessSource; + private final JComboBox cbxEpisodeProcessSource; private final JCheckBox chkConfirmProviderMapping; public OptionsPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - TitlePanel.title(Messages.getString("PreferenceDialog.DownloadOptions")) - .marginBottom(0).padding(0).paddingLeft(20).addTo(this, "span, grow, wrap") - .addComponent(this.chkAlwaysConfirm = new JCheckBox(Messages.getString("PreferenceDialog.CheckBeforeDownloading")), "wrap") - .addComponent("wrap, grow", PanelCheckBox - .checkbox(this.chkMinScoreSelection = new JCheckBox(Messages.getString("PreferenceDialog.MinAutomaticScoreSelection"))) - .panelOnSameLine().build() - .addComponent(this.sldMinScoreSelection = new JSlider().withMinimum(0).withMaximum(100), "wrap")) - .addComponent("wrap, grow", PanelCheckBox - .checkbox(this.chkDefaultSelection = new JCheckBox(Messages.getString("PreferenceDialog.DefaultSelection"), null, true)) - .panelOnNewLine().build() - .addComponent(this.pnlDefaultSelection = new DefaultSelectionPanel(settingsCtrl))); - - TitlePanel.title(Messages.getString("PreferenceDialog.SearchFilter")) - .marginBottom(0).padding(0).paddingLeft(20).addTo(this, "span, grow, wrap") - .addComponent(this.chkSubtitleExactMethod = new JCheckBox(Messages.getString("PreferenceDialog.SearchFilterExact")), "wrap") - .addComponent(this.chkSubtitleKeywordMethod = new JCheckBox(Messages.getString("PreferenceDialog.SearchFilterKeyword")), "wrap") - .addComponent(this.chkExcludeHearingImpaired = new JCheckBox(Messages.getString("PreferenceDialog.ExcludeHearingImpaired"))); - - TitlePanel.title(Messages.getString("PreferenceDialog.TableOptions")) - .marginBottom(0).padding(0).paddingLeft(20).addTo(this, "span, grow, wrap") - .addComponent(this.chkOnlyFound = new JCheckBox(Messages.getString("PreferenceDialog.ShowOnlyFound"))); - - TitlePanel.title(Messages.getString("PreferenceDialog.ErrorHandlingOption")) - .marginBottom(0).padding(0).paddingLeft(20).addTo(this, "span, grow, wrap") - .addComponent(this.chkStopOnSearchError = new JCheckBox(Messages.getString("PreferenceDialog.StopAfterError"))); - - TitlePanel.title(Messages.getString("PreferenceDialog.SerieDatabaseSource")) - .marginBottom(0).padding(0).paddingLeft(20).addTo(this, "span, grow") - .addComponent(this.cbxEpisodeProcessSource = MyComboBox.ofValues(SettingsProcessEpisodeSource.values()), "wrap") - .addComponent(this.chkConfirmProviderMapping = new JCheckBox(Messages.getString("PreferenceDialog.ConfirmProviderMapping"))); + TitlePanel.title(getText("PreferenceDialog.DownloadOptions")) + .marginBottom(0) + .padding(0) + .paddingLeft(20) + .addTo(this, "span, grow, wrap") + .addComponent(this.chkAlwaysConfirm = + new JCheckBox(getText("PreferenceDialog.CheckBeforeDownloading")), "wrap") + .addComponent("wrap, grow", PanelCheckBox.checkbox(this.chkMinScoreSelection = + new JCheckBox(getText("PreferenceDialog.MinAutomaticScoreSelection"))) + .panelOnSameLine() + .build() + .addComponent(this.sldMinScoreSelection = new JSlider().minimum(0).maximum(100), "wrap")) + .addComponent("wrap, grow", + PanelCheckBox.checkbox(this.chkDefaultSelection = + new JCheckBox(getText("PreferenceDialog.DefaultSelection"), null, true)) + .panelOnNewLine() + .build() + .addComponent(this.pnlDefaultSelection = new DefaultSelectionPanel(settingsCtrl))); + + TitlePanel.title(getText("PreferenceDialog.SearchFilter")) + .marginBottom(0) + .padding(0) + .paddingLeft(20) + .addTo(this, "span, grow, wrap") + .addComponent( + this.chkSubtitleExactMethod = new JCheckBox(getText("PreferenceDialog.SearchFilterExact")), + "wrap") + .addComponent(this.chkSubtitleKeywordMethod = + new JCheckBox(getText("PreferenceDialog.SearchFilterKeyword")), "wrap") + .addComponent(this.chkExcludeHearingImpaired = + new JCheckBox(getText("PreferenceDialog.ExcludeHearingImpaired"))); + + TitlePanel.title(getText("PreferenceDialog.TableOptions")) + .marginBottom(0) + .padding(0) + .paddingLeft(20) + .addTo(this, "span, grow, wrap") + .addComponent(this.chkOnlyFound = new JCheckBox(getText("PreferenceDialog.ShowOnlyFound"))); + + TitlePanel.title(getText("PreferenceDialog.ErrorHandlingOption")) + .marginBottom(0) + .padding(0) + .paddingLeft(20) + .addTo(this, "span, grow, wrap") + .addComponent(this.chkStopOnSearchError = new JCheckBox(getText("PreferenceDialog.StopAfterError"))); + + TitlePanel.title(getText("PreferenceDialog.SerieDatabaseSource")) + .marginBottom(0) + .padding(0) + .paddingLeft(20) + .addTo(this, "span, grow") + .addComponent(this.cbxEpisodeProcessSource = new JComboBox<>(SettingsProcessEpisodeSource.values()), + "wrap") + .addComponent(this.chkConfirmProviderMapping = + new JCheckBox(getText("PreferenceDialog.ConfirmProviderMapping"))); loadPreferenceSettings(); } public void loadPreferenceSettings() { - chkAlwaysConfirm.setSelected(settingsCtrl.getSettings().isOptionsAlwaysConfirm()); - chkMinScoreSelection.setSelected(settingsCtrl.getSettings().isOptionsMinAutomaticSelection()); - sldMinScoreSelection.setValue(settingsCtrl.getSettings().getOptionsMinAutomaticSelectionValue()); - chkDefaultSelection.setSelected(settingsCtrl.getSettings().isOptionsDefaultSelection()); - chkSubtitleExactMethod.setSelected(settingsCtrl.getSettings().isOptionSubtitleExactMatch()); - chkSubtitleKeywordMethod.setSelected(settingsCtrl.getSettings().isOptionSubtitleKeywordMatch()); - chkExcludeHearingImpaired.setSelected(settingsCtrl.getSettings().isOptionSubtitleExcludeHearingImpaired()); - chkOnlyFound.setSelected(settingsCtrl.getSettings().isOptionsShowOnlyFound()); - chkStopOnSearchError.setSelected(settingsCtrl.getSettings().isOptionsStopOnSearchError()); - cbxEpisodeProcessSource.setSelectedItem(settingsCtrl.getSettings().getProcessEpisodeSource()); - chkConfirmProviderMapping.setSelected(settingsCtrl.getSettings().isOptionsConfirmProviderMapping()); + chkAlwaysConfirm.setSelected(settingsCtrl.settings.optionsAlwaysConfirm); + chkMinScoreSelection.setSelected(settingsCtrl.settings.optionsMinAutomaticSelection); + sldMinScoreSelection.setValue(settingsCtrl.settings.optionsMinAutomaticSelectionValue); + chkDefaultSelection.setSelected(settingsCtrl.settings.optionsDefaultSelection); + chkSubtitleExactMethod.setSelected(settingsCtrl.settings.optionSubtitleExactMatch); + chkSubtitleKeywordMethod.setSelected(settingsCtrl.settings.optionSubtitleKeywordMatch); + chkExcludeHearingImpaired.setSelected(settingsCtrl.settings.optionSubtitleExcludeHearingImpaired); + chkOnlyFound.setSelected(settingsCtrl.settings.optionsShowOnlyFound); + chkStopOnSearchError.setSelected(settingsCtrl.settings.optionsStopOnSearchError); + cbxEpisodeProcessSource.setSelectedItem(settingsCtrl.settings.processEpisodeSource); + chkConfirmProviderMapping.setSelected(settingsCtrl.settings.optionsConfirmProviderMapping); } public void savePreferenceSettings() { - settingsCtrl.getSettings() - .setOptionsAlwaysConfirm(chkAlwaysConfirm.isSelected()) - .setOptionsMinAutomaticSelection(chkMinScoreSelection.isSelected()) - .setOptionsMinAutomaticSelectionValue(sldMinScoreSelection.getValue()) - .setOptionsDefaultSelection(chkDefaultSelection.isSelected()) - .setOptionSubtitleExactMatch(chkSubtitleExactMethod.isSelected()) - .setOptionSubtitleKeywordMatch(chkSubtitleKeywordMethod.isSelected()) - .setOptionSubtitleExcludeHearingImpaired(chkExcludeHearingImpaired.isSelected()) - .setOptionsShowOnlyFound(chkOnlyFound.isSelected()) - .setOptionsStopOnSearchError(chkStopOnSearchError.isSelected()) - .setProcessEpisodeSource(cbxEpisodeProcessSource.getSelectedItem()) - .setOptionsConfirmProviderMapping(chkConfirmProviderMapping.isSelected()); + settingsCtrl.settings.optionsAlwaysConfirm = chkAlwaysConfirm.isSelected(); + settingsCtrl.settings.optionsMinAutomaticSelection = chkMinScoreSelection.isSelected(); + settingsCtrl.settings.optionsMinAutomaticSelectionValue = sldMinScoreSelection.getValue(); + settingsCtrl.settings.optionsDefaultSelection = chkDefaultSelection.isSelected(); + settingsCtrl.settings.optionSubtitleExactMatch = chkSubtitleExactMethod.isSelected(); + settingsCtrl.settings.optionSubtitleKeywordMatch = chkSubtitleKeywordMethod.isSelected(); + settingsCtrl.settings.optionSubtitleExcludeHearingImpaired = chkExcludeHearingImpaired.isSelected(); + settingsCtrl.settings.optionsShowOnlyFound = chkOnlyFound.isSelected(); + settingsCtrl.settings.optionsStopOnSearchError = chkStopOnSearchError.isSelected(); + settingsCtrl.settings.processEpisodeSource = cbxEpisodeProcessSource.getSelectedValue(); + settingsCtrl.settings.optionsConfirmProviderMapping = chkConfirmProviderMapping.isSelected(); pnlDefaultSelection.savePreferenceSettings(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java index 6161d161..5a7f59d7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java @@ -1,40 +1,29 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; import static java.util.function.Predicate.*; +import static org.lodder.subtools.multisubdownloader.Messages.*; +import javax.swing.*; import java.io.Serial; import java.nio.file.Path; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; - -import org.lodder.subtools.multisubdownloader.Messages; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.JListWithImages; import org.lodder.subtools.multisubdownloader.gui.extra.JListWithImages.LabelPanel; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyPasswordField; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldString; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.PathMatchType; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesApi; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JTextFieldExtension.class, JCheckBoxExtension.class, JComponentExtension.class, AbstractButtonExtension.class }) public class SerieProvidersPanel extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = -5458593307643063563L; + private final SettingsControl settingsCtrl; private final JCheckBox chkSourceAddic7ed; private final JCheckBox chkUserAddic7edLogin; @@ -55,99 +44,110 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - JPanel titelPanel = TitlePanel.title(Messages.getString("PreferenceDialog.SelectPreferredSources")).addTo(this, "span, grow"); + JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.SelectPreferredSources")) + .addTo(this, "span, grow"); { // ADDIC7ED this.chkSourceAddic7ed = new JCheckBox("Addic7ed"); - this.chkUserAddic7edLogin = new JCheckBox(Messages.getString("PreferenceDialog.UseAddic7edLogin")); - this.chkSourceAddic7edProxy = new JCheckBox(Messages.getString("PreferenceDialog.Proxy")); + this.chkUserAddic7edLogin = new JCheckBox(getText("PreferenceDialog.UseAddic7edLogin")); + this.chkSourceAddic7edProxy = new JCheckBox(getText("PreferenceDialog.Proxy")); - PanelCheckBox.checkbox(chkSourceAddic7ed).panelOnNewLine().addTo(titelPanel, "wrap") + PanelCheckBox.checkbox(chkSourceAddic7ed).panelOnNewLine().addTo(titlePanel, "wrap") .addComponent("wrap", chkSourceAddic7edProxy) .addComponent(PanelCheckBox.checkbox(chkUserAddic7edLogin).panelOnNewLine() .panelLayout(new MigLayout("insets 0, novisualpadding")).build() - .addComponent(new JLabel(Messages.getString("PreferenceDialog.Username"))) - .addComponent("wrap", this.txtAddic7edUsername = MyTextFieldString.builder().requireValue().build().withColumns(20)) - .addComponent(new JLabel(Messages.getString("PreferenceDialog.Password"))) - .addComponent(this.txtAddic7edPassword = MyPasswordField.builder().requireValue().build().withColumns(20))); + .addComponent(new JLabel(getText("PreferenceDialog.Username"))) + .addComponent("wrap", this.txtAddic7edUsername = + MyTextFieldString.builder().requireValue().build().columns(20)) + .addComponent(new JLabel(getText("PreferenceDialog.Password"))) + .addComponent(this.txtAddic7edPassword = + MyPasswordField.builder().requireValue().build().columns(20))); // TV SUBTITLES - this.chkSourceTvSubtitles = new JCheckBox("Tv Subtitles").addTo(titelPanel, "wrap"); + this.chkSourceTvSubtitles = new JCheckBox("Tv Subtitles").addTo(titlePanel, "wrap"); // PODNAPISI - this.chkSourcePodnapisi = new JCheckBox("Podnapisi").addTo(titelPanel, "wrap"); + this.chkSourcePodnapisi = new JCheckBox("Podnapisi").addTo(titlePanel, "wrap"); // OPENSUBTITLES this.chkSourceOpenSubtitles = new JCheckBox("OpenSubtitles"); - this.chkUserOpenSubtitlesLogin = new JCheckBox(Messages.getString("PreferenceDialog.UseOpenSubtitlesLogin")); - PanelCheckBox.checkbox(chkSourceOpenSubtitles).panelOnNewLine().addTo(titelPanel, "wrap") + this.chkUserOpenSubtitlesLogin = new JCheckBox(getText("PreferenceDialog.UseOpenSubtitlesLogin")); + PanelCheckBox.checkbox(chkSourceOpenSubtitles).panelOnNewLine().addTo(titlePanel, "wrap") .addComponent(PanelCheckBox.checkbox(chkUserOpenSubtitlesLogin).panelOnNewLine() .panelLayout(new MigLayout("insets 0, novisualpadding")).build() - .addComponent(new JLabel(Messages.getString("PreferenceDialog.Username"))) - .addComponent("wrap", txtOpenSubtitlesUsername = MyTextFieldString.builder().requireValue().build().withColumns(20)) - .addComponent(new JLabel(Messages.getString("PreferenceDialog.Password"))) - .addComponent(txtOpenSubtitlesPassword = MyPasswordField.builder().requireValue().build().withColumns(20))); + .addComponent(new JLabel(getText("PreferenceDialog.Username"))) + .addComponent("wrap", txtOpenSubtitlesUsername = + MyTextFieldString.builder().requireValue().build().columns(20)) + .addComponent(new JLabel(getText("PreferenceDialog.Password"))) + .addComponent(txtOpenSubtitlesPassword = + MyPasswordField.builder().requireValue().build().columns(20))); // SUBSCENE - this.chkSourceSubscene = new JCheckBox("Subscene").addTo(titelPanel, "wrap"); + this.chkSourceSubscene = new JCheckBox("Subscene").addTo(titlePanel, "wrap"); // LOCAL - this.chkSourceLocal = new JCheckBox(Messages.getString("PreferenceDialog.Local")); - JScrollPane scrlPlocalSources = - new JScrollPane().scrollPane(this.localSourcesFoldersList = JListWithImages.createForType(Path.class).distinctValues().build()); - JButton btnBrowseLocalSources = new JButton(Messages.getString("PreferenceDialog.AddFolder")) - .withActionListener(() -> MemoryFolderChooser.getInstance() - .selectDirectory(this, Messages.getString("PreferenceDialog.SelectFolder")) + this.chkSourceLocal = new JCheckBox(getText("PreferenceDialog.Local")); + JScrollPane scrLocalSources = + new JScrollPane().viewportView(this.localSourcesFoldersList = + JListWithImages.createForType(Path.class).distinctValues().build()); + JButton btnBrowseLocalSources = new JButton(getText("PreferenceDialog.AddFolder")) + .actionListener(() -> MemoryFolderChooser.getInstance() + .selectDirectory(this, getText("PreferenceDialog.SelectFolder")) .map(Path::toAbsolutePath).filter(not(localSourcesFoldersList::contains)) - .ifPresent(path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.getImage(), path))); - JButton btnRemoveLocalSources = new JButton(Messages.getString("PreferenceDialog.DeleteFolder")) - .withActionListener(localSourcesFoldersList::removeSelectedItem); - - PanelCheckBox.checkbox(chkSourceLocal).panelOnNewLine().addTo(titelPanel) - .addComponent("aligny top, gapy 5px", new JLabel(Messages.getString("PreferenceDialog.LocalFolderWithSubtitles"))) - .addComponent("wrap", new JPanel(new MigLayout("insets 0", "[grow, nogrid]")).addComponent("split", btnBrowseLocalSources) - .addComponent("wrap", btnRemoveLocalSources).addComponent("wrap", scrlPlocalSources)); + .ifPresent(path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.image, path))); + JButton btnRemoveLocalSources = new JButton(getText("PreferenceDialog.DeleteFolder")) + .actionListener(localSourcesFoldersList::removeSelectedItem); + + PanelCheckBox.checkbox(chkSourceLocal).panelOnNewLine().addTo(titlePanel) + .addComponent("aligny top, gapy 5px", + new JLabel(getText("PreferenceDialog.LocalFolderWithSubtitles"))) + .addComponent("wrap", + new JPanel(new MigLayout("insets 0", "[grow, nogrid]")) + .addComponent("split", btnBrowseLocalSources) + .addComponent("wrap", btnRemoveLocalSources) + .addComponent("wrap", scrLocalSources)); } loadPreferenceSettings(); } public void loadPreferenceSettings() { - chkSourceAddic7ed.setSelected(settingsCtrl.getSettings().isSerieSourceAddic7ed()); - chkUserAddic7edLogin.setSelected(settingsCtrl.getSettings().isLoginAddic7edEnabled()); - chkSourceAddic7edProxy.setSelected(settingsCtrl.getSettings().isSerieSourceAddic7edProxy()); - // chkSourceAddic7edProxy.setEnabled(settingsCtrl.getSettings().isSerieSourceAddic7ed()); - txtAddic7edUsername.setText(settingsCtrl.getSettings().getLoginAddic7edUsername()); - txtAddic7edPassword.setText(settingsCtrl.getSettings().getLoginAddic7edPassword()); - - chkSourceTvSubtitles.setSelected(settingsCtrl.getSettings().isSerieSourceTvSubtitles()); - chkSourcePodnapisi.setSelected(settingsCtrl.getSettings().isSerieSourcePodnapisi()); - chkSourceOpenSubtitles.setSelected(settingsCtrl.getSettings().isSerieSourceOpensubtitles()); - chkUserOpenSubtitlesLogin.setSelected(settingsCtrl.getSettings().isLoginOpenSubtitlesEnabled()); - txtOpenSubtitlesUsername.setText(settingsCtrl.getSettings().getLoginOpenSubtitlesUsername()); - txtOpenSubtitlesPassword.setText(settingsCtrl.getSettings().getLoginOpenSubtitlesPassword()); - chkSourceSubscene.setSelected(settingsCtrl.getSettings().isSerieSourceSubscene()); - chkSourceLocal.setSelected(settingsCtrl.getSettings().isSerieSourceLocal()); - settingsCtrl.getSettings().getLocalSourcesFolders().forEach(path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.getImage(), path)); + chkSourceAddic7ed.setSelected(settingsCtrl.settings.serieSourceAddic7ed); + chkUserAddic7edLogin.setSelected(settingsCtrl.settings.loginAddic7edEnabled); + chkSourceAddic7edProxy.setSelected(settingsCtrl.settings.serieSourceAddic7edProxy); + // chkSourceAddic7edProxy.setEnabled(settingsCtrl.settings.serieSourceAddic7ed); + txtAddic7edUsername.setText(settingsCtrl.settings.loginAddic7edUsername); + txtAddic7edPassword.setText(settingsCtrl.settings.loginAddic7edPassword); + + chkSourceTvSubtitles.setSelected(settingsCtrl.settings.serieSourceTvSubtitles); + chkSourcePodnapisi.setSelected(settingsCtrl.settings.serieSourcePodnapisi); + chkSourceOpenSubtitles.setSelected(settingsCtrl.settings.serieSourceOpensubtitles); + chkUserOpenSubtitlesLogin.setSelected(settingsCtrl.settings.loginOpenSubtitlesEnabled); + txtOpenSubtitlesUsername.setText(settingsCtrl.settings.loginOpenSubtitlesUsername); + txtOpenSubtitlesPassword.setText(settingsCtrl.settings.loginOpenSubtitlesPassword); + chkSourceSubscene.setSelected(settingsCtrl.settings.serieSourceSubscene); + chkSourceLocal.setSelected(settingsCtrl.settings.serieSourceLocal); + settingsCtrl.settings.localSourcesFolders.forEach( + path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.image, path)); } public void savePreferenceSettings() { - settingsCtrl.getSettings() - .setSerieSourceAddic7ed(chkSourceAddic7ed.isSelected()) - .setLoginAddic7edEnabled(chkUserAddic7edLogin.isSelected()) - .setSerieSourceAddic7edProxy(chkSourceAddic7edProxy.isSelected()) - .setLoginAddic7edUsername(txtAddic7edUsername.getText()) - .setLoginAddic7edPassword(new String(txtAddic7edPassword.getPassword())) - .setSerieSourceTvSubtitles(chkSourceTvSubtitles.isSelected()) - .setSerieSourcePodnapisi(chkSourcePodnapisi.isSelected()) - .setSerieSourceOpensubtitles(chkSourceOpenSubtitles.isSelected()) - .setLoginOpenSubtitlesEnabled(chkUserOpenSubtitlesLogin.isSelected()) - .setLoginOpenSubtitlesUsername(txtOpenSubtitlesUsername.getText()) - .setLoginOpenSubtitlesPassword(new String(txtOpenSubtitlesPassword.getPassword())) - .setSerieSourceSubscene(chkSourceSubscene.isSelected()) - .setSerieSourceLocal(chkSourceLocal.isSelected()) - .setLocalSourcesFolders(localSourcesFoldersList.stream().map(LabelPanel::getObject).toList()); + settingsCtrl.settings.serieSourceAddic7ed = chkSourceAddic7ed.isSelected(); + settingsCtrl.settings.loginAddic7edEnabled = chkUserAddic7edLogin.isSelected(); + settingsCtrl.settings.serieSourceAddic7edProxy = chkSourceAddic7edProxy.isSelected(); + settingsCtrl.settings.loginAddic7edUsername = txtAddic7edUsername.getText(); + settingsCtrl.settings.loginAddic7edPassword = new String(txtAddic7edPassword.getPassword()); + settingsCtrl.settings.serieSourceTvSubtitles = chkSourceTvSubtitles.isSelected(); + settingsCtrl.settings.serieSourcePodnapisi = chkSourcePodnapisi.isSelected(); + settingsCtrl.settings.serieSourceOpensubtitles = chkSourceOpenSubtitles.isSelected(); + settingsCtrl.settings.loginOpenSubtitlesEnabled = chkUserOpenSubtitlesLogin.isSelected(); + settingsCtrl.settings.loginOpenSubtitlesUsername = txtOpenSubtitlesUsername.getText(); + settingsCtrl.settings.loginOpenSubtitlesPassword = new String(txtOpenSubtitlesPassword.getPassword()); + settingsCtrl.settings.serieSourceSubscene = chkSourceSubscene.isSelected(); + settingsCtrl.settings.serieSourceLocal = chkSourceLocal.isSelected(); + settingsCtrl.settings.localSourcesFolders = + localSourcesFoldersList.stream().map(LabelPanel::getObject).toList(); } private boolean hasValidSettingsAddic7ed() { @@ -158,8 +158,9 @@ private boolean hasValidSettingsOpenSubtitles() { if (!txtOpenSubtitlesUsername.hasValidValue() || !txtOpenSubtitlesPassword.hasValidValue()) { return false; } - if (chkUserOpenSubtitlesLogin.isSelected() && !OpenSubtitlesApi.isValidCredentials(txtOpenSubtitlesUsername.getText(), - new String(txtOpenSubtitlesPassword.getPassword()))) { + if (chkUserOpenSubtitlesLogin.isSelected() && + !OpenSubtitlesApi.isValidCredentials(txtOpenSubtitlesUsername.getText(), + new String(txtOpenSubtitlesPassword.getPassword()))) { txtOpenSubtitlesUsername.setErrorBorder(); txtOpenSubtitlesPassword.setErrorBorder(); return false; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java index 3316f73c..4b55e41f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java @@ -1,5 +1,10 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; import java.io.Serial; import java.util.LinkedHashMap; import java.util.List; @@ -11,24 +16,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; - -import org.lodder.subtools.multisubdownloader.Messages; +import lombok.extern.slf4j.Slf4j; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.dialog.StructureBuilderDialog; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldString; import org.lodder.subtools.multisubdownloader.lib.library.FilenameLibraryBuilder; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; @@ -37,21 +29,15 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import java.awt.Color; - -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JTextFieldExtension.class, JComponentExtension.class, JCheckBoxExtension.class, AbstractButtonExtension.class }) +@Slf4j public class StructureFilePanel extends JPanel { - @Serial - private static final long serialVersionUID = -5458593307643063563L; - private final LibrarySettings librarySettings; + @Serial private static final long serialVersionUID = -5458593307643063563L; + private final LibrarySettings librarySettings; private final MyTextFieldString txtFileStructure; private final JCheckBox chkReplaceSpace; - private final MyComboBox cbxReplaceSpaceChar; + private final JComboBox cbxReplaceSpaceChar; private final JCheckBox chkIncludeLanguageCode; private final Supplier addLanguageSupplier; private final LanguageMapping languageMapping = new LanguageMapping(); @@ -61,78 +47,87 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titelPanel = TitlePanel.title(Messages.getString("PreferenceDialog.RenameFiles")) - .margin(0).padding(0).marginLeft(20).paddingLeft(20).addTo(this, "span, grow"); - + JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.RenameFiles")) + .margin(0) + .padding(0) + .marginLeft(20) + .paddingLeft(20) + .addTo(this, "span, grow"); + + new JLabel(getText("PreferenceDialog.Structure")).addTo(titlePanel, "shrink"); + this.txtFileStructure = + MyTextFieldString.builder().requireValue().build().columns(20).addTo(titlePanel, "grow"); + new JButton(getText("StructureBuilderDialog.Structure")) + .actionListener(() -> { + StructureBuilderDialog sDialog = + new StructureBuilderDialog(null, getText("PreferenceDialog.StructureBuilderTitle"), + true, videoType, StructureBuilderDialog.StructureType.FILE, manager, + userInteractionHandler, getLibraryStructureBuilder()); + String value = sDialog.showDialog(txtFileStructure.getText()); + if (!value.isEmpty()) { + txtFileStructure.setText(value); + } + + }).addTo(titlePanel, "shrink, wrap"); + + this.chkReplaceSpace = new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith")); + + PanelCheckBox.checkbox(chkReplaceSpace) + .panelOnSameLine() + .addTo(titlePanel, "wrap") + .addComponent("width pref+10px, wrap", + this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); + + this.chkIncludeLanguageCode = + new JCheckBox(getText("PreferenceDialog.IncludeLanguageInFileName")) + .selectedListener(languageMapping::refreshState).addTo(titlePanel, "wrap"); + + JPanel languagePanelRoot = PanelCheckBox.checkbox(chkIncludeLanguageCode) + .panelOnNewLine() + .panelLayout(new MigLayout("insets 0, novisualpadding", "[][][]")) + .addTo(titlePanel, "span, growx"); { - new JLabel(Messages.getString("PreferenceDialog.Structure")).addTo(titelPanel, "shrink"); - this.txtFileStructure = MyTextFieldString.builder().requireValue().build().withColumns(20).addTo(titelPanel, "grow"); - new JButton(Messages.getString("StructureBuilderDialog.Structure")) - .withActionListener(() -> { - StructureBuilderDialog sDialog = - new StructureBuilderDialog(null, Messages.getString("PreferenceDialog.StructureBuilderTitle"), true, videoType, - StructureBuilderDialog.StructureType.FILE, manager, userInteractionHandler, getLibraryStructureBuilder()); - String value = sDialog.showDialog(txtFileStructure.getText()); - if (!value.isEmpty()) { - txtFileStructure.setText(value); - } - - }) - .addTo(titelPanel, "shrink, wrap"); - - this.chkReplaceSpace = new JCheckBox(Messages.getString("PreferenceDialog.ReplaceSpaceWith")); - - PanelCheckBox.checkbox(chkReplaceSpace).panelOnSameLine().addTo(titelPanel, "wrap") - .addComponent("width pref+10px, wrap", this.cbxReplaceSpaceChar = MyComboBox.ofValues('-', '.', '_')); - - this.chkIncludeLanguageCode = new JCheckBox(Messages.getString("PreferenceDialog.IncludeLanguageInFileName")) - .withSelectedListener(languageMapping::refreshState).addTo(titelPanel, "wrap"); - - JPanel languagePanelRoot = PanelCheckBox.checkbox(chkIncludeLanguageCode) - .panelOnNewLine().panelLayout(new MigLayout("insets 0, novisualpadding", "[][][]")) - .addTo(titelPanel, "span, growx"); - { - JPanel languagePanel = new JPanel(new MigLayout("insets 0, novisualpadding", "[][][][20px]")); - JScrollPane languageScrollPane = new JScrollPane(languagePanel).addTo(languagePanelRoot, "span, growx, wrap, hidemode 3"); - languageScrollPane.setBorder(new EmptyBorder(0, 0, 0, 0)); - languageScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - languageScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); - languageScrollPane.setVisible(false); - - AtomicInteger langId = new AtomicInteger(); - addLanguageSupplier = () -> { - int id = langId.getAndIncrement(); - MyComboBox cmbLanguage = - new MyComboBox<>(Language.values()).withToMessageStringRenderer(Language::getMsgCode).addTo(languagePanel); - MyTextFieldString txtLanguage = MyTextFieldString.builder().build().withColumns(20).addTo(languagePanel); - JButton btnDelete = new JButton(Messages.getString("StructureFilePanel.Delete")) - .withActionListenerSelf(delBtn -> { - languagePanel.remove(cmbLanguage); - languagePanel.remove(txtLanguage); - languagePanel.remove(delBtn); - languageMapping.remove(id); - languageScrollPane.setVisible(!languageMapping.isEmpty()); - languagePanelRoot.repaint(); - languagePanelRoot.revalidate(); - }) - .addTo(languagePanel, "wrap"); - LanguageComponents languageComponents = new LanguageComponents(cmbLanguage, txtLanguage, btnDelete); - languageMapping.put(id, languageComponents); - - languageScrollPane.setVisible(true); - languagePanelRoot.repaint(); - languagePanelRoot.revalidate(); - return languageComponents; - }; - new JButton(Messages.getString("StructureFilePanel.AddLanguage")) - .withActionListener(addLanguageSupplier::get).addTo(languagePanelRoot); - } + JPanel languagePanel = new JPanel(new MigLayout("insets 0, novisualpadding", "[][][][20px]")); + JScrollPane languageScrollPane = + new JScrollPane(languagePanel).addTo(languagePanelRoot, "span, growx, wrap, hidemode 3"); + languageScrollPane.setBorder(new EmptyBorder(0, 0, 0, 0)); + languageScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + languageScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + languageScrollPane.setVisible(false); + + AtomicInteger langId = new AtomicInteger(); + addLanguageSupplier = () -> { + int id = langId.getAndIncrement(); + JComboBox cmbLanguage = new JComboBox<>(Language.values()) + .toMessageStringRenderer(Language::getMsgCode).addTo(languagePanel); + MyTextFieldString txtLanguage = MyTextFieldString.builder().build().columns(20).addTo(languagePanel); + JButton btnDelete = new JButton(getText("StructureFilePanel.Delete")) + .actionListenerSelf(delBtn -> { + languagePanel.remove(cmbLanguage); + languagePanel.remove(txtLanguage); + languagePanel.remove(delBtn); + languageMapping.remove(id); + languageScrollPane.setVisible(!languageMapping.isEmpty()); + languagePanelRoot.repaint(); + languagePanelRoot.revalidate(); + }).addTo(languagePanel, "wrap"); + LanguageComponents languageComponents = new LanguageComponents(cmbLanguage, txtLanguage, btnDelete); + languageMapping.put(id, languageComponents); + + languageScrollPane.setVisible(true); + languagePanelRoot.repaint(); + languagePanelRoot.revalidate(); + return languageComponents; + }; + new JButton(getText("StructureFilePanel.AddLanguage")).actionListener( + addLanguageSupplier::get).addTo(languagePanelRoot); } loadPreferenceSettings(); } - private record LanguageComponents(MyComboBox cmbLanguage, MyTextFieldString txtLanguage, JButton btnDelete) { + private record LanguageComponents(JComboBox cmbLanguage, MyTextFieldString txtLanguage, + JButton btnDelete) { public void setValue(Language language, String langCode) { cmbLanguage.setSelectedItem(language); @@ -144,7 +139,7 @@ public boolean hasValidValue() { } Language getLanguage() { - return cmbLanguage.getSelectedItem(); + return cmbLanguage.getSelectedValue(); } } @@ -158,7 +153,7 @@ private Function getLibraryStructureBuilder() { return structure -> FilenameLibraryBuilder.builder() .structure(structure) .replaceSpace(chkReplaceSpace.isSelected()) - .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()) + .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedValue()) .includeLanguageCode(chkIncludeLanguageCode.isSelected()) .languageTags(languageMapping.toSettingsMap()) .useTvdbName(false) @@ -174,20 +169,18 @@ public void setEnabled(boolean enabled) { } public void loadPreferenceSettings() { - txtFileStructure.setText(librarySettings.getLibraryFilenameStructure()); - chkReplaceSpace.setSelected(librarySettings.isLibraryFilenameReplaceSpace()); - cbxReplaceSpaceChar.setSelectedItem(librarySettings.getLibraryFilenameReplacingSpaceChar()); - chkIncludeLanguageCode.setSelected(librarySettings.isLibraryIncludeLanguageCode()); - librarySettings.getLangCodeMap().forEach(this::addLanguage); + txtFileStructure.setText(librarySettings.libraryFilenameStructure); + chkReplaceSpace.setSelected(librarySettings.libraryFilenameReplaceSpace); + cbxReplaceSpaceChar.setSelectedItem(librarySettings.libraryFilenameReplacingSpaceChar); + chkIncludeLanguageCode.setSelected(librarySettings.libraryIncludeLanguageCode); + librarySettings.langCodeMap.forEach(this::addLanguage); } public void savePreferenceSettings() { - librarySettings - .setLibraryFilenameStructure(txtFileStructure.getText()) - .setLibraryFilenameReplaceSpace(chkReplaceSpace.isSelected()) - .setLibraryFilenameReplacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()) - .setLibraryIncludeLanguageCode(chkIncludeLanguageCode.isSelected()) - .setLangCodeMap(languageMapping.toSettingsMap()); + librarySettings.libraryFilenameStructure = txtFileStructure.getText(); + librarySettings.libraryFilenameReplaceSpace = chkReplaceSpace.isSelected(); + librarySettings.libraryFilenameReplacingSpaceChar = cbxReplaceSpaceChar.getSelectedValue(); + librarySettings.langCodeMap = languageMapping.toSettingsMap(); } private static class LanguageMapping { @@ -202,11 +195,11 @@ public void remove(int id) { public void put(int id, LanguageComponents languageComponents) { languageComponentsMap.put(id, languageComponents); - MyComboBox cmbLanguage = languageComponents.cmbLanguage(); + JComboBox cmbLanguage = languageComponents.cmbLanguage(); cmbLanguage.putClientProperty(DEFAULT_BORDER_PROPERTY, cmbLanguage.getBorder()); - cmbLanguage.withSelectedItemConsumer(this::updateBorder); + cmbLanguage.selectedItemConsumer(this::updateBorder); cmbLanguage.addItemListener(e -> updateBorder((Language) e.getItem())); - updateBorder(cmbLanguage.getSelectedItem()); + updateBorder(cmbLanguage.getSelectedValue()); } private void updateBorder(Language lang) { @@ -215,14 +208,14 @@ private void updateBorder(Language lang) { return; } - Border border = componentList.size() > 1 ? ERROR_BORDER : getDefaultBorder(componentList.get(0)); + Border border = componentList.size() > 1 ? ERROR_BORDER : getDefaultBorder(componentList.first); componentList.forEach(components -> components.cmbLanguage.setBorder(border)); } public boolean hasValidSettings() { - return languageComponentsMap.values().stream().allMatch(LanguageComponents::hasValidValue) - && languageComponentsMap.values().stream().map(LanguageComponents::getLanguage).distinct().count() == languageComponentsMap - .size(); + return languageComponentsMap.values().stream().allMatch(LanguageComponents::hasValidValue) && + languageComponentsMap.values().stream().map(LanguageComponents::getLanguage).distinct().count() == + languageComponentsMap.size(); } private Stream getLanguageComponentsForLanguageStream(Language language) { @@ -234,14 +227,18 @@ public Optional getLanguageComponentsForLanguage(Language la } public Map toSettingsMap() { - return languageComponentsMap.values().stream().collect(Collectors.toMap( - langComps -> langComps.cmbLanguage().getSelectedItem(), langComps -> langComps.txtLanguage().getText(), - (v1, v2) -> v1, LinkedHashMap::new)); + return languageComponentsMap.values() + .stream() + .collect(Collectors.toMap(langComps -> langComps.cmbLanguage().getSelectedValue(), + langComps -> langComps.txtLanguage().getText(), (v1, _) -> v1, LinkedHashMap::new)); } public void refreshState(boolean enabled) { if (enabled) { - languageComponentsMap.values().stream().map(langComp -> langComp.cmbLanguage.getSelectedItem()).distinct() + languageComponentsMap.values() + .stream() + .map(langComp -> langComp.cmbLanguage.getSelectedValue()) + .distinct() .forEach(this::updateBorder); } else { languageComponentsMap.values() @@ -259,8 +256,7 @@ public boolean isEmpty() { } public boolean hasValidSettings() { - return !isVisible() - || (txtFileStructure.hasValidValue() - && (!chkIncludeLanguageCode.isSelected() || languageMapping.hasValidSettings())); + return !isVisible() || (txtFileStructure.hasValidValue() && + (!chkIncludeLanguageCode.isSelected() || languageMapping.hasValidSettings())); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java index 7848cb42..4be98404 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java @@ -1,23 +1,16 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import javax.swing.*; import java.io.Serial; import java.util.function.Function; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; - -import org.lodder.subtools.multisubdownloader.Messages; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.dialog.StructureBuilderDialog; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldPath; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldString; import org.lodder.subtools.multisubdownloader.lib.library.PathLibraryBuilder; @@ -26,67 +19,63 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JTextFieldExtension.class, JComponentExtension.class, JCheckBoxExtension.class, AbstractButtonExtension.class }) public class StructureFolderPanel extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = 3476596236588408382L; - private final LibrarySettings librarySettings; + private final LibrarySettings librarySettings; private final MyTextFieldPath txtLibraryFolder; private final MyTextFieldString txtFolderStructure; private final JCheckBox chkRemoveEmptyFolder; private final JCheckBox chkReplaceSpace; - private final MyComboBox cbxReplaceSpaceChar; + private final JComboBox cbxReplaceSpaceChar; public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType, Manager manager, UserInteractionHandler userInteractionHandler) { super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titelPanel = TitlePanel.title(Messages.getString("PreferenceDialog.MoveToLibrary")) + JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.MoveToLibrary")) .margin(0).padding(0).marginLeft(20).paddingLeft(20).useGrid() .panelColumnConstraints("[shrink][grow][shrink]").addTo(this, "span, grow"); - { - new JLabel(Messages.getString("PreferenceDialog.Location")).addTo(titelPanel, "shrink"); - this.txtLibraryFolder = MyTextFieldPath.builder().requireValue().build().withColumns(20).addTo(titelPanel, "grow"); - new JButton(Messages.getString("App.Browse")) - .withActionListener(() -> MemoryFolderChooser.getInstance() - .selectDirectory(getRootPane(), Messages.getString("PreferenceDialog.LibraryFolder")) - .ifPresent(txtLibraryFolder::setObject)) - .addTo(titelPanel, "shrink, wrap"); - - new JLabel(Messages.getString("StructureBuilderDialog.Structure")).addTo(titelPanel, "shrink"); - this.txtFolderStructure = - MyTextFieldString.builder().requireValue().build().withColumns(20).withDisabled().addTo(titelPanel, "grow"); - JButton btnStructure = new JButton(Messages.getString("StructureBuilderDialog.Structure")) - .withActionListener(() -> { - StructureBuilderDialog sDialog = new StructureBuilderDialog(null, - Messages.getString("PreferenceDialog.StructureBuilderTitle"), - true, videoType, StructureBuilderDialog.StructureType.FOLDER, manager, userInteractionHandler, - getLibraryStructureBuilder()); - String value = sDialog.showDialog(txtFolderStructure.getText()); - if (!"".equals(value)) { - txtFolderStructure.setText(value); - } - - }) - .withDisabled() - .addTo(titelPanel, "shrink, wrap"); - - this.chkRemoveEmptyFolder = new JCheckBox(Messages.getString("PreferenceDialog.RemoveEmptyFolders")).addTo(titelPanel, "span, wrap"); - - PanelCheckBox.checkbox(this.chkReplaceSpace = new JCheckBox(Messages.getString("PreferenceDialog.ReplaceSpaceWith"))) - .panelOnSameLine().addTo(titelPanel, "span") - .addComponent(this.cbxReplaceSpaceChar = MyComboBox.ofValues('-', '.', '_')); - - // behaviour - txtLibraryFolder.withValidityChangedCallback(txtFolderStructure::setEnabled, btnStructure::setEnabled); - } + new JLabel(getText("PreferenceDialog.Location")).addTo(titlePanel, "shrink"); + this.txtLibraryFolder = + MyTextFieldPath.builder().requireValue().build().columns(20).addTo(titlePanel, "grow"); + new JButton(getText("App.Browse")) + .actionListener(() -> MemoryFolderChooser.getInstance() + .selectDirectory(getRootPane(), getText("PreferenceDialog.LibraryFolder")) + .ifPresent(txtLibraryFolder::setObject)) + .addTo(titlePanel, "shrink, wrap"); + + new JLabel(getText("StructureBuilderDialog.Structure")).addTo(titlePanel, "shrink"); + this.txtFolderStructure = + MyTextFieldString.builder().requireValue().build().columns(20).disabled().addTo(titlePanel, "grow"); + JButton btnStructure = new JButton(getText("StructureBuilderDialog.Structure")) + .actionListener(() -> { + StructureBuilderDialog sDialog = new StructureBuilderDialog(null, + getText("PreferenceDialog.StructureBuilderTitle"), + true, videoType, StructureBuilderDialog.StructureType.FOLDER, manager, + userInteractionHandler, getLibraryStructureBuilder()); + String value = sDialog.showDialog(txtFolderStructure.getText()); + if (!"".equals(value)) { + txtFolderStructure.setText(value); + } + }) + .disabled() + .addTo(titlePanel, "shrink, wrap"); + + this.chkRemoveEmptyFolder = new JCheckBox(getText("PreferenceDialog.RemoveEmptyFolders")) + .addTo(titlePanel, "span, wrap"); + + PanelCheckBox.checkbox(this.chkReplaceSpace = + new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith"))) + .panelOnSameLine().addTo(titlePanel, "span") + .addComponent(this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); + + // behaviour + txtLibraryFolder.withValidityChangedCallback(txtFolderStructure::setEnabled, btnStructure::setEnabled); loadPreferenceSettings(); } @@ -95,7 +84,7 @@ private Function getLibraryStructureBuilder() { return structure -> PathLibraryBuilder.builder() .structure(structure) .replaceSpace(chkReplaceSpace.isSelected()) - .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()) + .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedValue()) .useTvdbName(false) .tvdbAdapter(null) .libraryFolder(txtLibraryFolder.getObject()) @@ -111,22 +100,19 @@ public void setEnabled(boolean enabled) { } public void loadPreferenceSettings() { - txtLibraryFolder.setObject(librarySettings.getLibraryFolder()); - txtFolderStructure.setText(librarySettings.getLibraryFolderStructure()); - chkRemoveEmptyFolder.setSelected(librarySettings.isLibraryRemoveEmptyFolders()); - chkReplaceSpace.setSelected(librarySettings.isLibraryFolderReplaceSpace()); - cbxReplaceSpaceChar.setSelectedItem(librarySettings.getLibraryFolderReplacingSpaceChar()); + txtLibraryFolder.setObject(librarySettings.libraryFolder); + txtFolderStructure.setText(librarySettings.libraryFolderStructure); + chkRemoveEmptyFolder.setSelected(librarySettings.libraryRemoveEmptyFolders); + chkReplaceSpace.setSelected(librarySettings.libraryFolderReplaceSpace); + cbxReplaceSpaceChar.setSelectedItem(librarySettings.libraryFolderReplacingSpaceChar); } public void savePreferenceSettings() { - librarySettings - .setLibraryFolder(txtLibraryFolder.getObject()) - .setLibraryFolderStructure(txtFolderStructure.getText()) - .setLibraryRemoveEmptyFolders(chkRemoveEmptyFolder.isSelected()) - .setLibraryFolderReplaceSpace(chkReplaceSpace.isSelected()) - // if (pnlStructureFolder.isReplaceSpaceSelected()) { - .setLibraryFolderReplacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()); - // } + librarySettings.libraryFolder = txtLibraryFolder.getObject(); + librarySettings.libraryFolderStructure = txtFolderStructure.getText(); + librarySettings.libraryRemoveEmptyFolders = chkRemoveEmptyFolder.isSelected(); + librarySettings.libraryFolderReplaceSpace = chkReplaceSpace.isSelected(); + librarySettings.libraryFolderReplacingSpaceChar = cbxReplaceSpaceChar.getSelectedValue(); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructurePanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructurePanel.java index a96f3d32..5da8dedd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructurePanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructurePanel.java @@ -1,39 +1,29 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; -import java.io.Serial; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JPanel; - -import org.lodder.subtools.multisubdownloader.Messages; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; +import static manifold.ext.props.rt.api.PropOption.*; +import javax.swing.*; import java.awt.event.ActionListener; +import java.io.Serial; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.get; +import org.lodder.subtools.multisubdownloader.Messages; -@ExtensionMethod({ JCheckBoxExtension.class }) public abstract class StructurePanel> extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = 7507970016496546514L; - @Getter(value = AccessLevel.PROTECTED) - private final JButton btnBuildStructure; - @Getter(value = AccessLevel.PROTECTED) - private final JCheckBox chkReplaceSpace; - @Getter(value = AccessLevel.PROTECTED) - private final MyComboBox cbxReplaceSpaceChar; - public StructurePanel() { - this.btnBuildStructure = new JButton(Messages.getString("StructureBuilderDialog.Structure")); + @get(Protected) JButton btnBuildStructure; + @get(Protected) JCheckBox chkReplaceSpace; + @get(Protected) JComboBox cbxReplaceSpaceChar; + + StructurePanel() { + this.btnBuildStructure = new JButton(Messages.getText("StructureBuilderDialog.Structure")); - this.cbxReplaceSpaceChar = new MyComboBox<>(new String[] { "-", ".", "_" }); + this.cbxReplaceSpaceChar = JComboBox.create("-", ".", "_"); - this.chkReplaceSpace = new JCheckBox(Messages.getString("PreferenceDialog.ReplaceSpaceWith")) + this.chkReplaceSpace = new JCheckBox(Messages.getText("PreferenceDialog.ReplaceSpaceWith")) .addCheckedChangeListener(cbxReplaceSpaceChar::setEnabled); } @@ -44,19 +34,19 @@ public T addBuildStructureAction(ActionListener buildStructureAction) { } public String getReplaceSpaceChar() { - return this.getCbxReplaceSpaceChar().getSelectedItem(); + return this.cbxReplaceSpaceChar.getSelectedValue(); } public void setReplaceSpaceChar(String s) { - this.getCbxReplaceSpaceChar().setSelectedItem(s); + this.cbxReplaceSpaceChar.setSelectedItem(s); } public boolean isReplaceSpaceSelected() { - return this.getChkReplaceSpace().isSelected(); + return this.chkReplaceSpace.isSelected(); } public void setReplaceSpaceSelected(boolean b) { - this.getChkReplaceSpace().setSelected(b); + this.chkReplaceSpace.setSelected(b); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java index cf4982b6..27be7c52 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java @@ -1,34 +1,22 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; -import java.io.Serial; +import static org.lodder.subtools.multisubdownloader.Messages.*; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; +import javax.swing.*; +import java.io.Serial; -import org.lodder.subtools.multisubdownloader.Messages; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.button.AbstractButtonExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.JTextFieldExtension; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldPath; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ JCheckBoxExtension.class, JTextFieldExtension.class, JComponentExtension.class, AbstractButtonExtension.class }) public class SubtitleBackupPanel extends JPanel implements PreferencePanelIntf { - @Serial - private static final long serialVersionUID = -1498846730946617177L; + @Serial private static final long serialVersionUID = -1498846730946617177L; private final LibrarySettings librarySettings; - private final JCheckBox chkBackupSubtitle; private final MyTextFieldPath txtBackupSubtitlePath; private final JCheckBox chkBackupUseSourceFileName; @@ -37,38 +25,40 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { super(new MigLayout("insets 0, fillx, nogrid")); this.librarySettings = librarySettings; - JPanel titelPanel = TitlePanel.title(Messages.getString("PreferenceDialog.SubtitlesBackup")) + JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.SubtitlesBackup")) .margin(0).padding(0).paddingLeft(20).addTo(this, "span, growx"); { - this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().withColumns(20); + this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); - PanelCheckBox.checkbox(this.chkBackupSubtitle = new JCheckBox(Messages.getString("PreferenceDialog.BackupSubtitles"))) - .panelOnNewLine().addTo(titelPanel, "span, wrap, growx") - .addComponent("split 3, shrink", new JLabel(Messages.getString("PreferenceDialog.Location"))) + PanelCheckBox.checkbox(this.chkBackupSubtitle = new JCheckBox(getText("PreferenceDialog.BackupSubtitles"))) + .panelOnNewLine() + .addTo(titlePanel, "span, wrap, growx") + .addComponent("split 3, shrink", new JLabel(getText("PreferenceDialog.Location"))) .addComponent("growx", txtBackupSubtitlePath) - .addComponent("shrink", new JButton(Messages.getString("App.Browse")) - .withActionListener(l -> MemoryFolderChooser.getInstance() - .selectDirectory(this, Messages.getString("PreferenceDialog.SubtitleBackupFolder")) - .ifPresent(txtBackupSubtitlePath::setObject))); - - chkBackupUseSourceFileName = new JCheckBox(Messages.getString("PreferenceDialog.IncludeSourceInFileName")).addTo(titelPanel); + .addComponent("shrink", + new JButton(getText("App.Browse")) + .actionListener(_ -> MemoryFolderChooser.getInstance() + .selectDirectory(this, getText("PreferenceDialog.SubtitleBackupFolder")) + .ifPresent(txtBackupSubtitlePath::setObject))); + + chkBackupUseSourceFileName = + new JCheckBox(getText("PreferenceDialog.IncludeSourceInFileName")).addTo(titlePanel); } loadPreferenceSettings(); } public void loadPreferenceSettings() { - chkBackupSubtitle.setSelected(librarySettings.isLibraryBackupSubtitle()); - txtBackupSubtitlePath.setObject(librarySettings.getLibraryBackupSubtitlePath()); - chkBackupUseSourceFileName.setSelected(librarySettings.isLibraryBackupUseWebsiteFileName()); + chkBackupSubtitle.setSelected(librarySettings.libraryBackupSubtitle); + txtBackupSubtitlePath.setObject(librarySettings.libraryBackupSubtitlePath); + chkBackupUseSourceFileName.setSelected(librarySettings.libraryBackupUseWebsiteFileName); } public void savePreferenceSettings() { - librarySettings - .setLibraryBackupSubtitle(chkBackupSubtitle.isSelected()) - .setLibraryBackupSubtitlePath(txtBackupSubtitlePath.getObject()) - .setLibraryBackupUseWebsiteFileName(chkBackupUseSourceFileName.isSelected()); + librarySettings.libraryBackupSubtitle = chkBackupSubtitle.isSelected(); + librarySettings.libraryBackupSubtitlePath = txtBackupSubtitlePath.getObject(); + librarySettings.libraryBackupUseWebsiteFileName = chkBackupUseSourceFileName.isSelected(); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java index 6ca98434..1b4d08d0 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java @@ -1,18 +1,16 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import javax.swing.*; import java.io.Serial; import java.nio.file.Files; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; - -import org.lodder.subtools.multisubdownloader.Messages; +import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.val; +import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.PartialDisableComboBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcheckbox.JCheckBoxExtension; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcombobox.MyComboBox; -import org.lodder.subtools.multisubdownloader.gui.jcomponent.jcomponent.JComponentExtension; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; @@ -20,81 +18,79 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; -import net.miginfocom.swing.MigLayout; - -@ExtensionMethod({ Files.class, JComponentExtension.class, JCheckBoxExtension.class }) -public abstract class VideoLibraryPanel extends JPanel implements PreferencePanelIntf { - - @Serial - private static final long serialVersionUID = -9175813173306481849L; +@ExtensionMethod({ Files.class }) +public abstract sealed class VideoLibraryPanel extends JPanel implements PreferencePanelIntf + permits EpisodeLibraryPanel, MovieLibraryPanel { - @Getter - private final LibrarySettings librarySettings; + @Serial private static final long serialVersionUID = -9175813173306481849L; - protected final StructureFolderPanel pnlStructureFolder; - protected final StructureFilePanel pnlStructureFile; - private final MyComboBox cbxLibraryAction; + @val LibrarySettings librarySettings; + private final JComboBox cbxLibraryAction; private final JCheckBox chkUseTVDBNaming; private final PartialDisableComboBox cbxLibraryOtherFileAction; private final SubtitleBackupPanel pnlBackup; + protected final StructureFolderPanel pnlStructureFolder; + protected final StructureFilePanel pnlStructureFile; - public VideoLibraryPanel(LibrarySettings librarySettings, VideoType videoType, Manager manager, boolean renameMode, + VideoLibraryPanel(LibrarySettings librarySettings, VideoType videoType, Manager manager, boolean renameMode, UserInteractionHandler userInteractionHandler) { super(new MigLayout("fillx, nogrid")); this.librarySettings = librarySettings; this.pnlBackup = renameMode ? null : new SubtitleBackupPanel(librarySettings).addTo(this, "wrap, span, growx"); - JPanel performActionPanel = TitlePanel.title(Messages.getString("PreferenceDialog.PerformActions")) + JPanel performActionPanel = TitlePanel.title(getText("PreferenceDialog.PerformActions")) .margin(0).padding(0).paddingLeft(20).addTo(this, "span, growx"); { - this.chkUseTVDBNaming = new JCheckBox(Messages.getString("PreferenceDialog.UseTvdbName")) - .visible(VideoType.EPISODE == videoType) - .addTo(performActionPanel, "hidemode 3, wrap"); + this.chkUseTVDBNaming = new JCheckBox(getText("PreferenceDialog.UseTvdbName")).visible( + VideoType.EPISODE == videoType).addTo(performActionPanel, "hidemode 3, wrap"); - new JLabel(Messages.getString("PreferenceDialog.ActionForShowFiles")).addTo(performActionPanel); - this.cbxLibraryAction = new MyComboBox<>(LibraryActionType.values()) - .withToMessageStringRenderer(LibraryActionType::getMsgCode) + new JLabel(getText("PreferenceDialog.ActionForShowFiles")).addTo(performActionPanel); + this.cbxLibraryAction = new JComboBox<>(LibraryActionType.values()) + .toMessageStringRenderer(LibraryActionType::getMsgCode) .addTo(performActionPanel, "wrap"); - this.pnlStructureFolder = new StructureFolderPanel(librarySettings, videoType, manager, userInteractionHandler) - .addTo(performActionPanel, "hidemode 3, wrap, span, growx"); - this.pnlStructureFile = new StructureFilePanel(librarySettings, videoType, manager, userInteractionHandler) - .addTo(performActionPanel, "hidemode 3, wrap, span, growx"); + this.pnlStructureFolder = + new StructureFolderPanel(librarySettings, videoType, manager, userInteractionHandler) + .addTo(performActionPanel, "hidemode 3, wrap, span, growx"); + this.pnlStructureFile = + new StructureFilePanel(librarySettings, videoType, manager, userInteractionHandler) + .addTo(performActionPanel, "hidemode 3, wrap, span, growx"); - JLabel lblActionForOtherFiles = new JLabel(Messages.getString("PreferenceDialog.ActionForOtherFiles")).addTo(performActionPanel); - this.cbxLibraryOtherFileAction = PartialDisableComboBox.of(LibraryOtherFileActionType.values()).addTo(performActionPanel); + JLabel lblActionForOtherFiles = + new JLabel(getText("PreferenceDialog.ActionForOtherFiles")).addTo(performActionPanel); + this.cbxLibraryOtherFileAction = + PartialDisableComboBox.of(LibraryOtherFileActionType.values()).addTo(performActionPanel); // - this.cbxLibraryAction.withSelectedItemConsumer(action -> { + this.cbxLibraryAction.selectedItemConsumer(action -> { boolean enable = action != LibraryActionType.NOTHING; cbxLibraryOtherFileAction.setEnabled(enable); lblActionForOtherFiles.setEnabled(enable); }); } - this.cbxLibraryAction - .withItemListener(() -> { - checkEnableStatusPanel(); - checkPossibleOtherFileActions(); - if (!cbxLibraryOtherFileAction.isItemEnabled(cbxLibraryOtherFileAction.getSelectedIndex())) { - cbxLibraryOtherFileAction.setSelectedIndex(0); - } - }); + this.cbxLibraryAction.itemListener(() -> { + checkEnableStatusPanel(); + checkPossibleOtherFileActions(); + if (!cbxLibraryOtherFileAction.isItemEnabled(cbxLibraryOtherFileAction.getSelectedIndex())) { + cbxLibraryOtherFileAction.setSelectedIndex(0); + } + }); loadPreferenceSettings(); } private void checkPossibleOtherFileActions() { - LibraryActionType libraryActionType = cbxLibraryAction.getSelectedItem(); + LibraryActionType libraryActionType = cbxLibraryAction.getSelectedValue(); for (int i = 0; i < cbxLibraryOtherFileAction.getModel().getSize(); i++) { LibraryOtherFileActionType ofa = cbxLibraryOtherFileAction.getItemAt(i); boolean enabled = switch (libraryActionType) { - case MOVE -> LibraryOtherFileActionType.MOVEANDRENAME != ofa && LibraryOtherFileActionType.RENAME != ofa; - case RENAME -> LibraryOtherFileActionType.MOVEANDRENAME != ofa && LibraryOtherFileActionType.MOVE != ofa; + case MOVE -> + LibraryOtherFileActionType.MOVEANDRENAME != ofa && LibraryOtherFileActionType.RENAME != ofa; + case RENAME -> + LibraryOtherFileActionType.MOVEANDRENAME != ofa && LibraryOtherFileActionType.MOVE != ofa; case MOVEANDRENAME -> true; case NOTHING -> LibraryOtherFileActionType.NOTHING == ofa; }; @@ -103,7 +99,7 @@ private void checkPossibleOtherFileActions() { } private void checkEnableStatusPanel() { - LibraryActionType libraryActionType = cbxLibraryAction.getSelectedItem(); + LibraryActionType libraryActionType = cbxLibraryAction.getSelectedValue(); boolean pnlStructureFileVisible = switch (libraryActionType) { case MOVE, NOTHING -> false; case RENAME, MOVEANDRENAME -> true; @@ -122,9 +118,9 @@ private void checkEnableStatus(JPanel panel, boolean status) { } public void loadPreferenceSettings() { - cbxLibraryAction.setSelectedItem(librarySettings.getLibraryAction()); - chkUseTVDBNaming.setSelected(librarySettings.isLibraryUseTVDBNaming()); - cbxLibraryOtherFileAction.setSelectedItem(librarySettings.getLibraryOtherFileAction()); + cbxLibraryAction.setSelectedItem(librarySettings.libraryAction); + chkUseTVDBNaming.setSelected(librarySettings.libraryUseTVDBNaming); + cbxLibraryOtherFileAction.setSelectedItem(librarySettings.libraryOtherFileAction); checkEnableStatusPanel(); checkPossibleOtherFileActions(); @@ -134,9 +130,9 @@ public void savePreferenceSettings() { if (pnlBackup != null) { pnlBackup.savePreferenceSettings(); } - librarySettings.setLibraryAction(this.cbxLibraryAction.getSelectedItem()) - .setLibraryUseTVDBNaming(this.chkUseTVDBNaming.isSelected()) - .setLibraryOtherFileAction((LibraryOtherFileActionType) this.cbxLibraryOtherFileAction.getSelectedItem()); + librarySettings.libraryAction = this.cbxLibraryAction.getSelectedValue(); + librarySettings.libraryUseTVDBNaming = this.chkUseTVDBNaming.isSelected(); + librarySettings.libraryOtherFileAction = this.cbxLibraryOtherFileAction.getSelectedValue(); pnlStructureFolder.savePreferenceSettings(); pnlStructureFile.savePreferenceSettings(); @@ -144,7 +140,8 @@ public void savePreferenceSettings() { @Override public boolean hasValidSettings() { - return pnlStructureFolder.hasValidSettings() && pnlStructureFile.hasValidSettings() && (pnlBackup == null || pnlBackup.hasValidSettings()); + return pnlStructureFolder.hasValidSettings() && pnlStructureFile.hasValidSettings() && + (pnlBackup == null || pnlBackup.hasValidSettings()); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java index 76a2dbe5..33864619 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java @@ -23,18 +23,18 @@ import org.slf4j.LoggerFactory; /** - * Created by IntelliJ IDEA. User: lodder Date: 4/12/11 Time: 8:52 AM To change this template use - * Path | Settings | Path Templates. + * Created by IntelliJ IDEA. User: lodder Date: 4/12/11 Time: 8:52 AM To change this template use Path | Settings | Path + * Templates. */ public class DownloadWorker extends SwingWorker implements Cancelable { + private static final Logger LOGGER = LoggerFactory.getLogger(DownloadWorker.class); + private final CustomTable table; private final Settings settings; private final DownloadAction downloadAction; private final UserInteractionHandlerAction userInteractionHandlerAction; - private static final Logger LOGGER = LoggerFactory.getLogger(DownloadWorker.class); - public DownloadWorker(CustomTable table, Settings settings, Manager manager, GUI gui) { this.table = table; this.settings = settings; @@ -63,7 +63,7 @@ protected Void doInBackground() { progress = 1; } setProgress(progress); - publish(selectedShow.getFileName()); + publish(selectedShow.fileName); List selection = userInteractionHandlerAction.subtitleSelection(selectedShow, true); try { for (int j = 0; j < selection.size(); j++) { @@ -83,11 +83,12 @@ protected Void doInBackground() { @Override protected void process(List data) { - data.forEach(s -> StatusMessenger.instance.message(Messages.getString("MainWindow.DownloadingSubtitle", s))); + data.forEach(s -> StatusMessenger.instance.message(Messages.getText("MainWindow.DownloadingSubtitle", s))); } private void showErrorMessage(String message) { - JOptionPane.showConfirmDialog(null, message, "JBierSubDownloader", JOptionPane.CLOSED_OPTION, JOptionPane.ERROR_MESSAGE); + JOptionPane.showConfirmDialog(null, message, "JBierSubDownloader", JOptionPane.CLOSED_OPTION, + JOptionPane.ERROR_MESSAGE); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java index 536989e5..41ffc68d 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java @@ -1,9 +1,9 @@ package org.lodder.subtools.multisubdownloader.gui.workers; +import javax.swing.*; import java.util.List; -import javax.swing.SwingWorker; - +import lombok.RequiredArgsConstructor; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.RenameAction; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; @@ -13,16 +13,11 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import lombok.RequiredArgsConstructor; /** - * Created by IntelliJ IDEA. User: lodder Date: 4/12/11 Time: 8:52 AM To change this template use - * Path | Settings | Path Templates. + * Created by IntelliJ IDEA. User: lodder Date: 4/12/11 Time: 8:52 AM To change this template use Path | Settings | Path + * Templates. */ @RequiredArgsConstructor public class RenameWorker extends SwingWorker implements Cancelable { @@ -32,8 +27,6 @@ public class RenameWorker extends SwingWorker implements Cancelabl private final Manager manager; private final UserInteractionHandler userInteractionHandler; - private static final Logger LOGGER = LoggerFactory.getLogger(RenameWorker.class); - @Override protected Void doInBackground() { final VideoTableModel model = (VideoTableModel) table.getModel(); @@ -53,17 +46,11 @@ protected Void doInBackground() { } setProgress(progress); - RenameAction renameAction = null; - if (selectedShow.getVideoType() == VideoType.EPISODE) { - LOGGER.debug("Treat as EPISODE"); - renameAction = new RenameAction(settings.getEpisodeLibrarySettings(), manager, userInteractionHandler); - } else if (selectedShow.getVideoType() == VideoType.MOVIE) { - LOGGER.debug("Treat as MOVIE"); - renameAction = new RenameAction(settings.getMovieLibrarySettings(), manager, userInteractionHandler); - } - if (renameAction != null) { - renameAction.rename(selectedShow.getPath().resolve(selectedShow.getFileName()), selectedShow); - } + RenameAction renameAction = switch (selectedShow.videoType) { + case EPISODE -> new RenameAction(settings.episodeLibrarySettings, manager, userInteractionHandler); + case MOVIE -> new RenameAction(settings.movieLibrarySettings, manager, userInteractionHandler); + }; + renameAction.rename(selectedShow.getPath().resolve(selectedShow.fileName), selectedShow); model.removeShow(selectedShow); } }); @@ -72,6 +59,6 @@ protected Void doInBackground() { @Override protected void process(List data) { - data.forEach(s -> StatusMessenger.instance.message(Messages.getString("MainWindow.RenamingFile", s))); + data.forEach(s -> StatusMessenger.instance.message(Messages.getText("MainWindow.RenamingFile", s))); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java index be0af20f..2c5d5ee5 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java @@ -1,10 +1,12 @@ package org.lodder.subtools.multisubdownloader.lib; +import lombok.experimental.UtilityClass; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@UtilityClass public class Info { private static final Logger LOGGER = LoggerFactory.getLogger(Info.class); @@ -17,12 +19,12 @@ public static void subtitleSources(Settings settings, boolean isCli) { } for (SubtitleSource source : SubtitleSource.values()) { boolean enabled = switch (source) { - case ADDIC7ED -> settings.isSerieSourceAddic7ed(); - case LOCAL -> settings.isSerieSourceLocal(); - case OPENSUBTITLES -> settings.isSerieSourceOpensubtitles(); - case PODNAPISI -> settings.isSerieSourcePodnapisi(); - case TVSUBTITLES -> settings.isSerieSourceTvSubtitles(); - case SUBSCENE -> settings.isSerieSourceSubscene(); + case ADDIC7ED -> settings.serieSourceAddic7ed; + case LOCAL -> settings.serieSourceLocal; + case OPENSUBTITLES -> settings.serieSourceOpensubtitles; + case PODNAPISI -> settings.serieSourcePodnapisi; + case TVSUBTITLES -> settings.serieSourceTvSubtitles; + case SUBSCENE -> settings.serieSourceSubscene; }; if (isCli) { System.out.println(" - provider : " + source + " enabled: " + enabled); @@ -40,15 +42,16 @@ public static void subtitleSources(Settings settings, boolean isCli) { public static void subtitleFiltering(Settings settings, boolean isCli) { if (isCli) { System.out.println("----- Subtitle Filtering ------"); - System.out.println(" - OptionSubtitleExactMatch : " + settings.isOptionSubtitleExactMatch()); - System.out.println(" - OptionSubtitleKeywordMatch : " + settings.isOptionSubtitleKeywordMatch()); - System.out.println(" - OptionSubtitleExcludeHearingImpaired : " + settings.isOptionSubtitleExcludeHearingImpaired()); + System.out.println(" - OptionSubtitleExactMatch : " + settings.optionSubtitleExactMatch); + System.out.println(" - OptionSubtitleKeywordMatch : " + settings.optionSubtitleKeywordMatch); + System.out.println( + " - OptionSubtitleExcludeHearingImpaired : " + settings.optionSubtitleExcludeHearingImpaired); System.out.println("-------------------------------"); } else { LOGGER.info("----- Subtitle Filtering ------"); - LOGGER.info(" - OptionSubtitleExactMatch: {} ", settings.isOptionSubtitleExactMatch()); - LOGGER.info(" - OptionSubtitleKeywordMatch: {} ", settings.isOptionSubtitleKeywordMatch()); - LOGGER.info(" - OptionSubtitleExcludeHearingImpaired: {} ", settings.isOptionSubtitleExcludeHearingImpaired()); + LOGGER.info(" - OptionSubtitleExactMatch: {} ", settings.optionSubtitleExactMatch); + LOGGER.info(" - OptionSubtitleKeywordMatch: {} ", settings.optionSubtitleKeywordMatch); + LOGGER.info(" - OptionSubtitleExcludeHearingImpaired: {} ", settings.optionSubtitleExcludeHearingImpaired); LOGGER.info("-------------------------------"); } @@ -57,11 +60,11 @@ public static void subtitleFiltering(Settings settings, boolean isCli) { public static void downloadOptions(Settings settings, boolean isCli) { if (isCli) { System.out.println("----- Download Options ------"); - System.out.println(" - OptionsAlwaysConfirm : " + settings.isOptionsAlwaysConfirm()); + System.out.println(" - OptionsAlwaysConfirm : " + settings.optionsAlwaysConfirm); System.out.println("-----------------------------"); } else { LOGGER.info("----- Download Options ------"); - LOGGER.info(" - OptionsAlwaysConfirm : " + settings.isOptionsAlwaysConfirm()); + LOGGER.info(" - OptionsAlwaysConfirm : " + settings.optionsAlwaysConfirm); LOGGER.info("-----------------------------"); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java index e9c91679..c5da1670 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java @@ -19,12 +19,12 @@ public class ReleaseFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseFactory.class); + private final ReleaseParser releaseParser; private final Settings settings; private final Manager manager; - private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseFactory.class); - public ReleaseFactory(Settings settings, Manager manager) { this.releaseParser = new ReleaseParser(); this.settings = settings; @@ -33,13 +33,13 @@ public ReleaseFactory(Settings settings, Manager manager) { public Release createRelease(Path file, UserInteractionHandler userInteractionHandler) { try { - Release r = releaseParser.parse(file); - ReleaseControl releaseControl = switch (r.getVideoType()) { - case EPISODE -> new TvReleaseControl((TvRelease) r, settings, manager, userInteractionHandler); - case MOVIE -> new MovieReleaseControl((MovieRelease) r, settings, manager, userInteractionHandler); + ReleaseControl releaseControl = switch (releaseParser.parse(file)) { + case TvRelease tvRelease -> new TvReleaseControl(tvRelease, settings, manager, userInteractionHandler); + case MovieRelease movieRelease -> new MovieReleaseControl(movieRelease, settings, manager, + userInteractionHandler); }; releaseControl.process(); - return releaseControl.getVideoFile(); + return releaseControl.videoFile; } catch (ReleaseParseException | ReleaseControlException e) { LOGGER.error("createRelease: " + e.getMessage(), e); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java index b6b6f2c5..6929705e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java @@ -12,21 +12,18 @@ import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ OptionalExtension.class }) -public class MovieReleaseControl extends ReleaseControl { +public final class MovieReleaseControl extends ReleaseControl { private final ImdbAdapter imdbAdapter; private final OmdbAdapter omdbAdapter; private final MovieRelease movieRelease; private static final Logger LOGGER = LoggerFactory.getLogger(MovieReleaseControl.class); - public MovieReleaseControl(MovieRelease movieRelease, Settings settings, Manager manager, UserInteractionHandler userInteractionHandler) { + public MovieReleaseControl(MovieRelease movieRelease, Settings settings, Manager manager, + UserInteractionHandler userInteractionHandler) { super(settings, manager); this.movieRelease = movieRelease; this.imdbAdapter = ImdbAdapter.getInstance(manager, userInteractionHandler); @@ -35,22 +32,23 @@ public MovieReleaseControl(MovieRelease movieRelease, Settings settings, Manager @Override public void process() throws ReleaseControlException { - if (StringUtils.isBlank(movieRelease.getName())) { + if (StringUtils.isBlank(movieRelease.name)) { throw new ReleaseControlException("Unable to extract/find title, check file", movieRelease); } else { - int imdbId = imdbAdapter.getImdbId(movieRelease.getName(), movieRelease.getYear()) - .orElseThrow(() -> new ReleaseControlException("Movie not found on IMDB, check file", movieRelease)); - movieRelease.setImdbId(imdbId); + movieRelease.setImdbId(imdbAdapter.getImdbId(movieRelease.name, movieRelease.year) + .orElseThrow( + () -> new ReleaseControlException("Movie not found on IMDB, check file", movieRelease))); Optional movieDetails = movieRelease.getImdbId().mapToObj(imdbAdapter::getMovieDetails).orElseGet(Optional::empty); if (movieDetails.isEmpty()) { - movieDetails = movieRelease.getImdbId().mapToObj(omdbAdapter::getMovieDetails).orElseGet(Optional::empty); + movieDetails = + movieRelease.getImdbId().mapToObj(omdbAdapter::getMovieDetails).orElseGet(Optional::empty); } - movieDetails.ifPresentDo(info -> { - movieRelease.setYear(info.year()); - movieRelease.setName(info.getName()); - }).ifEmptyDo(() -> LOGGER.error("Unable to get details from OMDB API, continue with filename info {}", movieRelease)); + movieDetails.ifPresentOrElse(info -> { + movieRelease.year = info.year; + movieRelease.name = info.name; + }, () -> LOGGER.error("Unable to get details from OMDB API, continue with filename info $movieRelease")); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java index 89a84073..394df25e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java @@ -1,22 +1,25 @@ package org.lodder.subtools.multisubdownloader.lib.control; +import static manifold.ext.props.rt.api.PropOption.*; + +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.get; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.exception.ReleaseControlException; import org.lodder.subtools.sublibrary.model.Release; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter(value = AccessLevel.PROTECTED) @AllArgsConstructor -public abstract class ReleaseControl { +public abstract sealed class ReleaseControl permits MovieReleaseControl, TvReleaseControl { - private final Settings settings; - private final Manager manager; + @get(Protected) Settings settings; + @get(Protected) Manager manager; + @get abstract Release videoFile; - public abstract void process() throws ReleaseControlException; + ReleaseControl(Settings settings, Manager manager) { + this.settings = settings; + this.manager = manager; + } - public abstract Release getVideoFile(); + public abstract void process() throws ReleaseControlException; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java index 620dee1a..830f38e9 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.lib.control; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.settings.model.SettingsProcessEpisodeSource; @@ -9,36 +11,33 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.experimental.ExtensionMethod; +public final class TvReleaseControl extends ReleaseControl { -@ExtensionMethod({ OptionalExtension.class }) -public class TvReleaseControl extends ReleaseControl { + private static final Logger LOGGER = LoggerFactory.getLogger(TvReleaseControl.class); private final TheTvdbAdapter jtvdba; private final TvRelease tvRelease; + @val @override Release videoFile; - private static final Logger LOGGER = LoggerFactory.getLogger(TvReleaseControl.class); - - public TvReleaseControl(TvRelease tvRelease, Settings settings, Manager manager, UserInteractionHandler userInteractionHandler) { + public TvReleaseControl(TvRelease tvRelease, Settings settings, Manager manager, + UserInteractionHandler userInteractionHandler) { super(settings, manager); this.tvRelease = tvRelease; + this.videoFile = tvRelease; this.jtvdba = TheTvdbAdapter.getInstance(manager, userInteractionHandler); } @Override public void process() throws ReleaseControlException { - // return episodeFile; - if (StringUtils.isBlank(tvRelease.getName())) { + if (StringUtils.isBlank(tvRelease.name)) { throw new ReleaseControlException("Unable to extract episode details, check file", tvRelease); } else { - LOGGER.debug("process: show name [{}], season [{}], episode [{}]", tvRelease.getName(), - tvRelease.getSeason(), tvRelease.getEpisodeNumbers()); - - if (tvRelease.isSpecial()) { + LOGGER.debug("process: show name [{}], season [{}], episode [{}]", tvRelease.name, tvRelease.season, + tvRelease.episodeNumbers); + if (tvRelease.special) { processSpecial(); } else { processTvdb(); @@ -47,30 +46,25 @@ public void process() throws ReleaseControlException { } private void processTvdb() throws ReleaseControlException { - jtvdba.getSerie(tvRelease.getName()).ifPresentOrThrow(tvdbSerie -> { - tvRelease.setTvdbId(tvdbSerie.getId()); - tvRelease.setOriginalName(tvdbSerie.getSerieName()); - jtvdba.getEpisode(tvdbSerie.getId(), tvRelease.getSeason(), tvRelease.getEpisodeNumbers().get(0)) - .ifPresentOrThrow( - tvRelease::updateTvdbEpisodeInfo, - () -> new ReleaseControlException("Season %s Episode %s not found, check file".formatted(tvRelease.getSeason(), - tvRelease.getEpisodeNumbers().toString()), tvRelease)); - }, () -> new ReleaseControlException("Show not found, check file", tvRelease)); + jtvdba.getSerie(tvRelease.name).useIfPresent(tvdbSerie -> { + tvRelease.tvdbId = tvdbSerie.id; + tvRelease.originalName = tvdbSerie.serieName; + jtvdba.getEpisode(tvdbSerie.id, tvRelease.season, tvRelease.firstEpisodeNumber) + .useIfPresent(tvRelease::updateTvdbEpisodeInfo) + .orElseThrow(() -> new ReleaseControlException( + "Season ${tvRelease.season} Episode ${tvRelease.episodeNumbers} not found, check file", + tvRelease)); + }).orElseThrow(() -> new ReleaseControlException("Show not found, check file", tvRelease)); } private void processSpecial() throws ReleaseControlException { - jtvdba.getSerie(tvRelease.getName()).ifPresentOrThrow(tvdbSerie -> { - tvRelease.setTvdbId(tvdbSerie.getId()); - tvRelease.setOriginalName(tvdbSerie.getSerieName()); - if (getSettings().getProcessEpisodeSource() == SettingsProcessEpisodeSource.TVDB) { - jtvdba.getEpisode(tvdbSerie.getId(), tvRelease.getSeason(), tvRelease.getEpisodeNumbers().get(0)) + jtvdba.getSerie(tvRelease.name).useIfPresent(tvdbSerie -> { + tvRelease.tvdbId = tvdbSerie.id; + tvRelease.originalName = tvdbSerie.serieName; + if (settings.processEpisodeSource == SettingsProcessEpisodeSource.TVDB) { + jtvdba.getEpisode(tvdbSerie.id, tvRelease.season, tvRelease.firstEpisodeNumber) .ifPresent(tvRelease::updateTvdbEpisodeInfo); } - }, () -> new ReleaseControlException("Show not found, check file", tvRelease)); - } - - @Override - public Release getVideoFile() { - return tvRelease; + }).orElseThrow(() -> new ReleaseControlException("Show not found, check file", tvRelease)); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java index e851496d..3418cbed 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.ExactNameFilter; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.KeywordFilter; -import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.ReleasegroupFilter; +import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.ReleaseGroupFilter; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.SubtitleFilter; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.model.Release; @@ -15,7 +15,7 @@ public class SubtitleFiltering { private final Settings settings; private final SubtitleFilter exactName = new ExactNameFilter(); private final SubtitleFilter keyword = new KeywordFilter(); - private final SubtitleFilter releaseGroup = new ReleasegroupFilter(); + private final SubtitleFilter releaseGroup = new ReleaseGroupFilter(); public boolean useSubtitle(Subtitle subtitle, Release release) { return !excludeSubtitle(subtitle, release); @@ -23,20 +23,20 @@ public boolean useSubtitle(Subtitle subtitle, Release release) { public boolean excludeSubtitle(Subtitle subtitle, Release release) { return excludeSubtitleHearingImpaired(subtitle, release) - || excludeSubtitleKeywordMatch(subtitle, release) - || excludeSubtitleExactMatch(subtitle, release); + || excludeSubtitleKeywordMatch(subtitle, release) + || excludeSubtitleExactMatch(subtitle, release); } private boolean excludeSubtitleHearingImpaired(Subtitle subtitle, Release release) { - return settings.isOptionSubtitleExcludeHearingImpaired() && subtitle.isHearingImpaired(); + return settings.optionSubtitleExcludeHearingImpaired && subtitle.hearingImpaired; } private boolean excludeSubtitleKeywordMatch(Subtitle subtitle, Release release) { - return settings.isOptionSubtitleKeywordMatch() && - (keyword.excludeSubtitle(release, subtitle) || releaseGroup.excludeSubtitle(release, subtitle)); + return settings.optionSubtitleKeywordMatch && + (keyword.excludeSubtitle(release, subtitle) || releaseGroup.excludeSubtitle(release, subtitle)); } private boolean excludeSubtitleExactMatch(Subtitle subtitle, Release release) { - return settings.isOptionSubtitleExactMatch() && exactName.excludeSubtitle(release, subtitle); + return settings.optionSubtitleExactMatch && exactName.excludeSubtitle(release, subtitle); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java index 04af46a9..1bb408fe 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java @@ -10,7 +10,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ExactNameFilter extends SubtitleFilter { +public final class ExactNameFilter extends SubtitleFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ExactNameFilter.class); @@ -18,11 +18,11 @@ public class ExactNameFilter extends SubtitleFilter { @Override public boolean useSubtitle(Release release, Subtitle subtitle) { - Pattern p = patterns.computeIfAbsent(getReleaseName(release), k -> + Pattern p = patterns.computeIfAbsent(getReleaseName(release), _ -> Pattern.compile(getReleaseName(release).replace(" ", "[. ]"), Pattern.CASE_INSENSITIVE)); - if (p.matcher(subtitle.getFileName().toLowerCase().replace(".srt", "")).matches()) { - LOGGER.debug("getSubtitlesFiltered: found EXACT match [{}] ", subtitle.getFileName()); - subtitle.setSubtitleMatchType(SubtitleMatchType.EXACT); + if (p.matcher(subtitle.fileName.toLowerCase().replace(".srt", "")).matches()) { + LOGGER.debug("getSubtitlesFiltered: found EXACT match [{}] ", subtitle.fileName); + subtitle.subtitleMatchType = SubtitleMatchType.EXACT; return true; } return false; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java index e0136721..c7469d94 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java @@ -7,24 +7,23 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class KeywordFilter extends SubtitleFilter { +public final class KeywordFilter extends SubtitleFilter { private static final Logger LOGGER = LoggerFactory.getLogger(KeywordFilter.class); - @Override public boolean useSubtitle(Release release, Subtitle subtitle) { String keywordsFile = ReleaseParser.getQualityKeyword(getReleaseName(release)); - if (subtitle.getQuality().isEmpty()) { - subtitle.setQuality(ReleaseParser.getQualityKeyword(subtitle.getFileName())); + if (subtitle.quality.isEmpty()) { + subtitle.quality = ReleaseParser.getQualityKeyword(subtitle.fileName); } - if(!checkKeywordSubtitleMatch(subtitle, keywordsFile)){ + if (!checkKeywordSubtitleMatch(subtitle, keywordsFile)) { return false; } - LOGGER.debug("getSubtitlesFiltered: found KEYWORD match [{}] ", subtitle.getFileName()); - subtitle.setSubtitleMatchType(SubtitleMatchType.KEYWORD); - return true; + LOGGER.debug("getSubtitlesFiltered: found KEYWORD match [{}] ", subtitle.fileName); + subtitle.subtitleMatchType = SubtitleMatchType.KEYWORD; + return true; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleasegroupFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java similarity index 54% rename from MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleasegroupFilter.java rename to MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java index efc3f00e..ea4cc496 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleasegroupFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java @@ -1,6 +1,5 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters; -import lombok.experimental.ExtensionMethod; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; @@ -9,21 +8,21 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({StringUtils.class}) -public class ReleasegroupFilter extends SubtitleFilter { +public final class ReleaseGroupFilter extends SubtitleFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(ReleasegroupFilter.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseGroupFilter.class); @Override public boolean useSubtitle(Release release, Subtitle subtitle) { - if (subtitle.getReleaseGroup().isEmpty()) { - subtitle.setReleaseGroup(ReleaseParser.extractReleasegroup(subtitle.getFileName(), subtitle.getFileName().endsWith(".srt"))); + if (subtitle.releaseGroup.isEmpty()) { + subtitle.releaseGroup = + ReleaseParser.extractReleaseGroup(subtitle.fileName, subtitle.fileName.endsWith(".srt")); } - if(!StringUtils.containsAnyIgnoreCase(subtitle.getReleaseGroup(), release.getReleaseGroup(), subtitle.getReleaseGroup())){ + if (!StringUtils.containsAnyIgnoreCase(subtitle.releaseGroup, release.releaseGroup, subtitle.releaseGroup)) { return false; } - LOGGER.debug("getSubtitlesFiltered: found KEYWORD based TEAM match [{}] ", subtitle.getFileName()); - subtitle.setSubtitleMatchType(SubtitleMatchType.TEAM); + LOGGER.debug("getSubtitlesFiltered: found KEYWORD based TEAM match [{}] ", subtitle.fileName); + subtitle.subtitleMatchType = SubtitleMatchType.TEAM; return true; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java index b951626a..c0a474c2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java @@ -1,25 +1,23 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters; -import java.util.Arrays; - import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -public abstract class SubtitleFilter { +public abstract sealed class SubtitleFilter permits ExactNameFilter, KeywordFilter, ReleaseGroupFilter { public abstract boolean useSubtitle(Release release, Subtitle subtitle); - public boolean excludeSubtitle(Release release, Subtitle subtitle){ + public boolean excludeSubtitle(Release release, Subtitle subtitle) { return !useSubtitle(release, subtitle); } protected String getReleaseName(Release release) { - return release.getFileName() == null ? "" : release.getFileName().toLowerCase().replace("." + release.getExtension(), ""); + return release.fileName == null ? "" : release.fileName.toLowerCase().replace("." + release.extension, ""); } protected boolean checkKeywordSubtitleMatch(Subtitle subtitle, String keywordsFile) { - String keywordsSub = ReleaseParser.getQualityKeyword(subtitle.getFileName()); + String keywordsSub = ReleaseParser.getQualityKeyword(subtitle.fileName); return keywordsFile.equalsIgnoreCase(keywordsSub) || keywordCheck(keywordsFile, keywordsSub); } @@ -54,6 +52,6 @@ private boolean keywordCheck(String videoFileName, String subFileName) { } private boolean containsBoth(String string1, String string2, String... values) { - return Arrays.stream(values).allMatch(string1::contains) && Arrays.stream(values).allMatch(string2::contains); + return values.stream().allMatch(string1::contains) && values.stream().allMatch(string2::contains); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java index c50555e7..9eb701c9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java @@ -11,13 +11,13 @@ public ScoreCalculator(SortWeight weights) { } public int calculate(Subtitle subtitle) { - if (weights.getMaxScore() <= 0) { + if (weights.maxScore <= 0) { return 0; } - - String subtitleInfo = "%s %s %s".formatted(subtitle.getFileName(), subtitle.getQuality(), subtitle.getReleaseGroup()).trim().toLowerCase(); - - int score = weights.getWeights().keySet().stream().filter(subtitleInfo::contains).mapToInt(weights.getWeights()::get).sum(); - return (int) Math.ceil((float) score / weights.getMaxScore() * 100); + String subtitleInfo = + "%s %s %s".formatted(subtitle.fileName, subtitle.quality, subtitle.releaseGroup).trim().toLowerCase(); + int score = + weights.weights.keySet().stream().filter(subtitleInfo::contains).mapToInt(weights.weights::get).sum(); + return (int) Math.ceil((float) score / weights.maxScore * 100); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java index fc732bc0..e0b9929d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java @@ -1,23 +1,25 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting; +import static manifold.ext.props.rt.api.PropOption.*; + import java.util.HashMap; import java.util.List; import java.util.Map; -import lombok.Getter; +import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.set; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.replacers.GroupReplacer; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.replacers.KeywordReplacer; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; -@Getter public class SortWeight { private static final List KEYWORD_REPLACERS = List.of(new GroupReplacer()); - private Map weights; - protected int maxScore; + @get Map weights = new HashMap<>(); + @get @set(Private) int maxScore; public SortWeight(Release release, Map defaultWeights) { this.setWeights(release, defaultWeights); @@ -25,7 +27,7 @@ public SortWeight(Release release, Map defaultWeights) { protected void setWeights(Release release, Map defaultWeights) { this.maxScore = 0; - this.weights = new HashMap<>(); + this.weights.clear(); /* make a clone, so we can't mess up the defined weights */ defaultWeights = new HashMap<>(defaultWeights); // clone @@ -33,9 +35,9 @@ protected void setWeights(Release release, Map defaultWeights) replaceReservedKeywords(release, defaultWeights); /* get a list of tags */ - List tags = ReleaseParser.getQualityKeyWords(release.getQuality()); - if (StringUtils.isNotBlank(release.getReleaseGroup())) { - tags.add(release.getReleaseGroup().toLowerCase()); + List tags = ReleaseParser.getQualityKeyWords(release.quality); + if (StringUtils.isNotBlank(release.releaseGroup)) { + tags.add(release.releaseGroup.toLowerCase()); } /* only store tags for which we have a weight defined */ diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java index 09ad5f68..a83f0b39 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java @@ -14,6 +14,6 @@ public class SubtitleComparator implements Comparator, Serializable { @Override public int compare(Subtitle a, Subtitle b) { /* inverse sorting */ - return Integer.compare(b.getScore(), a.getScore()); + return Integer.compare(b.score, a.score); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java index cf46a3ef..be7a412b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java @@ -19,7 +19,7 @@ public void replace(Release release, Map weights) { weights.remove(reservedKey); /* add replaced value */ - String group = StringUtils.lowerCase(release.getReleaseGroup()); + String group = StringUtils.lowerCase(release.releaseGroup); weights.put(group, weight); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java index 9414e3e4..27f54212 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java @@ -1,11 +1,14 @@ package org.lodder.subtools.multisubdownloader.lib.library; +import static org.lodder.subtools.multisubdownloader.settings.model.structure.SerieStructureTag.*; + import java.nio.file.Path; import java.util.Map; +import lombok.Setter; +import lombok.experimental.Accessors; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; -import org.lodder.subtools.multisubdownloader.settings.model.structure.SerieStructureTag; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.tvdb.TheTvdbAdapter; @@ -13,10 +16,6 @@ import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.StringUtil; - -import lombok.Setter; -import lombok.experimental.Accessors; public class FilenameLibraryBuilder extends LibraryBuilder { @@ -27,8 +26,9 @@ public class FilenameLibraryBuilder extends LibraryBuilder { private final Map languageTags; private final boolean rename; - private FilenameLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, boolean includeLanguageCode, - Map languageTags, boolean useTvdb, TheTvdbAdapter tvdbAdapter, boolean rename) { + private FilenameLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, + boolean includeLanguageCode, Map languageTags, boolean useTvdb, + TheTvdbAdapter tvdbAdapter, boolean rename) { super(useTvdb, tvdbAdapter); this.structure = structure; this.replaceSpace = replaceSpace; @@ -41,12 +41,12 @@ private FilenameLibraryBuilder(String structure, boolean replaceSpace, char repl public static FilenameLibraryBuilder fromSettings(LibrarySettings librarySettings, Manager manager, UserInteractionHandler userInteractionHandler) { return FilenameLibraryBuilder.builder() - .structure(librarySettings.getLibraryFolderStructure()) - .replaceSpace(librarySettings.isLibraryFolderReplaceSpace()) - .replacingSpaceChar(librarySettings.getLibraryFolderReplacingSpaceChar()) - .includeLanguageCode(librarySettings.isLibraryIncludeLanguageCode()) - .languageTags(librarySettings.getLangCodeMap()) - .useTvdbName(librarySettings.isLibraryUseTVDBNaming()) + .structure(librarySettings.libraryFolderStructure) + .replaceSpace(librarySettings.libraryFolderReplaceSpace) + .replacingSpaceChar(librarySettings.libraryFolderReplacingSpaceChar) + .includeLanguageCode(librarySettings.libraryIncludeLanguageCode) + .languageTags(librarySettings.langCodeMap) + .useTvdbName(librarySettings.libraryUseTVDBNaming) .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) .rename(librarySettings.hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)) .build(); @@ -94,16 +94,11 @@ public interface FilenameLibraryBuilderBuildIntf { @Setter @Accessors(chain = true, fluent = true) - public static class FilenameLibraryBuilderBuilder implements - FilenameLibraryBuilderStructureIntf, - FilenameLibraryBuilderReplaceSpaceIntf, - FilenameLibraryBuilderReplaceSpaceCharIntf, - FilenameLibraryBuilderIncludeLanguageCodeIntf, - FilenameLibraryBuilderLanguageTagIntf, - FilenameLibraryBuilderUseTvdbNameIntf, - FilenameLibraryBuilderTvdbAdapterIntf, - FilenameLibraryBuilderRenameIntf, - FilenameLibraryBuilderBuildIntf { + public static class FilenameLibraryBuilderBuilder + implements FilenameLibraryBuilderStructureIntf, FilenameLibraryBuilderReplaceSpaceIntf, + FilenameLibraryBuilderReplaceSpaceCharIntf, FilenameLibraryBuilderIncludeLanguageCodeIntf, + FilenameLibraryBuilderLanguageTagIntf, FilenameLibraryBuilderUseTvdbNameIntf, + FilenameLibraryBuilderTvdbAdapterIntf, FilenameLibraryBuilderRenameIntf, FilenameLibraryBuilderBuildIntf { private String structure; private boolean replaceSpace; @@ -119,60 +114,61 @@ public static class FilenameLibraryBuilderBuilder implements @Override public FilenameLibraryBuilder build() { - return new FilenameLibraryBuilder(structure, replaceSpace, replacingSpaceChar, includeLanguageCode, languageTags, useTvdbName, - tvdbAdapter, rename); + return new FilenameLibraryBuilder(structure, replaceSpace, replacingSpaceChar, includeLanguageCode, + languageTags, useTvdbName, tvdbAdapter, rename); } } @Override public Path build(Release release) { if (rename) { - String filename; - if (release instanceof TvRelease tvRelease && StringUtils.isNotBlank(structure)) { - filename = structure; - // order is important! - filename = replace(filename, SerieStructureTag.SHOW_NAME, getShowName(tvRelease.getName())); - filename = replaceFormattedEpisodeNumber(filename, SerieStructureTag.EPISODES_LONG, tvRelease.getEpisodeNumbers(), true); - filename = replaceFormattedEpisodeNumber(filename, SerieStructureTag.EPISODES_SHORT, tvRelease.getEpisodeNumbers(), false); - filename = replace(filename, SerieStructureTag.SEASON_LONG, formattedNumber(tvRelease.getSeason(), true)); - filename = replace(filename, SerieStructureTag.SEASON_SHORT, formattedNumber(tvRelease.getSeason(), false)); - filename = replace(filename, SerieStructureTag.EPISODE_LONG, formattedNumber(tvRelease.getEpisodeNumbers().get(0), true)); - filename = replace(filename, SerieStructureTag.EPISODE_SHORT, formattedNumber(tvRelease.getEpisodeNumbers().get(0), false)); - filename = replace(filename, SerieStructureTag.TITLE, tvRelease.getTitle()); - filename = replace(filename, SerieStructureTag.QUALITY, release.getQuality()); - filename = replace(filename, SerieStructureTag.DESCRIPTION, release.getDescription()); - - filename += "." + release.getExtension(); - } else { - filename = release.getFileName(); - } - filename = StringUtil.removeIllegalWindowsChars(filename); + String filename = switch (release) { + case TvRelease tvRelease when StringUtils.isNotBlank(structure) -> { + String fName = structure; + // order is important! + fName = replace(fName, SHOW_NAME, getShowName(tvRelease.name)); + fName = replaceFormattedEpisodeNumber(fName, EPISODES_LONG, tvRelease.episodeNumbers, true); + fName = replaceFormattedEpisodeNumber(fName, EPISODES_SHORT, tvRelease.episodeNumbers, false); + fName = replace(fName, SEASON_LONG, formattedNumber(tvRelease.season, true)); + fName = replace(fName, SEASON_SHORT, formattedNumber(tvRelease.season, false)); + fName = replace(fName, EPISODE_LONG, formattedNumber(tvRelease.firstEpisodeNumber, true)); + fName = replace(fName, EPISODE_SHORT, formattedNumber(tvRelease.firstEpisodeNumber, false)); + fName = replace(fName, TITLE, tvRelease.title); + fName = replace(fName, QUALITY, release.quality); + fName = replace(fName, DESCRIPTION, release.description); + + fName += "." + release.extension; + yield fName; + } + default -> release.fileName; + }; + filename = filename.removeIllegalWindowsChars(); if (replaceSpace) { filename = filename.replace(' ', replacingSpaceChar); } return Path.of(filename); } else { - return Path.of(release.getFileName()); + return Path.of(release.fileName); } } public String buildSubtitle(Release release, Subtitle sub, String filename, Integer version) { - return buildSubtitle(release, filename, sub.getLanguage(), version); + return buildSubtitle(release, filename, sub.language, version); } public String buildSubtitle(Release release, String filename, Language language, Integer version) { - final String extension = "." + release.getExtension(); + final String extension = "." + release.extension; if (version != null) { - filename = filename.substring(0, filename.indexOf(extension)) + "-v" + version + "." + release.getExtension(); + filename = filename.substring(0, filename.indexOf(extension)) + "-v$version.${release.extension}"; } if (includeLanguageCode) { - String langCode = language == null ? "" : languageTags.getOrDefault(language, language.getLangCode()); - filename = changeExtension(filename, !"".equals(langCode) ? ".%s.srt".formatted(langCode) : ".srt"); + String langCode = language == null ? "" : languageTags.getOrDefault(language, language.langCode); + filename = changeExtension(filename, !"".equals(langCode) ? ".$langCode.srt" : ".srt"); } else { filename = changeExtension(filename, ".srt"); } - filename = StringUtil.removeIllegalWindowsChars(filename); + filename = filename.removeIllegalWindowsChars(); if (replaceSpace) { filename = filename.replace(' ', replacingSpaceChar); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java index 2d7bff39..22e877ef 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java @@ -1,24 +1,21 @@ package org.lodder.subtools.multisubdownloader.lib.library; -import java.util.Arrays; - import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum LibraryActionType { NOTHING("PreferenceDialog.Action.Nothing"), RENAME("PreferenceDialog.Action.Rename"), MOVE("PreferenceDialog.Action.Move"), MOVEANDRENAME("PreferenceDialog.Action.MoveAndRename"); - @Getter - private final String msgCode; + @val String msgCode; @Deprecated(since = "Settings version 2") public static LibraryActionType fromString(String description) { - return Arrays.stream(LibraryActionType.values()) + return LibraryActionType.values().stream() .filter(v -> description.equalsIgnoreCase(v.toString())).findAny() .orElse(LibraryActionType.NOTHING); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java index f0ccd5f9..6105d83c 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java @@ -4,18 +4,14 @@ import java.util.List; import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.settings.model.structure.StructureTag; import org.lodder.subtools.sublibrary.data.tvdb.TheTvdbAdapter; import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbSerie; import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.util.OptionalExtension; - -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; @RequiredArgsConstructor -@ExtensionMethod({ OptionalExtension.class, StringUtils.class }) public abstract class LibraryBuilder { private final boolean useTvdb; @@ -28,20 +24,21 @@ protected String getShowName(String name) { } protected String replace(String structure, StructureTag tag, String value) { - return structure.replace(tag.getLabel(), value); + return structure.replace(tag.label, value); } - protected String replaceFormattedEpisodeNumber(String structure, StructureTag tag, List episodeNumbers, boolean leadingZero) { - if (structure.contains(tag.getLabel())) { - String afterLabel = structure.substringAfter(tag.getLabel()); - String separator = afterLabel.isNotEmpty() ? afterLabel.substring(0, 1) : ""; + protected String replaceFormattedEpisodeNumber(String structure, StructureTag tag, List episodeNumbers, + boolean leadingZero) { + if (structure.contains(tag.label)) { + String afterLabel = StringUtils.substringAfter(structure, tag.label); + String separator = StringUtils.isNotEmpty(afterLabel) ? afterLabel.substring(0, 1) : ""; if ("%".equals(separator)) { separator = ""; } String formattedEpisodeNumber = episodeNumbers.stream() .map(episode -> formattedNumber(episode, leadingZero)) .collect(Collectors.joining(separator)); - return structure.replace(tag.getLabel(), formattedEpisodeNumber); + return structure.replace(tag.label, formattedEpisodeNumber); } return structure; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java index 8a8410a0..9dcbe428 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java @@ -1,11 +1,8 @@ package org.lodder.subtools.multisubdownloader.lib.library; -import java.util.Arrays; - -import org.lodder.subtools.multisubdownloader.Messages; - import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import org.lodder.subtools.multisubdownloader.Messages; @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public enum LibraryOtherFileActionType { @@ -20,12 +17,12 @@ public enum LibraryOtherFileActionType { @Override public String toString() { - return Messages.getString(msgCode); + return Messages.getText(msgCode); } @Deprecated(since = "Settings version 2") public static LibraryOtherFileActionType fromString(String description) { - return Arrays.stream(LibraryOtherFileActionType.values()) + return LibraryOtherFileActionType.values().stream() .filter(v -> description.equalsIgnoreCase(v.toString())).findAny() .orElse(LibraryOtherFileActionType.NOTHING); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java index 5aa2b9fc..f8f264ff 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java @@ -3,6 +3,8 @@ import java.nio.file.Path; import java.nio.file.Paths; +import lombok.Setter; +import lombok.experimental.Accessors; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.multisubdownloader.settings.model.structure.FolderStructureTag; import org.lodder.subtools.multisubdownloader.settings.model.structure.MovieStructureTag; @@ -13,10 +15,6 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.StringUtil; - -import lombok.Setter; -import lombok.experimental.Accessors; public class PathLibraryBuilder extends LibraryBuilder { @@ -26,8 +24,8 @@ public class PathLibraryBuilder extends LibraryBuilder { private final Path libraryFolder; private final boolean move; - private PathLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, boolean useTvdb, TheTvdbAdapter tvdbAdapter, - Path libraryFolder, boolean move) { + private PathLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, boolean useTvdb, + TheTvdbAdapter tvdbAdapter, Path libraryFolder, boolean move) { super(useTvdb, tvdbAdapter); this.structure = structure; this.replaceSpace = replaceSpace; @@ -36,14 +34,15 @@ private PathLibraryBuilder(String structure, boolean replaceSpace, char replacin this.move = move; } - public static PathLibraryBuilder fromSettings(LibrarySettings librarySettings, Manager manager, UserInteractionHandler userInteractionHandler) { + public static PathLibraryBuilder fromSettings(LibrarySettings librarySettings, Manager manager, + UserInteractionHandler userInteractionHandler) { return PathLibraryBuilder.builder() - .structure(librarySettings.getLibraryFolderStructure()) - .replaceSpace(librarySettings.isLibraryFolderReplaceSpace()) - .replacingSpaceChar(librarySettings.getLibraryFolderReplacingSpaceChar()) - .useTvdbName(librarySettings.isLibraryUseTVDBNaming()) + .structure(librarySettings.libraryFolderStructure) + .replaceSpace(librarySettings.libraryFolderReplaceSpace) + .replacingSpaceChar(librarySettings.libraryFolderReplacingSpaceChar) + .useTvdbName(librarySettings.libraryUseTVDBNaming) .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) - .libraryFolder(librarySettings.getLibraryFolder()) + .libraryFolder(librarySettings.libraryFolder) .move(librarySettings.hasAnyLibraryAction(LibraryActionType.MOVE, LibraryActionType.MOVEANDRENAME)) .build(); } @@ -86,14 +85,10 @@ public interface PathLibraryBuilderBuildIntf { @Setter @Accessors(chain = true, fluent = true) - public static class PathLibraryBuilderBuilder implements - PathLibraryBuilderStructureIntf, - PathLibraryBuilderReplaceSpaceIntf, - PathLibraryBuilderReplaceSpaceCharIntf, - PathLibraryBuilderUseTvdbNameIntf, - PathLibraryBuilderTvdbAdapterIntf, - PathLibraryBuilderLibraryFolderIntf, - PathLibraryBuilderMoveIntf, + public static class PathLibraryBuilderBuilder + implements PathLibraryBuilderStructureIntf, PathLibraryBuilderReplaceSpaceIntf, + PathLibraryBuilderReplaceSpaceCharIntf, PathLibraryBuilderUseTvdbNameIntf, + PathLibraryBuilderTvdbAdapterIntf, PathLibraryBuilderLibraryFolderIntf, PathLibraryBuilderMoveIntf, PathLibraryBuilderBuildIntf { private String structure; @@ -109,21 +104,18 @@ public static class PathLibraryBuilderBuilder implements @Override public PathLibraryBuilder build() { - return new PathLibraryBuilder(structure, replaceSpace, replacingSpaceChar, useTvdbName, tvdbAdapter, libraryFolder, move); + return new PathLibraryBuilder(structure, replaceSpace, replacingSpaceChar, useTvdbName, tvdbAdapter, + libraryFolder, move); } } @Override public Path build(Release release) { if (move) { - Path subpath; - if (release instanceof TvRelease tvRelease) { - subpath = buildEpisode(tvRelease); - } else if (release instanceof MovieRelease movieRelease) { - subpath = buildMovie(movieRelease); - } else { - subpath = Path.of(""); - } + Path subpath = switch (release) { + case TvRelease tvRelease -> buildEpisode(tvRelease); + case MovieRelease movieRelease -> buildMovie(movieRelease); + }; return libraryFolder.resolve(subpath); } else { return release.getPath(); @@ -133,34 +125,36 @@ public Path build(Release release) { private Path buildEpisode(TvRelease tvRelease) { String folder = structure; - folder = folder.replace(SerieStructureTag.SHOW_NAME.getLabel(), StringUtil.removeIllegalWindowsChars(getShowName(tvRelease.getName()))); + folder = folder.replace(SerieStructureTag.SHOW_NAME.label, getShowName(tvRelease.name)) + .removeIllegalWindowsChars(); // order is important! - folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_LONG, tvRelease.getEpisodeNumbers(), true); - folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_SHORT, tvRelease.getEpisodeNumbers(), false); - folder = replace(folder, SerieStructureTag.SEASON_LONG, formattedNumber(tvRelease.getSeason(), true)); - folder = replace(folder, SerieStructureTag.SEASON_SHORT, formattedNumber(tvRelease.getSeason(), false)); - folder = replace(folder, SerieStructureTag.EPISODE_LONG, formattedNumber(tvRelease.getEpisodeNumbers().get(0), true)); - folder = replace(folder, SerieStructureTag.EPISODE_SHORT, formattedNumber(tvRelease.getEpisodeNumbers().get(0), false)); - folder = replace(folder, SerieStructureTag.TITLE, tvRelease.getTitle()); - folder = replace(folder, SerieStructureTag.QUALITY, tvRelease.getQuality()); - folder = replace(folder, SerieStructureTag.DESCRIPTION, tvRelease.getDescription()); + folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_LONG, tvRelease.episodeNumbers, true); + folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_SHORT, tvRelease.episodeNumbers, + false); + folder = replace(folder, SerieStructureTag.SEASON_LONG, formattedNumber(tvRelease.season, true)); + folder = replace(folder, SerieStructureTag.SEASON_SHORT, formattedNumber(tvRelease.season, false)); + folder = replace(folder, SerieStructureTag.EPISODE_LONG, formattedNumber(tvRelease.firstEpisodeNumber, true)); + folder = replace(folder, SerieStructureTag.EPISODE_SHORT, formattedNumber(tvRelease.firstEpisodeNumber, false)); + folder = replace(folder, SerieStructureTag.TITLE, tvRelease.title); + folder = replace(folder, SerieStructureTag.QUALITY, tvRelease.quality); + folder = replace(folder, SerieStructureTag.DESCRIPTION, tvRelease.description); if (replaceSpace) { folder = folder.replace(' ', replacingSpaceChar); } folder = folder.trim(); - return Paths.get("", folder.split(FolderStructureTag.SEPARATOR.getLabel())); + return Paths.get("", folder.split(FolderStructureTag.SEPARATOR.label)); } private Path buildMovie(MovieRelease movieRelease) { String folder = structure; - folder = replace(folder, MovieStructureTag.MOVIE_TITLE, StringUtil.removeIllegalWindowsChars(movieRelease.getName())); - folder = replace(folder, MovieStructureTag.YEAR, Integer.toString(movieRelease.getYear())); - folder = replace(folder, MovieStructureTag.QUALITY, movieRelease.getQuality()); + folder = replace(folder, MovieStructureTag.MOVIE_TITLE, movieRelease.name.removeIllegalWindowsChars()); + folder = replace(folder, MovieStructureTag.YEAR, Integer.toString(movieRelease.year)); + folder = replace(folder, MovieStructureTag.QUALITY, movieRelease.quality); if (replaceSpace) { folder = folder.replace(' ', replacingSpaceChar); } folder = folder.trim(); - return Paths.get("", folder.split(FolderStructureTag.SEPARATOR.getLabel())); + return Paths.get("", folder.split(FolderStructureTag.SEPARATOR.label)); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java index 1ad0d64c..5d48f1a5 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java @@ -2,6 +2,8 @@ import java.util.prefs.Preferences; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; @@ -18,12 +20,8 @@ public class Addic7edServiceProvider implements ServiceProvider { protected Container app; protected SubtitleProvider subtitleProvider; - - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { @@ -50,27 +48,29 @@ private SubtitleProvider createProvider(UserInteractionHandler userInteractionHa boolean loginEnabled = false; String username = ""; String password = ""; - if (settings.isLoginAddic7edEnabled()) { - username = StringUtils.trim(settings.getLoginAddic7edUsername()); - password = StringUtils.trim(settings.getLoginAddic7edPassword()); + if (settings.loginAddic7edEnabled) { + username = StringUtils.trim(settings.loginAddic7edUsername); + password = StringUtils.trim(settings.loginAddic7edPassword); /* Protect against empty login */ loginEnabled = !username.isEmpty() && !password.isEmpty(); } - if (settings.isSerieSourceAddic7edProxy()) { + if (settings.serieSourceAddic7edProxy) { return new JAddic7edViaProxyAdapter(manager, userInteractionHandler); } else { - return new JAddic7edAdapter(loginEnabled, username, password, preferences.getBoolean("speedy", false), manager, userInteractionHandler); + return new JAddic7edAdapter(loginEnabled, username, password, preferences.getBoolean("speedy", false), + manager, userInteractionHandler); } } // TODO is this still needed? - private void registerListener(SubtitleProviderStore subtitleProviderStore, UserInteractionHandler userInteractionHandler) { + private void registerListener(SubtitleProviderStore subtitleProviderStore, + UserInteractionHandler userInteractionHandler) { /* Resolve the EventEmitter from the IoC Container */ Emitter emitter = (Emitter) app.make("EventEmitter"); /* Listen for settings-change */ - emitter.listen("providers.settings.change", event -> { + emitter.listen("providers.settings.change", _ -> { /* Change occurred, delete outdated provider from store */ subtitleProviderStore.deleteProvider(subtitleProvider); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java index d72dd431..02d95932 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; @@ -12,15 +14,11 @@ public class LocalServiceProvider implements ServiceProvider { + private UserInteractionHandler userInteractionHandler; protected Container app; protected SubtitleProvider subtitleProvider; - private UserInteractionHandler userInteractionHandler; - - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java index 535722a6..1a0cfd2e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; @@ -15,12 +17,8 @@ public class OpenSubtitlesServiceProvider implements ServiceProvider { protected Container app; protected SubtitleProvider subtitleProvider; - - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { @@ -46,16 +44,17 @@ private SubtitleProvider createProvider(UserInteractionHandler userInteractionHa boolean loginEnabled = false; String username = ""; String password = ""; - if (settings.isLoginOpenSubtitlesEnabled()) { - username = StringUtils.trim(settings.getLoginOpenSubtitlesUsername()); - password = StringUtils.trim(settings.getLoginOpenSubtitlesPassword()); + if (settings.loginOpenSubtitlesEnabled) { + username = StringUtils.trim(settings.loginOpenSubtitlesUsername); + password = StringUtils.trim(settings.loginOpenSubtitlesPassword); /* Protect against empty login */ loginEnabled = !username.isEmpty() && !password.isEmpty(); } return new JOpenSubAdapter(loginEnabled, username, password, manager, userInteractionHandler); } - private void registerListener(SubtitleProviderStore subtitleProviderStore, UserInteractionHandler userInteractionHandler) { + private void registerListener(SubtitleProviderStore subtitleProviderStore, + UserInteractionHandler userInteractionHandler) { /* Resolve the EventEmitter from the IoC Container */ Emitter emitter = (Emitter) app.make("EventEmitter"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java index d64e1261..a7cf74c9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; @@ -7,12 +9,10 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JPodnapisiAdapter; import org.lodder.subtools.sublibrary.Manager; + public class PodnapisiServiceProvider implements ServiceProvider { - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java index bfd8e652..43f78d2c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; @@ -12,12 +14,8 @@ public class SubsceneServiceProvider implements ServiceProvider { protected Container app; protected SubtitleProvider subtitleProvider; - - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java index 8af65377..68ae2a7d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; @@ -8,10 +10,7 @@ public class SubtitleServiceProvider implements ServiceProvider { - @Override - public int getPriority() { - return 0; - } + @val @override int priority = 0; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java index 67c3edd1..bc676af5 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; @@ -9,11 +11,9 @@ import org.lodder.subtools.sublibrary.Manager; public class TvSubtitlesServiceProvider implements ServiceProvider { - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java index 80ca9b49..acefe126 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java @@ -1,7 +1,6 @@ package org.lodder.subtools.multisubdownloader.settings; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -13,6 +12,7 @@ import com.google.common.base.CaseFormat; import com.google.common.base.Objects; +import extensions.java.nio.file.Path.PathExt; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -29,7 +29,6 @@ import org.lodder.subtools.multisubdownloader.settings.model.UpdateType; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.control.VideoPatterns; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.lodder.subtools.sublibrary.util.TriConsumer; public enum SettingValue { @@ -144,32 +143,32 @@ public enum SettingValue { // SCREEN SETTINGS SCREEN_HIDE_EPISODE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().screenSettings) .valueGetter(ScreenSettings::isHideEpisode) .valueSetter(ScreenSettings::setHideEpisode) .defaultValue(true)), SCREEN_HIDE_FILENAME(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().screenSettings) .valueGetter(ScreenSettings::isHideFilename) .valueSetter(ScreenSettings::setHideFilename) .defaultValue(false)), SCREEN_HIDE_SEASON(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().screenSettings) .valueGetter(ScreenSettings::isHideSeason) .valueSetter(ScreenSettings::setHideSeason) .defaultValue(true)), SCREEN_HIDE_TITLE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().screenSettings) .valueGetter(ScreenSettings::isHideTitle) .valueSetter(ScreenSettings::setHideTitle) .defaultValue(true)), SCREEN_HIDE_TYPE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().screenSettings) .valueGetter(ScreenSettings::isHideType) .valueSetter(ScreenSettings::setHideType) .defaultValue(true)), SCREEN_HIDE_W_I_P(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().screenSettings) .valueGetter(ScreenSettings::isHideWIP) .valueSetter(ScreenSettings::setHideWIP) .defaultValue(true)), @@ -193,77 +192,77 @@ public enum SettingValue { // LIBRARY SERIE EPISODE_LIBRARY_BACKUP_SUBTITLE_PATH(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryBackupSubtitlePath) .valueSetter(LibrarySettings::setLibraryBackupSubtitlePath) .defaultValue(null)), EPISODE_LIBRARY_BACKUP_SUBTITLE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryBackupSubtitle) .valueSetter(LibrarySettings::setLibraryBackupSubtitle) .defaultValue(false)), EPISODE_LIBRARY_BACKUP_USE_WEBSITE_FILE_NAME(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryBackupUseWebsiteFileName) .valueSetter(LibrarySettings::setLibraryBackupUseWebsiteFileName) .defaultValue(false)), EPISODE_LIBRARY_ACTION(createSettingEnum(LibraryActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryAction) .valueSetter(LibrarySettings::setLibraryAction) .defaultValue(LibraryActionType.NOTHING)), EPISODE_LIBRARY_USE_T_V_D_B_NAMING(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryUseTVDBNaming) .valueSetter(LibrarySettings::setLibraryUseTVDBNaming) .defaultValue(false)), EPISODE_LIBRARY_OTHER_FILE_ACTION(createSettingEnum(LibraryOtherFileActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryOtherFileAction) .valueSetter(LibrarySettings::setLibraryOtherFileAction) .defaultValue(LibraryOtherFileActionType.NOTHING)), EPISODE_LIBRARY_FOLDER(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryFolder) .valueSetter(LibrarySettings::setLibraryFolder) .defaultValue(null)), EPISODE_LIBRARY_FOLDER_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryFolderStructure) .valueSetter(LibrarySettings::setLibraryFolderStructure) .defaultValue("")), EPISODE_LIBRARY_REMOVE_EMPTY_FOLDERS(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryRemoveEmptyFolders) .valueSetter(LibrarySettings::setLibraryRemoveEmptyFolders) .defaultValue(false)), EPISODE_LIBRARY_FILENAME_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryFilenameStructure) .valueSetter(LibrarySettings::setLibraryFilenameStructure) .defaultValue("")), EPISODE_LIBRARY_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryFilenameReplaceSpace) .valueSetter(LibrarySettings::setLibraryFilenameReplaceSpace) .defaultValue(false)), EPISODE_LIBRARY_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryFilenameReplacingSpaceChar) .valueSetter(LibrarySettings::setLibraryFilenameReplacingSpaceChar) .defaultValue('_')), EPISODE_LIBRARY_FOLDER_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryFolderReplaceSpace) .valueSetter(LibrarySettings::setLibraryFolderReplaceSpace) .defaultValue(false)), EPISODE_LIBRARY_FOLDER_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::getLibraryFolderReplacingSpaceChar) .valueSetter(LibrarySettings::setLibraryFolderReplacingSpaceChar) .defaultValue('_')), EPISODE_LIBRARY_INCLUDE_LANGUAGE_CODE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .valueGetter(LibrarySettings::isLibraryIncludeLanguageCode) .valueSetter(LibrarySettings::setLibraryIncludeLanguageCode) .defaultValue(false)), @@ -272,82 +271,82 @@ public enum SettingValue { .toObjectMapperKey(Language::valueOf) .toStringMapperValue(Function.identity()) .toObjectMapperValue(Function.identity()) - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().episodeLibrarySettings) .mapGetter(LibrarySettings::getLangCodeMap)), // LIBRARY MOVIE MOVIE_LIBRARY_BACKUP_SUBTITLE_PATH(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryBackupSubtitlePath) .valueSetter(LibrarySettings::setLibraryBackupSubtitlePath) .defaultValue(null)), MOVIE_LIBRARY_BACKUP_SUBTITLE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryBackupSubtitle) .valueSetter(LibrarySettings::setLibraryBackupSubtitle) .defaultValue(false)), MOVIE_LIBRARY_BACKUP_USE_WEBSITE_FILE_NAME(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryBackupUseWebsiteFileName) .valueSetter(LibrarySettings::setLibraryBackupUseWebsiteFileName) .defaultValue(false)), MOVIE_LIBRARY_ACTION(createSettingEnum(LibraryActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryAction) .valueSetter(LibrarySettings::setLibraryAction) .defaultValue(LibraryActionType.NOTHING)), MOVIE_LIBRARY_USE_T_V_D_B_NAMING(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryUseTVDBNaming) .valueSetter(LibrarySettings::setLibraryUseTVDBNaming) .defaultValue(false)), MOVIE_LIBRARY_OTHER_FILE_ACTION(createSettingEnum(LibraryOtherFileActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryOtherFileAction) .valueSetter(LibrarySettings::setLibraryOtherFileAction) .defaultValue(LibraryOtherFileActionType.NOTHING)), MOVIE_LIBRARY_FOLDER(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryFolder) .valueSetter(LibrarySettings::setLibraryFolder) .defaultValue(null)), MOVIE_LIBRARY_FOLDER_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryFolderStructure) .valueSetter(LibrarySettings::setLibraryFolderStructure) .defaultValue("")), MOVIE_LIBRARY_REMOVE_EMPTY_FOLDERS(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryRemoveEmptyFolders) .valueSetter(LibrarySettings::setLibraryRemoveEmptyFolders) .defaultValue(false)), MOVIE_LIBRARY_FILENAME_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryFilenameStructure) .valueSetter(LibrarySettings::setLibraryFilenameStructure) .defaultValue("")), MOVIE_LIBRARY_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryFilenameReplaceSpace) .valueSetter(LibrarySettings::setLibraryFilenameReplaceSpace) .defaultValue(false)), MOVIE_LIBRARY_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryFilenameReplacingSpaceChar) .valueSetter(LibrarySettings::setLibraryFilenameReplacingSpaceChar) .defaultValue('_')), MOVIE_LIBRARY_FOLDER_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryFolderReplaceSpace) .valueSetter(LibrarySettings::setLibraryFolderReplaceSpace) .defaultValue(false)), MOVIE_LIBRARY_FOLDER_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::getLibraryFolderReplacingSpaceChar) .valueSetter(LibrarySettings::setLibraryFolderReplacingSpaceChar) .defaultValue('_')), MOVIE_LIBRARY_INCLUDE_LANGUAGE_CODE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .valueGetter(LibrarySettings::isLibraryIncludeLanguageCode) .valueSetter(LibrarySettings::setLibraryIncludeLanguageCode) .defaultValue(false)), @@ -356,7 +355,7 @@ public enum SettingValue { .toObjectMapperKey(Language::valueOf) .toStringMapperValue(Function.identity()) .toObjectMapperValue(Function.identity()) - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) + .rootElementFunction(sCtr -> sCtr.getSettings().movieLibrarySettings) .mapGetter(LibrarySettings::getLangCodeMap)), // SERIE SOURCE SETTINGS @@ -448,7 +447,7 @@ public void load(SettingsControl settingsControl, Preferences preferences) { } public static void loadAll(SettingsControl settingsControl, Preferences preferences) { - Arrays.stream(SettingValue.values()).forEach(sv -> sv.load(settingsControl, preferences)); + SettingValue.values().forEach(sv -> sv.load(settingsControl, preferences)); } @@ -484,7 +483,7 @@ private static SettingTypedRootElementFunctionIntf createSettingBoolean private static SettingTypedRootElementFunctionIntf createSettingPath() { return createSetting(Path.class) - .toStringMapper(FileUtils::toAbsolutePathAsString) + .toStringMapper(PathExt::toAbsolutePathAsString) .toObjectMapper(Path::of); } @@ -509,7 +508,8 @@ private interface SettingTypedToObjectMapperIntf { } private interface SettingTypedPreferenceGetterIntf { - SettingTypedRootElementFunctionIntf preferencesGetter(TriFunction preferencesGetter); + SettingTypedRootElementFunctionIntf preferencesGetter( + TriFunction preferencesGetter); } private interface SettingTypedRootElementFunctionIntf { @@ -619,13 +619,15 @@ public SettingIntf build(String key) { case SINGLE_VALUE -> { super.storeValueFunction((settingsControl, preferences) -> { T value = valueGetter.apply(getRootElement(settingsControl)); - if (!Objects.equal(value, getDefaultValue()) && !(value instanceof String text && "".equals(text))) { + if (!Objects.equal(value, getDefaultValue()) && + !(value instanceof String text && "".equals(text))) { preferencesSetter.accept(preferences, key, value); } }); - super.loadValueFunction((settingsControl, preferences) -> valueSetter.accept(getRootElement(settingsControl), - preferencesGetter.apply(preferences, key, getDefaultValue()))); + super.loadValueFunction( + (settingsControl, preferences) -> valueSetter.accept(getRootElement(settingsControl), + preferencesGetter.apply(preferences, key, getDefaultValue()))); } case COLLECTION -> { super.storeValueFunction((settingsControl, preferences) -> { @@ -641,7 +643,8 @@ public SettingIntf build(String key) { R rootElement = getRootElement(settingsControl); listCleaner.accept(rootElement); IntStream.range(0, numberOfItems) - .forEach(i -> valueAdder.accept(rootElement, toObjectMapper.apply(preferences.get(key + i, "")))); + .forEach(i -> valueAdder.accept(rootElement, + toObjectMapper.apply(preferences.get(key + i, "")))); }); } default -> throw new IllegalArgumentException("Unexpected value: " + settingType); @@ -756,11 +759,11 @@ public SettingIntf build(String key) { }; this.loadValueFunction = (settingsControl, preferences) -> { int numberOfItems = preferences.getInt(key + "Size", 0); - IntStream.range(0, numberOfItems).forEach(idx -> { - valueAdder.accept(getRootElement(settingsControl), - toObjectMapperKey.apply(preferences.get(getKeyString(key, idx), "")), - toObjectMapperValue.apply(preferences.get(getValueString(key, idx), ""))); - }); + IntStream.range(0, numberOfItems).forEach(idx -> + valueAdder.accept(getRootElement(settingsControl), + toObjectMapperKey.apply(preferences.get(getKeyString(key, idx), "")), + toObjectMapperValue.apply(preferences.get(getValueString(key, idx), ""))) + ); }; return this; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java index 96507d9b..2efaa447 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.settings; +import static manifold.ext.props.rt.api.PropOption.*; import static org.lodder.subtools.multisubdownloader.settings.SettingValue.*; import java.io.BufferedInputStream; @@ -8,13 +9,15 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.List; import java.util.prefs.BackingStoreException; import java.util.prefs.InvalidPreferencesFormatException; import java.util.prefs.Preferences; import java.util.stream.IntStream; +import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.lodder.subtools.multisubdownloader.gui.dialog.MappingEpisodeNameDialog.MappingType; @@ -34,20 +37,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - @ExtensionMethod({ Files.class }) public class SettingsControl { + private static final Logger LOGGER = LoggerFactory.getLogger(SettingsControl.class); + private static final String BACKING_STORE_AVAIL = "BackingStoreAvail"; + private final Manager manager; private final Preferences preferences; - @Getter - private Settings settings; - @Getter - private State state; - private static final String BACKING_STORE_AVAIL = "BackingStoreAvail"; - private static final Logger LOGGER = LoggerFactory.getLogger(SettingsControl.class); + @get @set(Private) Settings settings; + @get @set(Private) State state; public SettingsControl(Manager manager) { if (!backingStoreAvailable()) { @@ -77,7 +76,7 @@ public void store() { try { // clean up preferences.clear(); - Arrays.stream(SettingValue.values()).forEach(sv -> sv.store(this, preferences)); + SettingValue.values().forEach(sv -> sv.store(this, preferences)); updateProxySettings(); } catch (BackingStoreException e) { LOGGER.error(e.getMessage(), e); @@ -98,7 +97,8 @@ public void exportPreferences(Path file) throws IOException, BackingStoreExcepti } } - public void importPreferences(Path file) throws IOException, BackingStoreException, InvalidPreferencesFormatException { + public void importPreferences(Path file) + throws IOException, BackingStoreException, InvalidPreferencesFormatException { try (InputStream is = new BufferedInputStream(file.newInputStream())) { preferences.clear(); Preferences.importPreferences(is); @@ -107,10 +107,10 @@ public void importPreferences(Path file) throws IOException, BackingStoreExcepti } public void updateProxySettings() { - if (settings.isGeneralProxyEnabled()) { + if (settings.generalProxyEnabled) { System.getProperties().put("proxySet", "true"); - System.getProperties().put("proxyHost", settings.getGeneralProxyHost()); - System.getProperties().put("proxyPort", settings.getGeneralProxyPort()); + System.getProperties().put("proxyHost", settings.generalProxyHost); + System.getProperties().put("proxyPort", settings.generalProxyPort); } else { System.getProperties().put("proxySet", "false"); } @@ -121,7 +121,7 @@ public void updateProxySettings() { */ private void migrateSettings() { SettingValue.loadAll(this, preferences); - int version = settings.getSettingsVersion(); + int version = settings.settingsVersion; if (version == 0) { migrateSettingsV0ToV1(); settings = new Settings(); @@ -153,7 +153,8 @@ private void migrateSettings() { public void migrateSettingsV0ToV1() { preferences.putInt("GeneralDefaultIncomingFolderSize", preferences.getInt("lastDefaultIncomingFolder", 0)); - preferences.putInt("LocalSubtitlesSourcesFoldersSize", preferences.getInt("lastLocalSubtitlesSourcesFolder", 0)); + preferences.putInt("LocalSubtitlesSourcesFoldersSize", + preferences.getInt("lastLocalSubtitlesSourcesFolder", 0)); preferences.putInt("GeneralDefaultIncomingFolderSize", preferences.getInt("lastDefaultIncomingFolder", 0)); preferences.putInt("DefaultSelectionQualitySize", preferences.getInt("lastItemDefaultSelectionQuality", 0)); preferences.putInt("DefaultSelectionQualitySize", preferences.getInt("lastItemDefaultSelectionQuality", 0)); @@ -164,7 +165,7 @@ public void migrateSettingsV0ToV1() { // int lastItemExclude = preferences.getInt("lastItemExclude", 0); // if (lastItemExclude > 0) { - // List excludeList = settings.getExcludeList(); + // List excludeList = settings.excludeList; // IntStream.range(0, preferences.getInt("lastItemExclude", 0)).forEach(i -> { // String description = preferences.get("ExcludeDescription" + i, ""); // PathMatchType type; @@ -179,27 +180,27 @@ public void migrateSettingsV0ToV1() { // } EPISODE_LIBRARY_FOLDER_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFolderStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFolderStructure())); + settings.episodeLibrarySettings.libraryFolderStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.libraryFolderStructure); EPISODE_LIBRARY_FOLDER_STRUCTURE.store(this, preferences); EPISODE_LIBRARY_FILENAME_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFilenameStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFilenameStructure())); + settings.episodeLibrarySettings.libraryFilenameStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.libraryFilenameStructure); EPISODE_LIBRARY_FILENAME_STRUCTURE.store(this, preferences); MOVIE_LIBRARY_FOLDER_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFolderStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFolderStructure())); + settings.episodeLibrarySettings.libraryFolderStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.libraryFolderStructure); MOVIE_LIBRARY_FOLDER_STRUCTURE.store(this, preferences); MOVIE_LIBRARY_FILENAME_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFilenameStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFilenameStructure())); + settings.episodeLibrarySettings.libraryFilenameStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.libraryFilenameStructure); MOVIE_LIBRARY_FILENAME_STRUCTURE.store(this, preferences); try { - Arrays.stream(preferences.keys()).forEach(key -> { + preferences.keys().forEach(key -> { String value = preferences.get(key, ""); preferences.remove(key); preferences.put(StringUtils.capitalize(key), value); @@ -208,29 +209,29 @@ public void migrateSettingsV0ToV1() { LOGGER.error("Error during migration of settings, ignoring..."); } - settings.setSettingsVersion(1); + settings.settingsVersion = 1; SETTINGS_VERSION.store(this, preferences); } @SuppressWarnings("deprecation") public void migrateSettingsV1ToV2() { - settings.getEpisodeLibrarySettings() - .setLibraryOtherFileAction(LibraryOtherFileActionType.fromString(preferences.get(EPISODE_LIBRARY_OTHER_FILE_ACTION.getKey(), ""))); + settings.episodeLibrarySettings.libraryOtherFileAction = + LibraryOtherFileActionType.fromString(preferences.get(EPISODE_LIBRARY_OTHER_FILE_ACTION.getKey(), "")); EPISODE_LIBRARY_OTHER_FILE_ACTION.store(this, preferences); - settings.getMovieLibrarySettings() - .setLibraryOtherFileAction(LibraryOtherFileActionType.fromString(preferences.get(MOVIE_LIBRARY_OTHER_FILE_ACTION.getKey(), ""))); + settings.movieLibrarySettings.libraryOtherFileAction = + LibraryOtherFileActionType.fromString(preferences.get(MOVIE_LIBRARY_OTHER_FILE_ACTION.getKey(), "")); MOVIE_LIBRARY_OTHER_FILE_ACTION.store(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryAction(LibraryActionType.fromString(preferences.get(EPISODE_LIBRARY_ACTION.getKey(), ""))); + settings.episodeLibrarySettings.libraryAction = + LibraryActionType.fromString(preferences.get(EPISODE_LIBRARY_ACTION.getKey(), "")); EPISODE_LIBRARY_ACTION.store(this, preferences); - settings.getMovieLibrarySettings() - .setLibraryAction(LibraryActionType.fromString(preferences.get(MOVIE_LIBRARY_ACTION.getKey(), ""))); + settings.movieLibrarySettings.libraryAction = + LibraryActionType.fromString(preferences.get(MOVIE_LIBRARY_ACTION.getKey(), "")); MOVIE_LIBRARY_ACTION.store(this, preferences); - settings.setSettingsVersion(2); + settings.settingsVersion = 2; SETTINGS_VERSION.store(this, preferences); } @@ -252,7 +253,7 @@ public void migrateSettingsV2ToV3() { // }); // preferences.remove("DictionarySize"); - settings.setSettingsVersion(3); + settings.settingsVersion = 3; SETTINGS_VERSION.store(this, preferences); } @@ -269,35 +270,37 @@ public void migrateSettingsV3ToV4() { // preferences.put("ExcludeItem" + i, newValue); // }); // EXCLUDE_ITEM.store(this, preferences); - settings.setSettingsVersion(4); + settings.settingsVersion = 4; SETTINGS_VERSION.store(this, preferences); } public void migrateSettingsV4ToV5() { - Arrays.stream(MappingType.ADDIC7ED_PROXY.getSelectionForKeyPrefixList()) + MappingType.ADDIC7ED_PROXY.selectionForKeyPrefixList .forEach(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix) - .forEach(serieMappingPair -> { - manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(serieMappingPair.getKey()) - .remove(); - })); - settings.setSettingsVersion(5); + .forEach(serieMappingPair -> manager.valueBuilder() + .cacheType(CacheType.DISK) + .key(serieMappingPair.getKey()) + .remove())); + settings.settingsVersion = 5; SETTINGS_VERSION.store(this, preferences); } public void migrateSettingsV5ToV6() { IntStream.range(0, preferences.getInt("ExcludeItemSize", 0)) - .forEach(i -> preferences.put("ExcludeItem" + i, preferences.get("ExcludeItem" + i, "").split("//", 2)[1])); + .forEach(i -> preferences.put("ExcludeItem" + i, + preferences.get("ExcludeItem" + i, "").split("//", 2)[1])); EXCLUDE_ITEM.store(this, preferences); // Conversion from String to enum + remove duplicates int defaultSelectionQualitySize = preferences.getInt("DefaultSelectionQualitySize", 0); if (defaultSelectionQualitySize > 0) { List defaultSelectionQualitySizes = IntStream.range(0, defaultSelectionQualitySize) - .mapToObj(i -> VideoPatterns.Source.fromValue(preferences.get("DefaultSelectionQuality" + i, ""))).distinct().toList(); + .mapToObj(i -> VideoPatterns.Source.fromValue(preferences.get("DefaultSelectionQuality" + i, ""))) + .distinct() + .toList(); IntStream.range(0, defaultSelectionQualitySizes.size()) - .forEach(i -> preferences.put("DefaultSelectionQuality" + i, defaultSelectionQualitySizes.get(i).name())); + .forEach(i -> preferences.put("DefaultSelectionQuality" + i, + defaultSelectionQualitySizes.get(i).name())); if (defaultSelectionQualitySize != defaultSelectionQualitySizes.size()) { preferences.putInt("DefaultSelectionQualitySize", defaultSelectionQualitySizes.size()); IntStream.range(defaultSelectionQualitySize, defaultSelectionQualitySizes.size()) @@ -306,7 +309,7 @@ public void migrateSettingsV5ToV6() { } DEFAULT_SELECTION_QUALITY.store(this, preferences); - settings.setSettingsVersion(6); + settings.settingsVersion = 6; SETTINGS_VERSION.store(this, preferences); } @@ -315,47 +318,50 @@ public void migrateSettingsV6ToV7() { String value = preferences.get(label, null); preferences.remove(label); if (StringUtils.isNotBlank(value)) { - librarySettings.getLangCodeMap().put(language, value.trim()); + librarySettings.langCodeMap.put(language, value.trim()); } }; - consumer.accept("EpisodeLibraryDefaultNlText", Language.DUTCH, settings.getEpisodeLibrarySettings()); - consumer.accept("EpisodeLibraryDefaultEnText", Language.ENGLISH, settings.getEpisodeLibrarySettings()); - consumer.accept("MovieLibraryDefaultNlText", Language.DUTCH, settings.getMovieLibrarySettings()); - consumer.accept("MovieLibraryDefaultENText", Language.ENGLISH, settings.getMovieLibrarySettings()); + consumer.accept("EpisodeLibraryDefaultNlText", Language.DUTCH, settings.episodeLibrarySettings); + consumer.accept("EpisodeLibraryDefaultEnText", Language.ENGLISH, settings.episodeLibrarySettings); + consumer.accept("MovieLibraryDefaultNlText", Language.DUTCH, settings.movieLibrarySettings); + consumer.accept("MovieLibraryDefaultENText", Language.ENGLISH, settings.movieLibrarySettings); EPISODE_LIBRARY_LANG_CODE_MAPPING.store(this, preferences); MOVIE_LIBRARY_LANG_CODE_MAPPING.store(this, preferences); - if (settings.getEpisodeLibrarySettings().hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)) { - if (StringUtils.isBlank(settings.getEpisodeLibrarySettings().getLibraryFilenameStructure())) { - settings.getMovieLibrarySettings().setLibraryFilenameStructure("%SHOW NAME%%SEPARATOR%%Season %S%"); + if (settings.episodeLibrarySettings.hasAnyLibraryAction(LibraryActionType.RENAME, + LibraryActionType.MOVEANDRENAME)) { + if (StringUtils.isBlank(settings.episodeLibrarySettings.libraryFilenameStructure)) { + settings.movieLibrarySettings.libraryFilenameStructure = "%SHOW NAME%%SEPARATOR%%Season %S%"; MOVIE_LIBRARY_FILENAME_STRUCTURE.store(this, preferences); EPISODE_LIBRARY_FILENAME_STRUCTURE.store(this, preferences); } - if (StringUtils.isBlank(settings.getEpisodeLibrarySettings().getLibraryFolderStructure())) { - settings.getMovieLibrarySettings().setLibraryFolderStructure("%SHOW NAME%.S%SS%E%EE%.%TITLE%"); + if (StringUtils.isBlank(settings.episodeLibrarySettings.libraryFolderStructure)) { + settings.movieLibrarySettings.libraryFolderStructure = "%SHOW NAME%.S%SS%E%EE%.%TITLE%"; EPISODE_LIBRARY_FOLDER_STRUCTURE.store(this, preferences); } } - if (settings.getMovieLibrarySettings().hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)) { - if (StringUtils.isBlank(settings.getMovieLibrarySettings().getLibraryFilenameStructure())) { - settings.getMovieLibrarySettings().setLibraryFilenameStructure("%MOVIE TITLE% (%YEAR%)"); + if (settings.movieLibrarySettings.hasAnyLibraryAction(LibraryActionType.RENAME, + LibraryActionType.MOVEANDRENAME)) { + if (StringUtils.isBlank(settings.movieLibrarySettings.libraryFilenameStructure)) { + settings.movieLibrarySettings.libraryFilenameStructure = "%MOVIE TITLE% (%YEAR%)"; MOVIE_LIBRARY_FILENAME_STRUCTURE.store(this, preferences); } } - settings.setSettingsVersion(7); + settings.settingsVersion = 7; SETTINGS_VERSION.store(this, preferences); } public void migrateSettingsV7ToV8() { - if (settings.isLoginOpenSubtitlesEnabled() - && !OpenSubtitlesApi.isValidCredentials(settings.getLoginOpenSubtitlesUsername(), settings.getLoginOpenSubtitlesPassword())) { - settings.setLoginOpenSubtitlesEnabled(false); + if (settings.loginOpenSubtitlesEnabled && + !OpenSubtitlesApi.isValidCredentials(settings.loginOpenSubtitlesUsername, + settings.loginOpenSubtitlesPassword)) { + settings.loginOpenSubtitlesEnabled = false; LOGIN_OPEN_SUBTITLES_ENABLED.store(this, preferences); } - settings.setSettingsVersion(8); + settings.settingsVersion = 8; SETTINGS_VERSION.store(this, preferences); } @@ -376,7 +382,8 @@ private static String migrateLibraryStructureV0(String oldStructure) { } private void migrateDatabase() { - int version = manager.valueBuilder().cacheType(CacheType.DISK).key("DATABSE_VERSION").valueSupplier(() -> 0).get(); + int version = + manager.valueBuilder().cacheType(CacheType.DISK).key("DATABSE_VERSION").valueSupplier(() -> 0).get(); if (version == 0) { migrateDatabaseV0ToV1(); } @@ -392,8 +399,11 @@ private void migrateDatabaseV0ToV1() { } private void migrateDatabaseV1ToV2() { - List> entries = manager.valueBuilder().cacheType(CacheType.DISK) - .keyFilter(k -> k.startsWith("SUBSCENE-serieName-")).returnType(SerieMapping.class).getEntries(); + List> entries = manager.valueBuilder() + .cacheType(CacheType.DISK) + .keyFilter(k -> k.startsWith("SUBSCENE-serieName-")) + .returnType(SerieMapping.class) + .getEntries(); List> editedEntries = entries.stream().map(pair -> { int lastIndexOfDash = pair.getKey().lastIndexOf("-"); int season; @@ -402,9 +412,9 @@ private void migrateDatabaseV1ToV2() { } catch (NumberFormatException e) { season = -1; } - String name = pair.getValue().getName(); - String providerId = pair.getValue().getProviderId(); - String providerName = pair.getValue().getProviderName(); + String name = pair.getValue().name; + String providerId = pair.getValue().providerId; + String providerName = pair.getValue().providerName; SerieMapping serieMapping = new SerieMapping(name, providerId, providerName, season); System.out.println(serieMapping); return Pair.of(pair.getKey(), serieMapping); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java index c1301277..796395da 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java @@ -1,46 +1,39 @@ package org.lodder.subtools.multisubdownloader.settings.model; import java.nio.file.Path; -import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; +import manifold.ext.props.rt.api.var; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; import org.lodder.subtools.sublibrary.Language; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; - -@Getter -@Setter -@Accessors(chain = true) public class LibrarySettings { - private String libraryFilenameStructure = ""; - private String libraryFolderStructure = ""; - private Path libraryFolder; - private boolean libraryFilenameReplaceSpace; - private boolean libraryFolderReplaceSpace; - private boolean libraryIncludeLanguageCode; - private boolean libraryRemoveEmptyFolders; - private boolean libraryUseTVDBNaming; - private LibraryActionType libraryAction = LibraryActionType.NOTHING; - private LibraryOtherFileActionType libraryOtherFileAction = LibraryOtherFileActionType.NOTHING; - private Character libraryFilenameReplacingSpaceChar; - private Character libraryFolderReplacingSpaceChar; - private boolean libraryBackupSubtitle; - private boolean libraryBackupUseWebsiteFileName; - private Path libraryBackupSubtitlePath; - private Map langCodeMap = new LinkedHashMap<>(); + @var String libraryFilenameStructure = ""; + @var String libraryFolderStructure = ""; + @var Path libraryFolder; + @var boolean libraryFilenameReplaceSpace; + @var boolean libraryFolderReplaceSpace; + @var boolean libraryIncludeLanguageCode; + @var boolean libraryRemoveEmptyFolders; + @var boolean libraryUseTVDBNaming; + @var LibraryActionType libraryAction = LibraryActionType.NOTHING; + @var LibraryOtherFileActionType libraryOtherFileAction = LibraryOtherFileActionType.NOTHING; + @var Character libraryFilenameReplacingSpaceChar; + @var Character libraryFolderReplacingSpaceChar; + @var boolean libraryBackupSubtitle; + @var boolean libraryBackupUseWebsiteFileName; + @var Path libraryBackupSubtitlePath; + @var Map langCodeMap = new LinkedHashMap<>(); public boolean hasLibraryAction(LibraryActionType libraryAction) { return this.libraryAction == libraryAction; } public boolean hasAnyLibraryAction(LibraryActionType... libraryActions) { - return Arrays.stream(libraryActions).anyMatch(this::hasLibraryAction); + return libraryActions.stream().anyMatch(this::hasLibraryAction); } public boolean hasLibraryOtherFileAction(LibraryOtherFileActionType libraryOtherFileAction) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java index 7d587202..e1d831a8 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java @@ -1,17 +1,15 @@ package org.lodder.subtools.multisubdownloader.settings.model; -import java.awt.Image; -import java.awt.Toolkit; +import java.awt.*; -import lombok.Getter; +import manifold.ext.props.rt.api.val; -@Getter public enum PathMatchType { FOLDER("/folder.png"), REGEX("/regex.gif"), FILE("/file.jpg"); - private final Image image; + @val Image image; PathMatchType(String imagePath) { this.image = Toolkit.getDefaultToolkit().getImage(getClass().getResource(imagePath)); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java index de770f93..ee325985 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.settings.model; +import java.awt.*; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.InvalidPathException; @@ -8,19 +9,15 @@ import java.util.function.Predicate; import java.util.regex.Pattern; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.util.NamedPattern; -import java.awt.Image; - -import lombok.Getter; - public class PathOrRegex implements Serializable { private static final long serialVersionUID = 1L; - @Getter - private final String value; - @Getter - private final transient Image image; + + @val String value; + @val transient Image image; private final transient Predicate isExcludedPathPredicate; public PathOrRegex(Path path) { @@ -40,7 +37,7 @@ public PathOrRegex(String value) { regex = true; } if (regex) { - this.image = PathMatchType.REGEX.getImage(); + this.image = PathMatchType.REGEX.image; NamedPattern np = NamedPattern.compile(value.replace("*", ".*") + ".*$", Pattern.CASE_INSENSITIVE); this.isExcludedPathPredicate = p -> np.matcher(p.getFileName().toString()).find(); } else { @@ -50,7 +47,7 @@ public PathOrRegex(String value) { } private Image getImage(Path path) { - return Files.isDirectory(path) ? PathMatchType.FOLDER.getImage() : PathMatchType.FILE.getImage(); + return Files.isDirectory(path) ? PathMatchType.FOLDER.image : PathMatchType.FILE.image; } public boolean isExcludedPath(Path path) { @@ -69,6 +66,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof PathOrRegex other && Objects.equals(value, other.getValue()); + return obj instanceof PathOrRegex other && Objects.equals(value, other.value); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java index 336c461d..fbb676f7 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java @@ -1,16 +1,14 @@ package org.lodder.subtools.multisubdownloader.settings.model; -import lombok.Getter; -import lombok.Setter; -@Getter -@Setter +import manifold.ext.props.rt.api.var; + public class ScreenSettings { - private boolean hideEpisode; - private boolean hideSeason; - private boolean hideTitle; - private boolean hideWIP; - private boolean hideType; - private boolean hideFilename; + @var boolean hideEpisode; + @var boolean hideSeason; + @var boolean hideTitle; + @var boolean hideWIP; + @var boolean hideType; + @var boolean hideFilename; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java index ccce4693..f03d1b17 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java @@ -2,7 +2,6 @@ import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -11,97 +10,92 @@ import java.util.Map; import java.util.Set; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.control.VideoPatterns; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; -import lombok.experimental.ExtensionMethod; - -@Getter -@Setter -@Accessors(chain = true) -@ExtensionMethod({ Arrays.class }) public class Settings implements UserInteractionSettingsIntf { - private Path lastOutputDir; - private boolean optionsAlwaysConfirm; - private boolean optionSubtitleExactMatch = true; - private boolean optionSubtitleKeywordMatch = true; - private boolean optionSubtitleExcludeHearingImpaired; - private boolean optionsShowOnlyFound, optionsStopOnSearchError; - private final Set excludeList = new LinkedHashSet<>(); - private final LibrarySettings movieLibrarySettings = new LibrarySettings(); - private final LibrarySettings episodeLibrarySettings = new LibrarySettings(); - private String generalProxyHost = ""; - private int generalProxyPort = 80; - private boolean generalProxyEnabled; - private List defaultIncomingFolders = new ArrayList<>(); - private List localSourcesFolders = new ArrayList<>(); - private boolean optionRecursive; - private final ScreenSettings screenSettings = new ScreenSettings(); - private boolean loginAddic7edEnabled; - private String loginAddic7edUsername; - private String loginAddic7edPassword; - private boolean loginOpenSubtitlesEnabled; - private String loginOpenSubtitlesUsername; - private String loginOpenSubtitlesPassword; - private boolean serieSourceAddic7ed = true; - private boolean serieSourceAddic7edProxy = true; - private boolean serieSourceTvSubtitles = true; - private boolean serieSourcePodnapisi = true; - private boolean serieSourceOpensubtitles = true; - private boolean serieSourceLocal = true; - private boolean serieSourceSubscene = true; - private SettingsProcessEpisodeSource processEpisodeSource = SettingsProcessEpisodeSource.TVDB; - private final Map sortWeights; - private Language subtitleLanguage; - private boolean optionsMinAutomaticSelection; - private int optionsMinAutomaticSelectionValue; - private UpdateCheckPeriod updateCheckPeriod; - private UpdateType updateType; - private boolean optionsDefaultSelection; - private List optionsDefaultSelectionQualityList = new ArrayList<>(); - private int settingsVersion; - private boolean optionsConfirmProviderMapping; - private Language language; + @var Path lastOutputDir; + @override @var boolean optionsAlwaysConfirm; + @var boolean optionSubtitleExactMatch = true; + @var boolean optionSubtitleKeywordMatch = true; + @var boolean optionSubtitleExcludeHearingImpaired; + @var boolean optionsShowOnlyFound; + @var boolean optionsStopOnSearchError; + @val Set excludeList = new LinkedHashSet<>(); + @val LibrarySettings movieLibrarySettings = new LibrarySettings(); + @val LibrarySettings episodeLibrarySettings = new LibrarySettings(); + @var String generalProxyHost = ""; + @var int generalProxyPort = 80; + @var boolean generalProxyEnabled; + @var List defaultIncomingFolders = new ArrayList<>(); + @var List localSourcesFolders = new ArrayList<>(); + @var boolean optionRecursive; + @val ScreenSettings screenSettings = new ScreenSettings(); + @var boolean loginAddic7edEnabled; + @var String loginAddic7edUsername; + @var String loginAddic7edPassword; + @var boolean loginOpenSubtitlesEnabled; + @var String loginOpenSubtitlesUsername; + @var String loginOpenSubtitlesPassword; + @var boolean serieSourceAddic7ed = true; + @var boolean serieSourceAddic7edProxy = true; + @var boolean serieSourceTvSubtitles = true; + @var boolean serieSourcePodnapisi = true; + @var boolean serieSourceOpensubtitles = true; + @var boolean serieSourceLocal = true; + @var boolean serieSourceSubscene = true; + @var SettingsProcessEpisodeSource processEpisodeSource = SettingsProcessEpisodeSource.TVDB; + @val Map sortWeights; + @var Language subtitleLanguage; + @override @var boolean optionsMinAutomaticSelection; + @override @var int optionsMinAutomaticSelectionValue; + @var UpdateCheckPeriod updateCheckPeriod; + @var UpdateType updateType; + @override @var boolean optionsDefaultSelection; + @override @var List optionsDefaultSelectionQualityList = new ArrayList<>(); + @var int settingsVersion; + @override @var boolean optionsConfirmProviderMapping; + @var Language language; public Settings() { // TODO: user should be able to edit/add these through a panel Map sortWeightsTemp = new HashMap<>(); sortWeightsTemp.put("%GROUP%", 5); VideoPatterns.Source.values().stream() - .forEach(source -> source.getValues().stream() - .forEach(keyword -> sortWeightsTemp.put(keyword, source.isManyDifferentSources() ? 1 : 2))); + .forEach(source -> source.values.stream() + .forEach(keyword -> sortWeightsTemp.put(keyword, source.manyDifferentSources ? 1 : 2))); VideoPatterns.AudioEncoding.values().stream() - .forEach(encoding -> encoding.getValues().stream().forEach(keyword -> sortWeightsTemp.put(keyword, 2))); + .forEach(encoding -> encoding.values.stream().forEach(keyword -> sortWeightsTemp.put(keyword, 2))); this.sortWeights = Collections.unmodifiableMap(sortWeightsTemp); } public List getDefaultFolders() { - return getDefaultIncomingFolders(); + return defaultIncomingFolders; } public boolean hasDefaultFolders() { - return !getDefaultIncomingFolders().isEmpty(); + return !defaultIncomingFolders.isEmpty(); } - public boolean isSerieSource(SubtitleSource sbtitleSource) { + public boolean isSerieSource(SubtitleSource subtitleSource) { // TODO: dynamically inject SubtitleProvider to settings - return switch (sbtitleSource) { - case ADDIC7ED -> this.isSerieSourceAddic7ed(); - case OPENSUBTITLES -> this.isSerieSourceOpensubtitles(); - case PODNAPISI -> this.isSerieSourcePodnapisi(); - case TVSUBTITLES -> this.isSerieSourceTvSubtitles(); - case LOCAL -> this.isSerieSourceLocal(); - case SUBSCENE -> this.isSerieSourceSubscene(); + return switch (subtitleSource) { + case ADDIC7ED -> serieSourceAddic7ed; + case OPENSUBTITLES -> serieSourceOpensubtitles; + case PODNAPISI -> serieSourcePodnapisi; + case TVSUBTITLES -> serieSourceTvSubtitles; + case LOCAL -> serieSourceLocal; + case SUBSCENE -> serieSourceSubscene; }; } - public Settings setExcludeList(Collection exclusions) { + public Settings replaceExcludeList(Collection exclusions) { excludeList.clear(); excludeList.addAll(exclusions); return this; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsExcludeItem.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsExcludeItem.java index ef44ad9b..2f320b5e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsExcludeItem.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsExcludeItem.java @@ -4,17 +4,15 @@ import java.util.function.Predicate; import java.util.regex.Pattern; +import manifold.ext.props.rt.api.get; import org.lodder.subtools.sublibrary.util.NamedPattern; -import lombok.Getter; - @Deprecated(since = "Settings version 6") -@Getter public class SettingsExcludeItem { - private final String description; - private final PathMatchType type; - private final Predicate isExcludedPredicate; + @get String description; + @get PathMatchType type; + @get Predicate isExcludedPredicate; public SettingsExcludeItem(String description, PathMatchType type) { this.description = description; @@ -22,7 +20,8 @@ public SettingsExcludeItem(String description, PathMatchType type) { this.isExcludedPredicate = switch (type) { case FOLDER, FILE -> Path.of(description)::equals; case REGEX -> { - NamedPattern np = NamedPattern.compile(description.replace("*", ".*") + ".*$", Pattern.CASE_INSENSITIVE); + NamedPattern np = + NamedPattern.compile(description.replace("*", ".*") + ".*$", Pattern.CASE_INSENSITIVE); yield file -> np.matcher(file.getFileName().toString()).find(); } }; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java index edd95b5d..1cffb265 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java @@ -2,12 +2,9 @@ import java.time.LocalDate; -import lombok.Getter; -import lombok.Setter; +import manifold.ext.props.rt.api.var; -@Getter -@Setter public class State { - private LocalDate latestUpdateCheck; + @var LocalDate latestUpdateCheck; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java index db2f1d7b..de586eb4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java @@ -1,16 +1,15 @@ package org.lodder.subtools.multisubdownloader.settings.model; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum UpdateCheckPeriod { MANUAL("InputPanel.UpdateInterval.Manual"), DAILY("InputPanel.UpdateInterval.Daily"), WEEKLY("InputPanel.UpdateInterval.Weekly"), MONTHLY("InputPanel.UpdateInterval.Monthly"); - private final String langCode; + @val String langCode; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java index 8a3f0e2e..07959558 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java @@ -1,13 +1,12 @@ package org.lodder.subtools.multisubdownloader.settings.model; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; -@RequiredArgsConstructor -@Getter +@AllArgsConstructor public enum UpdateType { STABLE("InputPanel.UpdateType.Stable"), NIGHTLY("InputPanel.UpdateType.Nightly"); - private final String msgCode; + @val String msgCode; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java index f805a643..23cc2ff4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java @@ -1,17 +1,16 @@ package org.lodder.subtools.multisubdownloader.settings.model.structure; -import org.lodder.subtools.multisubdownloader.Messages; - import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import org.lodder.subtools.multisubdownloader.Messages; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum FolderStructureTag implements StructureTag { - SEPARATOR("%SEPARATOR%", Messages.getString("StructureBuilderDialog.SystemdependendSeparator")); + SEPARATOR("%SEPARATOR%", Messages.getText("StructureBuilderDialog.SystemDependentSeparator")); - private final String label; - private final String description; + @val @override String label; + @val @override String description; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java index b3c29d54..1edcbdc4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java @@ -1,21 +1,20 @@ package org.lodder.subtools.multisubdownloader.settings.model.structure; -import org.lodder.subtools.multisubdownloader.Messages; - import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import org.lodder.subtools.multisubdownloader.Messages; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum MovieStructureTag implements StructureTag { - MOVIE_TITLE("%MOVIE TITLE%", Messages.getString("StructureBuilderDialog.MovieName")), - QUALITY("%QUALITY%", Messages.getString("StructureBuilderDialog.QualityOfMovie")), - DESCRIPTION("%DESCRIPTION%", Messages.getString("StructureBuilderDialog.MovieDescription")), - YEAR("%YEAR%", Messages.getString("StructureBuilderDialog.MovieYear")); + MOVIE_TITLE("%MOVIE TITLE%", Messages.getText("StructureBuilderDialog.MovieName")), + QUALITY("%QUALITY%", Messages.getText("StructureBuilderDialog.QualityOfMovie")), + DESCRIPTION("%DESCRIPTION%", Messages.getText("StructureBuilderDialog.MovieDescription")), + YEAR("%YEAR%", Messages.getText("StructureBuilderDialog.MovieYear")); - private final String label; - private final String description; + @val @override String label; + @val @override String description; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java index f092f23d..b3b60a40 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java @@ -1,16 +1,15 @@ package org.lodder.subtools.multisubdownloader.settings.model.structure; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; -import lombok.Getter; - -@Getter public enum SerieStructureTag implements StructureTag { SHOW_NAME("%SHOW NAME%", "StructureBuilderDialog.NameTvShow"), TITLE("%TITLE%", "StructureBuilderDialog.EpisodeTitle"), EPISODE_LONG("%EE%", "StructureBuilderDialog.NumberOfEpisodeLeadingZero"), - EPISODES_LONG("%EEX%", "StructureBuilderDialog.NumberOfEpisodeLeadingZeroForMultipe"), + EPISODES_LONG("%EEX%", "StructureBuilderDialog.NumberOfEpisodeLeadingZeroForMultiple"), EPISODE_SHORT("%E%", "StructureBuilderDialog.NumberOfEpisodeWithoutLeadingZero"), EPISODES_SHORT("%EX%", "StructureBuilderDialog.NumberOfEpisodeLeadingZeroMultiple"), SEASON_LONG("%SS%", "StructureBuilderDialog.NumberOfSeasonLeading"), @@ -18,12 +17,12 @@ public enum SerieStructureTag implements StructureTag { QUALITY("%QUALITY%", "StructureBuilderDialog.QualityOfRelease"), DESCRIPTION("%DESCRIPTION%", "StructureBuilderDialog.Description"); - private final String label; - private final String description; + @val @override String label; + @val @override String description; SerieStructureTag(String label, String descriptionMessage) { this.label = label; - this.description = Messages.getString(descriptionMessage); + this.description = Messages.getText(descriptionMessage); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java index 9a38a334..811aac95 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java @@ -1,9 +1,10 @@ package org.lodder.subtools.multisubdownloader.settings.model.structure; -public interface StructureTag { +import manifold.ext.props.rt.api.val; - String getLabel(); +public interface StructureTag { - String getDescription(); + @val String label; + @val String description; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/Local.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/Local.java index 09bd5449..f6915b3c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/Local.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/Local.java @@ -8,6 +8,9 @@ import java.util.Optional; import java.util.Set; +import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.NotImplementedException; import org.lodder.subtools.multisubdownloader.lib.control.MovieReleaseControl; import org.lodder.subtools.multisubdownloader.lib.control.TvReleaseControl; @@ -27,23 +30,18 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.FileUtils; -import org.lodder.subtools.sublibrary.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ FileUtils.class, Files.class }) +@ExtensionMethod({ Files.class }) public class Local implements SubtitleProvider { private static final Logger LOGGER = LoggerFactory.getLogger(Local.class); private final Settings settings; - @Getter - private final Manager manager; private final UserInteractionHandler userInteractionHandler; + @val @override Manager manager; + @val @override SubtitleSource subtitleSource = SubtitleSource.LOCAL; public Local(Settings settings, Manager manager, UserInteractionHandler userInteractionHandler) { this.settings = settings; @@ -51,13 +49,8 @@ public Local(Settings settings, Manager manager, UserInteractionHandler userInte this.userInteractionHandler = userInteractionHandler; } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.LOCAL; - } - private List getPossibleSubtitles(String filter) { - return settings.getLocalSourcesFolders().stream() + return settings.localSourcesFolders.stream() .flatMap(local -> getAllSubtitlesFiles(local, filter).stream()) .toList(); } @@ -68,33 +61,36 @@ public Set searchSubtitles(TvRelease tvRelease, Language language) { ReleaseParser vfp = new ReleaseParser(); String filter; - if (tvRelease.getOriginalName().length() > 0) { - filter = tvRelease.getOriginalName().replaceAll("[^A-Za-z]", "").trim(); + if (tvRelease.originalName.length() > 0) { + filter = tvRelease.originalName.replaceAll("[^A-Za-z]", "").trim(); } else { - filter = tvRelease.getName().replaceAll("[^A-Za-z]", "").trim(); + filter = tvRelease.name.replaceAll("[^A-Za-z]", "").trim(); } for (Path fileSub : getPossibleSubtitles(filter)) { try { Release release = vfp.parse(fileSub); - if ((release.getVideoType() == VideoType.EPISODE) - && (((TvRelease) release).getSeason() == tvRelease.getSeason() && Utils.containsAll( - ((TvRelease) release).getEpisodeNumbers(), tvRelease.getEpisodeNumbers()))) { + if ((release.videoType == VideoType.EPISODE) + && (((TvRelease) release).season == tvRelease.season && + new HashSet<>(((TvRelease) release).episodeNumbers).containsAll(tvRelease.episodeNumbers))) { - TvReleaseControl epCtrl = new TvReleaseControl((TvRelease) release, settings, manager, userInteractionHandler); + TvReleaseControl epCtrl = + new TvReleaseControl((TvRelease) release, settings, manager, userInteractionHandler); epCtrl.process(); - if (((TvRelease) release).getTvdbId().equals(tvRelease.getTvdbId())) { + if (((TvRelease) release).tvdbIdOptional.equals(tvRelease.tvdbIdOptional)) { Language detectedLang = DetectLanguage.execute(fileSub); if (detectedLang == language) { LOGGER.debug("Local Sub found, adding [{}]", fileSub); listFoundSubtitles.add( Subtitle.downloadSource(fileSub) - .subtitleSource(getSubtitleSource()) + .subtitleSource(subtitleSource) .fileName(fileSub.getFileNameAsString()) .language(language) .quality(ReleaseParser.getQualityKeyword(fileSub.getFileNameAsString())) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(fileSub.getFileNameAsString(), true)) + .releaseGroup( + ReleaseParser.extractReleaseGroup(fileSub.getFileNameAsString(), + true)) .uploader(fileSub.toAbsolutePath().toString()) .hearingImpaired(false)); } @@ -117,30 +113,31 @@ public Set searchSubtitles(MovieRelease movieRelease, Language languag Set listFoundSubtitles = new HashSet<>(); ReleaseParser releaseParser = new ReleaseParser(); - String filter = movieRelease.getName(); + String filter = movieRelease.name; for (Path fileSub : getPossibleSubtitles(filter)) { try { - Release release = releaseParser.parse(fileSub); - if (release.getVideoType() == VideoType.MOVIE) { - MovieReleaseControl movieCtrl = new MovieReleaseControl((MovieRelease) release, settings, manager, userInteractionHandler); - movieCtrl.process(); - if (((MovieRelease) release).getImdbId().equals(movieRelease.getImdbId())) { - Language detectedLang = DetectLanguage.execute(fileSub); - if (detectedLang == language) { + switch (releaseParser.parse(fileSub)) { + case MovieRelease release -> { + MovieReleaseControl movieCtrl = + new MovieReleaseControl(release, settings, manager, userInteractionHandler); + movieCtrl.process(); + if (release.getImdbId().equals(movieRelease.getImdbId()) + && DetectLanguage.execute(fileSub) == language) { LOGGER.debug("Local Sub found, adding {}", fileSub); - listFoundSubtitles.add( - Subtitle.downloadSource(fileSub) - .subtitleSource(getSubtitleSource()) - .fileName(fileSub.getFileNameAsString()) - .language(language) // TODO previously: language(""). This was not correct? - .quality(ReleaseParser.getQualityKeyword(fileSub.getFileNameAsString())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(fileSub.getFileNameAsString(), true)) - .uploader(fileSub.toAbsolutePath().toString()) - .hearingImpaired(false)); + listFoundSubtitles.add(Subtitle.downloadSource(fileSub) + .subtitleSource(subtitleSource) + .fileName(fileSub.getFileNameAsString()) + .language(language) // TODO previously: language(""). This was not correct? + .quality(ReleaseParser.getQualityKeyword(fileSub.getFileNameAsString())) + .subtitleMatchType(SubtitleMatchType.EVERYTHING) + .releaseGroup(ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true)) + .uploader(fileSub.toAbsolutePath().toString()) + .hearingImpaired(false)); } } + default -> { + } } } catch (ReleaseParseException | ReleaseControlException e) { if (LOGGER.isDebugEnabled() || LOGGER.isTraceEnabled()) { @@ -158,7 +155,10 @@ private List getAllSubtitlesFiles(Path dir, String filter) { try { return dir.list().filter(Files::isRegularFile) .filter(file -> file.hasExtension("srt")) - .filter(file -> file.getFileNameAsString().replaceAll("[^A-Za-z]", "").toLowerCase().contains(filter.toLowerCase())) + .filter(file -> file.getFileNameAsString() + .replaceAll("[^A-Za-z]", "") + .toLowerCase() + .contains(filter.toLowerCase())) .toList(); } catch (IOException e) { LOGGER.error(e.getMessage(), e); @@ -168,7 +168,7 @@ private List getAllSubtitlesFiles(Path dir, String filter) { @Override public String getProviderName() { - return getSubtitleSource().name(); + return subtitleSource.name(); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java index 78a2f4e1..d6b75efa 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java @@ -1,9 +1,10 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.model.SubtitleSource; public interface SubtitleApi { - SubtitleSource getSubtitleSource(); + @val SubtitleSource subtitleSource; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java index 7c8b776f..5f36a176 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java @@ -3,6 +3,7 @@ import java.util.Optional; import java.util.Set; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; @@ -16,17 +17,19 @@ public interface SubtitleProvider { + @val Manager manager; + @val SubtitleSource subtitleSource; + @val String providerName; + Set searchSubtitles(TvRelease tvRelease, Language language); Set searchSubtitles(MovieRelease movieRelease, Language language); - SubtitleSource getSubtitleSource(); - /** * @return The name of the SubtitleProvider */ default String getName() { - return getSubtitleSource().getName(); + return subtitleSource.name; } /** @@ -38,27 +41,24 @@ default String getName() { */ default Set search(Release release, Language language) { try { - if (release instanceof MovieRelease movieRelease) { - return this.searchSubtitles(movieRelease, language); - } else if (release instanceof TvRelease tvRelease) { - return this.searchSubtitles(tvRelease, language); - } + return switch (release) { + case MovieRelease movieRelease -> this.searchSubtitles(movieRelease, language); + case TvRelease tvRelease -> this.searchSubtitles(tvRelease, language); + }; } catch (Exception e) { - LoggerFactory.getLogger(SubtitleProvider.class).error("Error in %s API: %s".formatted(getName(), e.getMessage()), e); + LoggerFactory.getLogger(SubtitleProvider.class) + .error("Error in %s API: %s".formatted(getName(), e.getMessage()), e); } return Set.of(); } default void clearCache() { - getManager().clearExpiredCacheBuilder() + manager.clearExpiredCacheBuilder() .cacheType(CacheType.DISK) - .keyFilter((String k) -> k.startsWith(getProviderName() + "-")) + .keyFilter((String k) -> k.startsWith(providerName + "-")) .clear(); } - String getProviderName(); - - Manager getManager(); Optional getProviderSerieId(TvRelease tvRelease) throws X; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/AbstractAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/AbstractAdapter.java index 58365df7..70c562cf 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/AbstractAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/AbstractAdapter.java @@ -6,74 +6,72 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import com.pivovarit.function.ThrowingSupplier; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import manifold.ext.rt.api.Self; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.ProviderSerieId; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.pivovarit.function.ThrowingSupplier; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; /** - * @param - * type of the subtitle objects returned by the api - * @param - * type of the exception thrown by the api + * @param type of the subtitle objects returned by the api + * @param type of the exception thrown by the api */ -@Getter -@RequiredArgsConstructor -abstract class AbstractAdapter implements Adapter, SubtitleProvider { - Logger LOGGER = LoggerFactory.getLogger(AbstractAdapter.class); - private final Manager manager; - private final UserInteractionHandler userInteractionHandler; +@AllArgsConstructor +abstract class AbstractAdapter + implements Adapter, SubtitleProvider { + + @val @override Manager manager; + @val @override UserInteractionHandler userInteractionHandler; @RequiredArgsConstructor - public static class ExecuteCall> { + public static class ExecuteCall { private final ThrowingSupplier supplier; private String message; private int retries = 3; private final List> retryPredicates = new ArrayList<>(); private final List> exceptionHandlers = new ArrayList<>(); - private record HandleException(Predicate predicate, Function exceptionFunction) {} + private record HandleException(Predicate predicate, + Function exceptionFunction) {} - public E retryWhenException(Predicate predicate) { + public @Self ExecuteCall retryWhenException(Predicate predicate) { retryPredicates.add(predicate); - return getThis(); + return this; } - public E handleException(Predicate predicate, Function exceptionFunction) { + public @Self ExecuteCall handleException(Predicate predicate, Function exceptionFunction) { exceptionHandlers.add(new HandleException<>(predicate, exceptionFunction)); - return getThis(); + return this; } - public E handleException(Predicate predicate, Supplier supplier) { - return handleException(predicate, e -> supplier.get()); + public @Self ExecuteCall handleException(Predicate predicate, Supplier supplier) { + return handleException(predicate, _ -> supplier.get()); } - public E handleException(Function exceptionFunction) { - return handleException(e -> true, exceptionFunction); + public @Self ExecuteCall handleException(Function exceptionFunction) { + return handleException(_ -> true, exceptionFunction); } - public E handleException(Supplier supplier) { - return handleException(e -> true, e -> supplier.get()); + public @Self ExecuteCall handleException(Supplier supplier) { + return handleException(_ -> true, _ -> supplier.get()); } - public E retries(int retries) { + public @Self ExecuteCall retries(int retries) { if (retries <= 0) { throw new IllegalStateException("Retries should be greater than 0"); } this.retries = retries; - return getThis(); + return this; } - public E message(String message) { + public @Self ExecuteCall message(String message) { this.message = message; - return getThis(); + return this; } @SuppressWarnings("unchecked") @@ -97,18 +95,16 @@ public T execute() throws X { return execute(); } else { try { - return exceptionHandlers.stream().filter(handleException -> handleException.predicate().test(exception)).findAny() - .map(handleException -> handleException.exceptionFunction().apply(exception)).orElseThrow(() -> e); + return exceptionHandlers.stream() + .filter(handleException -> handleException.predicate().test(exception)) + .findAny() + .map(handleException -> handleException.exceptionFunction().apply(exception)) + .orElseThrow(() -> e); } catch (Exception e1) { throw (X) e1; } } } } - - @SuppressWarnings("unchecked") - private E getThis() { - return (E) this; - } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/Adapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/Adapter.java index 9fb96b45..2e876ddc 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/Adapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/Adapter.java @@ -1,7 +1,5 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.adapters; -import static org.lodder.subtools.sublibrary.util.OptionalExtension.*; - import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; @@ -16,6 +14,7 @@ import java.util.function.Function; import java.util.function.Supplier; +import lombok.experimental.ExtensionMethod; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; @@ -30,14 +29,10 @@ import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.experimental.ExtensionMethod; - /** - * * @param type of the subtitle objects returned by the api * @param type of the ProviderSerieId * @param type of the exception thrown by the api @@ -47,7 +42,7 @@ public interface Adapter exte Logger LOGGER = LoggerFactory.getLogger(Adapter.class); default UserInteractionSettingsIntf getUserInteractionSettings() { - return getUserInteractionHandler().getSettings(); + return getUserInteractionHandler().settings; } UserInteractionHandler getUserInteractionHandler(); @@ -55,16 +50,16 @@ default UserInteractionSettingsIntf getUserInteractionSettings() { @Override default Set searchSubtitles(MovieRelease movieRelease, Language language) { Set subtitles = new HashSet<>(); - if (StringUtils.isNotBlank(movieRelease.getFileName())) { - Path file = movieRelease.getPath().resolve(movieRelease.getFileName()); + if (StringUtils.isNotBlank(movieRelease.fileName)) { + Path file = movieRelease.getPath().resolve(movieRelease.fileName); if (file.exists()) { try { subtitles.addAll(searchMovieSubtitlesWithHash(OpenSubtitlesHasher.computeHash(file), language)); } catch (IOException e) { LOGGER.error("Error calculating file hash", e); } catch (Exception e) { - LOGGER.error("API %s searchSubtitles using file hash for movie [%s] (%s)".formatted(getSubtitleSource().getName(), - movieRelease.getName(), e.getMessage()), e); + LOGGER.error("API %s searchSubtitles using file hash for movie [%s] (%s)".formatted( + subtitleSource.name, movieRelease.name, e.getMessage()), e); } } } @@ -72,16 +67,16 @@ default Set searchSubtitles(MovieRelease movieRelease, Language langua try { subtitles.addAll(searchMovieSubtitlesWithId(imdbId, language)); } catch (Exception e) { - LOGGER.error("API %s searchSubtitles using imdbid [%s] for movie [%s] (%s)".formatted(getSubtitleSource().getName(), - imdbId, movieRelease.getName(), e.getMessage()), e); + LOGGER.error("API %s searchSubtitles using imdbid [%s] for movie [%s] (%s)".formatted( + subtitleSource.name, imdbId, movieRelease.name, e.getMessage()), e); } }); if (subtitles.isEmpty()) { try { - subtitles.addAll(searchMovieSubtitlesWithName(movieRelease.getName(), movieRelease.getYear(), language)); + subtitles.addAll(searchMovieSubtitlesWithName(movieRelease.name, movieRelease.year, language)); } catch (Exception e) { - LOGGER.error("API %s searchSubtitles using title for movie [%s] (%s)".formatted(getSubtitleSource().getName(), - movieRelease.getName(), e.getMessage()), e); + LOGGER.error("API %s searchSubtitles using title for movie [%s] (%s)".formatted(subtitleSource.name, + movieRelease.name, e.getMessage()), e); } } return convertToSubtitles(movieRelease, subtitles, language); @@ -100,9 +95,11 @@ default Set searchSubtitles(TvRelease tvRelease, Language language) { try { return convertToSubtitles(tvRelease, searchSerieSubtitles(tvRelease, language), language); } catch (Exception e) { - String displayName = StringUtils.defaultIfBlank(tvRelease.getOriginalName(), tvRelease.getName()); - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(displayName, tvRelease.getSeason(), tvRelease.getFirstEpisodeNumber()), e.getMessage()), e); + String displayName = StringUtils.defaultIfBlank(tvRelease.originalName, tvRelease.name); + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(displayName, tvRelease.season, tvRelease.firstEpisodeNumber), + e.getMessage()), + e); return Set.of(); } } @@ -115,7 +112,7 @@ default Set searchSubtitles(TvRelease tvRelease, Language language) { @Override default Optional getProviderSerieId(TvRelease tvRelease) throws X { - if (StringUtils.isNotBlank(tvRelease.getCustomName())) { + if (StringUtils.isNotBlank(tvRelease.customName)) { return getProviderSerieId(tvRelease, TvRelease::getOriginalName, TvRelease::getCustomName); } else { Optional providerSerieId = getProviderSerieId(tvRelease, TvRelease::getOriginalName); @@ -123,14 +120,15 @@ default Optional getProviderSerieId(TvRelease tvRelease) throws X } } - default Optional getProviderSerieId(TvRelease tvRelease, Function nameFunction) throws X { + default Optional getProviderSerieId(TvRelease tvRelease, Function nameFunction) + throws X { return getProviderSerieId(tvRelease, nameFunction, nameFunction); } default Optional getProviderSerieId(TvRelease tvRelease, Function nameFunction, Function customNameFunction) throws X { - return getProviderSerieId(nameFunction.apply(tvRelease), customNameFunction.apply(tvRelease), tvRelease.getDisplayName(), - tvRelease.getSeason(), tvRelease.getTvdbId()); + return getProviderSerieId(nameFunction.apply(tvRelease), customNameFunction.apply(tvRelease), + tvRelease.displayName, tvRelease.season, tvRelease.getTvdbIdOptional()); } default Optional getProviderSerieId(String serieName, String displayName, int season, @@ -138,11 +136,12 @@ default Optional getProviderSerieId(String serieName, String displ return getProviderSerieId(serieName, serieName, displayName, season, tvdbIdOptional); } - default Optional getProviderSerieId(String serieName, String serieNameToSearchFor, String displayName, int season, - OptionalInt tvdbIdOptional) throws X { - Supplier> tvdbIdValueBuilder = - () -> mapToObj(tvdbIdOptional, tvdbId -> getManager().valueBuilder().cacheType(CacheType.DISK) - .key("%s-serieName-tvdbId:%s-%s".formatted(getProviderName(), tvdbId, + default Optional getProviderSerieId(String serieName, String serieNameToSearchFor, String displayName, + int season, OptionalInt tvdbIdOptional) throws X { + Supplier> tvdbIdValueBuilder = () -> tvdbIdOptional.mapToObj( + tvdbId -> manager.valueBuilder() + .cacheType(CacheType.DISK) + .key("%s-serieName-tvdbId:%s-%s".formatted(providerName, tvdbId, useSeasonForSerieId() ? season : -1))).orElseThrow(); if (tvdbIdOptional.isPresent() && tvdbIdValueBuilder.get().isPresent()) { // if value using the tvdbId is present, return it @@ -152,25 +151,21 @@ default Optional getProviderSerieId(String serieName, String serie return Optional.empty(); } int seasonToUse = useSeasonForSerieId() ? season : 0; - ValueBuilderIsPresentIntf serieNameValueBuilder = getManager().valueBuilder() + ValueBuilderIsPresentIntf serieNameValueBuilder = manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%s-serieName-name:%s-%s".formatted(getProviderName(), serieName.toLowerCase(), seasonToUse)); + .key("%s-serieName-name:%s-%s".formatted(providerName, serieName.toLowerCase(), seasonToUse)); if (StringUtils.equals(serieNameToSearchFor, serieName) && serieNameValueBuilder.isPresent()) { - boolean returnValue; - Optional value; if (serieNameValueBuilder.isTemporaryObject()) { - returnValue = !serieNameValueBuilder.isExpiredTemporary(); - value = Optional.empty(); + if (!serieNameValueBuilder.isExpiredTemporary()) { + return Optional.empty(); + } } else { - value = serieNameValueBuilder.returnType(SerieMapping.class).getOptional(); - returnValue = true; - } - if (returnValue) { - // if value using the name is present, return it - // if tvdbId is known, also persist the value using the tvdbId - return ifPresentDo(value, - providerSerieName -> tvdbIdOptional.ifPresent(tvdbId -> tvdbIdValueBuilder.get().value(providerSerieName).store())); + Optional serieMapping = + serieNameValueBuilder.returnType(SerieMapping.class).getOptional(); + serieMapping.ifPresent(providerSerieName -> tvdbIdOptional.ifPresent( + tvdbId -> tvdbIdValueBuilder.get().value(providerSerieName).store())); + return serieMapping; } } @@ -179,48 +174,48 @@ default Optional getProviderSerieId(String serieName, String serie // if no provider serie id's could be found, store a temporary null value with expiration time of 1 day // (so the provider isn't contacted every time this method is being called) // If a temporary expired value was already found, persist the null value with a doubled expiration time - serieNameValueBuilder - .value(new SerieMapping(serieName, null, null, seasonToUse)) + serieNameValueBuilder.value(new SerieMapping(serieName, null, null, seasonToUse)) .storeTempNullValue() - .timeToLive(OptionalExtension - .map(serieNameValueBuilder.getTemporaryTimeToLive(), v -> v * 2) + .timeToLive(serieNameValueBuilder.getTemporaryTimeToLive() + .map(v -> v * 2) .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) .storeAsTempValue(); return Optional.empty(); } SerieMapping serieMapping; - if (!getUserInteractionSettings().isOptionsConfirmProviderMapping() && providerSerieIds.size() == 1) { - serieMapping = new SerieMapping(serieName, providerSerieIds.get(0).getId(), providerSerieIds.get(0).getName(), seasonToUse); + if (!getUserInteractionSettings().optionsConfirmProviderMapping && providerSerieIds.size() == 1) { + serieMapping = + new SerieMapping(serieName, providerSerieIds.first.id, providerSerieIds.first.name, seasonToUse); } else { - ValueBuilderIsPresentIntf previousResultsValueBuilder = getManager().valueBuilder() + ValueBuilderIsPresentIntf previousResultsValueBuilder = manager.valueBuilder() .cacheType(CacheType.MEMORY) - .key("%s-serieName-prev-results:%s-%s".formatted(getProviderName(), displayName.toLowerCase(), seasonToUse)); + .key("%s-serieName-prev-results:%s-%s".formatted(providerName, displayName.toLowerCase(), + seasonToUse)); boolean previousResultsPresent = previousResultsValueBuilder.isPresent(); Optional uriForSerie; // Check if the previous results were the same for the service. If so, don't ask the user to select again - if (previousResultsPresent - && previousResultsValueBuilder.returnType((Class>) null, null).getCollection().equals(providerSerieIds)) { + if (previousResultsPresent && previousResultsValueBuilder.returnType((Class>) null, null) + .getCollection() + .equals(providerSerieIds)) { uriForSerie = Optional.empty(); } else { // let the user select the correct provider serie id - uriForSerie = getUserInteractionHandler().selectFromList(providerSerieIds, - useSeasonForSerieId() - ? Messages.getString("SelectDialog.SelectSerieNameForNameWithSeason").formatted(displayName, seasonToUse) - : Messages.getString("SelectDialog.SelectSerieNameForName").formatted(displayName), - getProviderName(), - this::providerSerieIdToDisplayString); + uriForSerie = getUserInteractionHandler().selectFromList(providerSerieIds, useSeasonForSerieId() ? + Messages.getText("SelectDialog.SelectSerieNameForNameWithSeason") + .formatted(displayName, seasonToUse) : + Messages.getText("SelectDialog.SelectSerieNameForName").formatted(displayName), + providerName, this::providerSerieIdToDisplayString); } if (uriForSerie.isEmpty()) { if (serieNameToSearchFor.equals(serieName)) { // if no provider serie id was selected, store a temporary null value with expiration time of 1 day, // or the doubled previously temporary value (if present) - serieNameValueBuilder - .value(new SerieMapping(serieNameToSearchFor, null, null, seasonToUse)) + serieNameValueBuilder.value(new SerieMapping(serieNameToSearchFor, null, null, seasonToUse)) .storeTempNullValue() - .timeToLive(OptionalExtension - .map(serieNameValueBuilder.getTemporaryTimeToLive(), v -> v * 2) + .timeToLive(serieNameValueBuilder.getTemporaryTimeToLive() + .map(v -> v * 2) .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) .storeAsTempValue(); previousResultsValueBuilder.collectionValue(providerSerieIds).store(); @@ -228,7 +223,7 @@ default Optional getProviderSerieId(String serieName, String serie return Optional.empty(); } // create a serieMapping for the selected value - serieMapping = new SerieMapping(serieName, uriForSerie.get().getId(), uriForSerie.get().getName(), seasonToUse); + serieMapping = new SerieMapping(serieName, uriForSerie.get().id, uriForSerie.get().name, seasonToUse); } if (tvdbIdOptional.isPresent()) { tvdbIdValueBuilder.get().value(serieMapping).store(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edAdapter.java index 550347b7..4a56bf97 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edAdapter.java @@ -8,6 +8,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import extensions.java.lang.String.StringExt; +import lombok.Getter; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.JAddic7edApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.exception.Addic7edException; @@ -22,21 +26,18 @@ import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.OptionalExtension; -import org.lodder.subtools.sublibrary.util.StringUtil; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - @Getter -@ExtensionMethod({ OptionalExtension.class }) public class JAddic7edAdapter extends AbstractAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(JAddic7edAdapter.class); + private static LazySupplier jaapi; + @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; + @val @override String providerName = subtitleSource.name(); public JAddic7edAdapter(boolean isLoginEnabled, String username, String password, boolean speedy, Manager manager, UserInteractionHandler userInteractionHandler) { @@ -44,9 +45,10 @@ public JAddic7edAdapter(boolean isLoginEnabled, String username, String password if (jaapi == null) { jaapi = new LazySupplier<>(() -> { try { - return isLoginEnabled ? new JAddic7edApi(username, password, speedy, manager) : new JAddic7edApi(speedy, manager); + return isLoginEnabled ? new JAddic7edApi(username, password, speedy, manager) : + new JAddic7edApi(speedy, manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } @@ -56,15 +58,6 @@ private JAddic7edApi getApi() { return jaapi.get(); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.ADDIC7ED; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name(); - } @Override public List searchMovieSubtitlesWithHash(String hash, Language language) { @@ -85,40 +78,40 @@ public List searchMovieSubtitlesWithName(String name } @Override - public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, Language language) { + public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, + Language language) { // TODO implement this return Set.of(); } @Override - public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws Addic7edException { - return getProviderSerieId(tvRelease) - .map(providerSerieId -> tvRelease.getEpisodeNumbers().stream() - .flatMap(episode -> { - try { - return getApi().getSubtitles(providerSerieId, tvRelease.getSeason(), episode, language).stream(); - } catch (Addic7edException e) { - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode), - e.getMessage()), e); - return Stream.empty(); - } - }) - .collect(Collectors.toSet())) - .orElseGet(Set::of); + public Set searchSerieSubtitles(TvRelease tvRelease, Language language) + throws Addic7edException { + return getProviderSerieId(tvRelease).map( + providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + try { + return getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); + } catch (Addic7edException e) { + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode), + e.getMessage()), e); + return Stream.empty(); + } + }).collect(Collectors.toSet())).orElseGet(Set::of); } @Override - public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { + public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, + Language language) { return subtitles.stream() .filter(sub -> language == sub.getLanguage()) .map(sub -> Subtitle.downloadSource(sub.getUrl()) - .subtitleSource(getSubtitleSource()) - .fileName(StringUtil.removeIllegalFilenameChars(sub.getTitle() + " " + sub.getVersion())) + .subtitleSource(subtitleSource) + .fileName(StringExt.removeIllegalFilenameChars(sub.getTitle() + " " + sub.getVersion())) .language(sub.getLanguage()) .quality(ReleaseParser.getQualityKeyword(sub.getTitle() + " " + sub.getVersion())) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(sub.getTitle() + " " + sub.getVersion(), + .releaseGroup(ReleaseParser.extractReleaseGroup(sub.getTitle() + " " + sub.getVersion(), (sub.getTitle() + " " + sub.getVersion()).endsWith(".srt"))) .uploader(sub.getUploader()) .hearingImpaired(false)) @@ -126,9 +119,12 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws Addic7edException { - return getApi().getProviderId(serieName).stream() - .sorted(Comparator.comparing(n -> !serieName.replaceAll("[^A-Za-z]", "").equalsIgnoreCase(n.getName().replaceAll("[^A-Za-z]", "")))) + public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + throws Addic7edException { + return getApi().getProviderId(serieName) + .stream() + .sorted(Comparator.comparing(n -> !serieName.replaceAll("[^A-Za-z]", "") + .equalsIgnoreCase(n.name.replaceAll("[^A-Za-z]", "")))) .toList(); } @@ -139,6 +135,6 @@ public boolean useSeasonForSerieId() { @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { - return providerSerieId.getName(); + return providerSerieId.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edViaProxyAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edViaProxyAdapter.java index ca51c350..6ef924a1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edViaProxyAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JAddic7edViaProxyAdapter.java @@ -11,6 +11,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.pivovarit.function.ThrowingSupplier; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.gestdown.invoker.ApiException; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.proxy.gestdown.JAddic7edProxyGestdownApi; @@ -21,38 +26,23 @@ import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.pivovarit.function.ThrowingSupplier; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; - @Getter -@ExtensionMethod({ OptionalExtension.class }) public class JAddic7edViaProxyAdapter extends AbstractAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(JAddic7edViaProxyAdapter.class); + private final JAddic7edProxyGestdownApi jaapi; + @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; + @val @override String providerName = subtitleSource.name() + "-GESTDOWN"; public JAddic7edViaProxyAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); this.jaapi = new JAddic7edProxyGestdownApi(manager); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.ADDIC7ED; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name() + "-GESTDOWN"; - } - private JAddic7edProxyGestdownApi getApi() { return jaapi; } @@ -82,51 +72,52 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set @Override public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws ApiException { - return getProviderSerieId(tvRelease) - .map(providerSerieId -> tvRelease.getEpisodeNumbers().stream() - .flatMap(episode -> { - try { - return new ExecuteCall<>(() -> getApi().getSubtitles(providerSerieId, tvRelease.getSeason(), episode, language)) - .message("getSubtitles: [%s]".formatted( - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode))) - .retryWhenHttpCode(ReturnCode.REFRESHING) - .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) - .execute().stream(); - } catch (ApiException e) { - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode), - e.getMessage()), e); - return Stream.empty(); - } - }) - .collect(Collectors.toSet())) - .orElseGet(Set::of); + return getProviderSerieId(tvRelease).map( + providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + try { + return new ExecuteCall<>( + () -> getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language)) + .message("getSubtitles: [%s]".formatted( + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode))) + .retryWhenHttpCode(ReturnCode.REFRESHING) + .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) + .execute() + .stream(); + } catch (ApiException e) { + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode), + e.getMessage()), e); + return Stream.empty(); + } + }).collect(Collectors.toSet())).orElseGet(Set::of); } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws ApiException { - List serieIds = tvdbIdOptional.mapToObj(tvdbId -> new ExecuteCall<>(() -> getApi().getProviderSerieName(tvdbId)) - .message("getProviderSerieName: [%s]".formatted(tvdbId)) - .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) - .handleHttpCode(ReturnCode.NOT_FOUND, () -> { - LOGGER.info("API %s - Could not find tvdbId [%s]".formatted(getProviderName(), tvdbId)); - return List.of(); - }) - .execute()).orElseGet(List::of); + public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + throws ApiException { + List serieIds = tvdbIdOptional.mapToObj( + tvdbId -> new ExecuteCall<>(() -> getApi().getProviderSerieName(tvdbId)).message( + "getProviderSerieName: [$tvdbId]") + .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) + .handleHttpCode(ReturnCode.NOT_FOUND, () -> { + LOGGER.info("API %s - Could not find tvdbId [%s]".formatted(providerName, tvdbId)); + return List.of(); + }) + .execute()).orElseGet(List::of); if (serieIds.isEmpty()) { - serieIds = new ExecuteCall<>(() -> getApi().getProviderSerieName(serieName)) - .message("getProviderSerieName: [%s]".formatted(serieName)) + serieIds = new ExecuteCall<>(() -> getApi().getProviderSerieName(serieName)).message( + "getProviderSerieName: [$serieName]") .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) .handleHttpCode(ReturnCode.NOT_FOUND, () -> { - LOGGER.info("API %s - Could not find serie name [%s]".formatted(getProviderName(), serieName)); + LOGGER.info("API $providerName - Could not find serie name [$serieName]"); return List.of(); }) .execute(); } return serieIds.stream() - .sorted(Comparator - .comparing(n -> !serieName.replaceAll("[^A-Za-z]", "").equalsIgnoreCase(n.getName().replaceAll("[^A-Za-z]", "")))) + .sorted(Comparator.comparing(n -> !serieName.replaceAll("[^A-Za-z]", "") + .equalsIgnoreCase(n.name.replaceAll("[^A-Za-z]", "")))) .toList(); } @@ -142,15 +133,13 @@ public boolean useSeasonForSerieId() { @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { - return providerSerieId.getName(); + return providerSerieId.name; } @Getter @RequiredArgsConstructor private enum ReturnCode { - NOT_FOUND(404), - RATE_LIMIT_REACHED(429), - REFRESHING(423); + NOT_FOUND(404), RATE_LIMIT_REACHED(429), REFRESHING(423); final int code; @@ -159,27 +148,31 @@ public boolean isSameCode(int code) { } } - public static class ExecuteCall extends AbstractAdapter.ExecuteCall> { + private static class ExecuteCall extends AbstractAdapter.ExecuteCall { public ExecuteCall(ThrowingSupplier supplier) { super(supplier); } public ExecuteCall retryWhenHttpCode(ReturnCode returnCode) { - return super.retryWhenException(e -> returnCode.isSameCode(e.getCode())); + super.retryWhenException(e -> returnCode.isSameCode(e.getCode())); + return this; } public ExecuteCall handleHttpCode(ReturnCode returnCode, Function function) { - return super.handleException(e -> returnCode.isSameCode(e.getCode()), function); + super.handleException(e -> returnCode.isSameCode(e.getCode()), function); + return this; } public ExecuteCall handleHttpCode(ReturnCode returnCode, Supplier supplier) { - return super.handleException(e -> returnCode.isSameCode(e.getCode()), supplier); + super.handleException(e -> returnCode.isSameCode(e.getCode()), supplier); + return this; } @Override public ExecuteCall handleException(Supplier suppliers) { - return super.handleException(e -> true, suppliers); + super.handleException(_ -> true, suppliers); + return this; } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JOpenSubAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JOpenSubAdapter.java index 99cbf7b8..c8043b7f 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JOpenSubAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JOpenSubAdapter.java @@ -8,6 +8,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.Getter; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; @@ -23,23 +26,21 @@ import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; -import org.opensubtitles.model.Latest200ResponseDataInnerAttributesFilesInner; import org.opensubtitles.model.SubtitleAttributes; +import org.opensubtitles.model.SubtitleAttributesFilesInner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - @Getter -@ExtensionMethod({ OptionalExtension.class }) public class JOpenSubAdapter extends AbstractAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(JOpenSubAdapter.class); + private static LazySupplier osApi; + @val @override SubtitleSource subtitleSource = SubtitleSource.OPENSUBTITLES; + @val @override String providerName = subtitleSource.name(); public JOpenSubAdapter(boolean isLoginEnabled, String username, String password, Manager manager, UserInteractionHandler userInteractionHandler) { @@ -53,7 +54,7 @@ public JOpenSubAdapter(boolean isLoginEnabled, String username, String password, return new OpenSubtitlesApi(manager); } } catch (OpenSubtitlesException e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } @@ -64,108 +65,91 @@ private OpenSubtitlesApi getApi() { } @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.OPENSUBTITLES; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name(); - } - - @Override - public List searchMovieSubtitlesWithHash(String hash, Language language) throws OpenSubtitlesException { - return getApi().searchSubtitles() - .movieHash(hash) - .language(language) - .searchSubtitles() - .getData(); + public List searchMovieSubtitlesWithHash(String hash, Language language) + throws OpenSubtitlesException { + return getApi().searchSubtitles().movieHash(hash).language(language).searchSubtitles().getData(); } @Override - public List searchMovieSubtitlesWithId(int tvdbId, Language language) throws OpenSubtitlesException { - return getApi().searchSubtitles() - .imdbId(tvdbId) - .language(language) - .searchSubtitles() - .getData(); + public List searchMovieSubtitlesWithId(int tvdbId, Language language) + throws OpenSubtitlesException { + return getApi().searchSubtitles().imdbId(tvdbId).language(language).searchSubtitles().getData(); } @Override public List searchMovieSubtitlesWithName(String name, int year, Language language) throws OpenSubtitlesException { - return getApi().searchSubtitles() - .query(name) - .language(language) - .searchSubtitles() - .getData(); + return getApi().searchSubtitles().query(name).language(language).searchSubtitles().getData(); } @Override - public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, Language language) { - return subtitles.stream().map(org.opensubtitles.model.Subtitle::getAttributes) - .filter(attributes -> movieRelease.getYear() == attributes.getFeatureDetails().getYear().intValue()) + public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, + Language language) { + return subtitles.stream() + .map(org.opensubtitles.model.Subtitle::getAttributes) + .filter(attributes -> movieRelease.year == attributes.getFeatureDetails().getYear().intValue()) .flatMap(attributes -> attributes.getFiles().stream().map(file -> createSubtitle(file, attributes))) .collect(Collectors.toSet()); } @Override - public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws OpenSubtitlesException { - return getProviderSerieId(tvRelease) - .map(providerSerieId -> tvRelease.getEpisodeNumbers().stream() - .flatMap(episode -> { - try { - return getApi().searchSubtitles() - .query(providerSerieId.getName()) - .season(tvRelease.getSeason()) - .episode(episode) - .language(language) - .searchSubtitles() - .getData().stream(); - } catch (OpenSubtitlesException e) { - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode), - e.getMessage()), e); - return Stream.empty(); - } - }) - .collect(Collectors.toSet())) - .orElseGet(Set::of); + public Set searchSerieSubtitles(TvRelease tvRelease, Language language) + throws OpenSubtitlesException { + return getProviderSerieId(tvRelease).map( + providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + try { + return getApi().searchSubtitles() + .query(providerSerieId.name) + .season(tvRelease.season) + .episode(episode) + .language(language) + .searchSubtitles() + .getData() + .stream(); + } catch (OpenSubtitlesException e) { + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode), + e.getMessage()), e); + return Stream.empty(); + } + }).collect(Collectors.toSet())).orElseGet(Set::of); } @Override - public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { - String name = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.getName(), "[^A-Za-z]", "")); - String originalName = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.getOriginalName(), "[^A-Za-z]", "")); - return subtitles.stream().map(org.opensubtitles.model.Subtitle::getAttributes) - .flatMap(attributes -> attributes.getFiles().stream() - .filter(file -> file.getFileName() != null) - .filter(file -> { - String subFileName = file.getFileName().replaceAll("[^A-Za-z]", "").toLowerCase(); - return subFileName.contains(name) || (StringUtils.isNotBlank(originalName) && subFileName.contains(originalName)); - }) - .map(file -> createSubtitle(file, attributes))) + public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, + Language language) { + String name = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.name, "[^A-Za-z]", "")); + String originalName = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.originalName, "[^A-Za-z]", "")); + return subtitles.stream() + .map(org.opensubtitles.model.Subtitle::getAttributes) + .flatMap(attributes -> attributes.getFiles().stream().filter(file -> { + String subFileName = file.getFileName().replaceAll("[^A-Za-z]", "").toLowerCase(); + return subFileName.contains(name) || + (StringUtils.isNotBlank(originalName) && subFileName.contains(originalName)); + }).map(file -> createSubtitle(file, attributes))) .collect(Collectors.toSet()); } - private Subtitle createSubtitle(Latest200ResponseDataInnerAttributesFilesInner file, SubtitleAttributes attributes) { - return Subtitle.downloadSource(() -> getApi().downloadSubtitle().fileId(file.getFileId().intValue()).download().getLink()) - .subtitleSource(getSubtitleSource()) + private Subtitle createSubtitle(SubtitleAttributesFilesInner file, SubtitleAttributes attributes) { + return Subtitle.downloadSource( + () -> getApi().downloadSubtitle().fileId(file.getFileId().intValue()).download().getLink()) + .subtitleSource(subtitleSource) .fileName(file.getFileName()) .language(Language.fromIdOptional(attributes.getLanguage()).orElse(null)) .quality(ReleaseParser.getQualityKeyword(file.getFileName())) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(file.getFileName(), file.getFileName().endsWith(".srt"))) - .uploader(attributes.getUploader().getName()) - .hearingImpaired(attributes.isHearingImpaired()); + .releaseGroup(ReleaseParser.extractReleaseGroup(file.fileName, file.fileName.endsWith(".srt"))) + .uploader(attributes.getUploader() != null ? attributes.getUploader().getName() : null) + .hearingImpaired(Boolean.TRUE == attributes.isHearingImpaired()); } @Override public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws OpenSubtitlesException { - return getApi().getProviderSerieIds(serieName).stream() - .sorted(Comparator.comparing( - (OpensubtitleSerieId n) -> !serieName.replaceAll("[^A-Za-z]", "").equalsIgnoreCase(n.getName().replaceAll("[^A-Za-z]", ""))) + return getApi().getProviderSerieIds(serieName) + .stream() + .sorted(Comparator.comparing((OpensubtitleSerieId n) -> !serieName.replaceAll("[^A-Za-z]", "") + .equalsIgnoreCase(n.name.replaceAll("[^A-Za-z]", ""))) .thenComparing(OpensubtitleSerieId::getYear, Comparator.reverseOrder())) .toList(); } @@ -177,6 +161,6 @@ public boolean useSeasonForSerieId() { @Override public String providerSerieIdToDisplayString(OpensubtitleSerieId providerSerieId) { - return "%s (%s)".formatted(providerSerieId.getName(), providerSerieId.getYear()); + return "${providerSerieId.name} (${providerSerieId.year})"; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JPodnapisiAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JPodnapisiAdapter.java index 4869aaff..75e23e06 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JPodnapisiAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JPodnapisiAdapter.java @@ -7,6 +7,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.JPodnapisiApi; @@ -22,20 +24,18 @@ import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@Getter -@ExtensionMethod({ OptionalExtension.class }) -public class JPodnapisiAdapter extends AbstractAdapter { +public class JPodnapisiAdapter + extends AbstractAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(JPodnapisiAdapter.class); + private static LazySupplier jpapi; + @val @override SubtitleSource subtitleSource = SubtitleSource.PODNAPISI; + @val @override String providerName = subtitleSource.name(); public JPodnapisiAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -44,7 +44,7 @@ public JPodnapisiAdapter(Manager manager, UserInteractionHandler userInteraction try { return new JPodnapisiApi(manager, "JBierSubDownloader"); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } @@ -54,16 +54,6 @@ private JPodnapisiApi getApi() { return jpapi.get(); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.PODNAPISI; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name(); - } - @Override public List searchMovieSubtitlesWithHash(String hash, Language language) { return List.of(); @@ -75,56 +65,58 @@ public List searchMovieSubtitlesWithId(int tvdbId, } @Override - public List searchMovieSubtitlesWithName(String name, int year, Language language) throws PodnapisiException { + public List searchMovieSubtitlesWithName(String name, int year, Language language) + throws PodnapisiException { return getApi().getMovieSubtitles(name, year, 0, 0, language); } @Override - public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, Language language) { + public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, + Language language) { return buildListSubtitles(language, subtitles); } @Override - public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws PodnapisiException { - return getProviderSerieId(tvRelease) - .map(providerSerieId -> tvRelease.getEpisodeNumbers().stream() - .flatMap(episode -> { - try { - return getApi().getSerieSubtitles(providerSerieId, tvRelease.getSeason(), episode, language).stream(); - } catch (PodnapisiException e) { - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode), - e.getMessage()), e); - return Stream.empty(); - } - }) - .collect(Collectors.toSet())) - .orElseGet(Set::of); + public Set searchSerieSubtitles(TvRelease tvRelease, Language language) + throws PodnapisiException { + return getProviderSerieId(tvRelease).map( + providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + try { + return api.getSerieSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); + } catch (PodnapisiException e) { + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode), + e.getMessage()), e); + return Stream.empty(); + } + }).collect(Collectors.toSet())).orElseGet(Set::of); } @Override - public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { + public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, + Language language) { return buildListSubtitles(language, subtitles); } private Set buildListSubtitles(Language language, Collection lSubtitles) { return lSubtitles.stream() - .filter(ossd -> StringUtils.isNotBlank(ossd.getReleaseString())) - .map(ossd -> Subtitle.downloadSource(ossd.getUrl()) - .subtitleSource(getSubtitleSource()) - .fileName(ossd.getReleaseString()) + .filter(ossd -> StringUtils.isNotBlank(ossd.releaseString)) + .map(ossd -> Subtitle.downloadSource(ossd.url) + .subtitleSource(subtitleSource) + .fileName(ossd.releaseString) .language(language) - .quality(ReleaseParser.getQualityKeyword(ossd.getReleaseString())) + .quality(ReleaseParser.getQualityKeyword(ossd.releaseString)) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(ossd.getReleaseString(), - StringUtils.endsWith(ossd.getReleaseString(), ".srt"))) - .uploader(ossd.getUploaderName()) - .hearingImpaired(ossd.isHearingImpaired())) + .releaseGroup(ReleaseParser.extractReleaseGroup(ossd.releaseString, + StringUtils.endsWith(ossd.releaseString, ".srt"))) + .uploader(ossd.uploaderName) + .hearingImpaired(ossd.hearingImpaired)) .collect(Collectors.toSet()); } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws PodnapisiException { + public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + throws PodnapisiException { return getApi().getPodnapisiShowName(serieName).stream().toList(); } @@ -135,6 +127,6 @@ public boolean useSeasonForSerieId() { @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { - return providerSerieId.getName(); + return providerSerieId.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JSubsceneAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JSubsceneAdapter.java index d7286a62..7f756595 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JSubsceneAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JSubsceneAdapter.java @@ -13,6 +13,8 @@ import java.util.stream.IntStream; import java.util.stream.Stream; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.SubsceneApi; @@ -28,21 +30,17 @@ import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.OptionalExtension; -import org.lodder.subtools.sublibrary.util.StringUtil; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@Getter -@ExtensionMethod({ OptionalExtension.class }) public class JSubsceneAdapter extends AbstractAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(JSubsceneAdapter.class); + private static LazySupplier api; + @val @override SubtitleSource subtitleSource = SubtitleSource.SUBSCENE; + @val @override String providerName = subtitleSource.name(); public JSubsceneAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -51,7 +49,7 @@ public JSubsceneAdapter(Manager manager, UserInteractionHandler userInteractionH try { return new SubsceneApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } @@ -61,16 +59,6 @@ private SubsceneApi getApi() { return api.get(); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.SUBSCENE; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name(); - } - @Override public List searchMovieSubtitlesWithHash(String hash, Language language) { // TODO implement this @@ -90,31 +78,31 @@ public List searchMovieSubtitlesWithName(String name } @Override - public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, Language language) { + public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, + Language language) { // TODO implement this return Set.of(); } @Override - public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws SubsceneException { - return getProviderSerieId(tvRelease) - .map(providerSerieId -> tvRelease.getEpisodeNumbers().stream() - .flatMap(episode -> { - try { - return getApi().getSubtitles(providerSerieId, tvRelease.getSeason(), episode, language).stream(); - } catch (SubsceneException e) { - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode), - e.getMessage()), e); - return Stream.empty(); - } - }) - .collect(Collectors.toSet())) - .orElseGet(Set::of); + public Set searchSerieSubtitles(TvRelease tvRelease, Language language) + throws SubsceneException { + return getProviderSerieId(tvRelease).map( + providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + try { + return getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); + } catch (SubsceneException e) { + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode), + e.getMessage()), e); + return Stream.empty(); + } + }).collect(Collectors.toSet())).orElseGet(Set::of); } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws SubsceneException { + public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + throws SubsceneException { ToIntFunction providerTypeFunction = value -> switch (value) { case "TV-Serie" -> 1; case "Exact" -> 2; @@ -122,13 +110,15 @@ public List getSortedProviderSerieIds(OptionalInt tvdbIdOptiona default -> 4; }; Pattern yearPattern = Pattern.compile("(\\d\\d\\d\\d)"); - return getApi().getSubSceneSerieNames(serieName).entrySet().stream() + return getApi().getSubSceneSerieNames(serieName) + .entrySet() + .stream() .sorted(Comparator.comparingInt(entry -> providerTypeFunction.applyAsInt(entry.getKey()))) - .map(Entry::getValue).flatMap(List::stream) - .sorted(Comparator - .comparing((SubSceneSerieId serieId) -> serieId.getSeason() == 0) + .map(Entry::getValue) + .flatMap(List::stream) + .sorted(Comparator.comparing((SubSceneSerieId serieId) -> serieId.season == 0) .thenComparing(serieId -> { - Matcher matcher = yearPattern.matcher(serieId.getName()); + Matcher matcher = yearPattern.matcher(serieId.name); return matcher.find() ? Integer.parseInt(matcher.group()) : 0; }, Comparator.reverseOrder()) .thenComparing(SubSceneSerieId::getSeason, Comparator.reverseOrder())) @@ -137,25 +127,26 @@ public List getSortedProviderSerieIds(OptionalInt tvdbIdOptiona } @Override - public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { + public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, + Language language) { return subtitles.stream() .filter(sub -> language == sub.getLanguage()) - .filter(sub -> sub.getName().contains(getSeasonEpisodeString(tvRelease.getSeason(), tvRelease.getEpisodeNumbers().get(0)))) + .filter(sub -> sub.getName() + .contains(getSeasonEpisodeString(tvRelease.season, tvRelease.firstEpisodeNumber))) .map(sub -> Subtitle.downloadSource(sub.getUrlSupplier()) - .subtitleSource(getSubtitleSource()) - .fileName(StringUtil.removeIllegalFilenameChars(sub.getName())) + .subtitleSource(subtitleSource) + .fileName(sub.getName().removeIllegalFilenameChars()) .language(sub.getLanguage()) .quality(ReleaseParser.getQualityKeyword(sub.getName())) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(sub.getName(), false)) + .releaseGroup(ReleaseParser.extractReleaseGroup(sub.getName(), false)) .uploader(sub.getUploader()) .hearingImpaired(sub.isHearingImpaired())) .collect(Collectors.toSet()); } private String getSeasonEpisodeString(int season, int episode) { - return "S" + org.apache.commons.lang3.StringUtils.leftPad(String.valueOf(season), 2, "0") + "E" - + org.apache.commons.lang3.StringUtils.leftPad(String.valueOf(episode), 2, "0"); + return "S%02dE%02d".formatted(season, episode); } @Override @@ -165,13 +156,14 @@ public boolean useSeasonForSerieId() { @Override public String providerSerieIdToDisplayString(SubSceneSerieId providerSerieId) { - if (providerSerieId.getId().endsWith("-season")) { + if (providerSerieId.id.endsWith("-season")) { OptionalInt season = IntStream.rangeClosed(1, 100) - .filter(i -> providerSerieId.getId().endsWith("-%s-season".formatted(SubsceneApi.getOrdinalName(i).toLowerCase()))).findAny(); + .filter(i -> providerSerieId.id.endsWith("-${SubsceneApi.getOrdinalName(i).toLowerCase()}-season")) + .findAny(); if (season.isPresent()) { - return "%s (%s %s)".formatted(providerSerieId.getName(), Messages.getString("App.Season"), season.getAsInt()); + return "%s %s %s".formatted(providerSerieId.name, Messages.getText("App.Season"), season.getAsInt()); } } - return providerSerieId.getName(); + return providerSerieId.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JTVsubtitlesAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JTVsubtitlesAdapter.java index 97baa08f..3c8452fb 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JTVsubtitlesAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/adapters/JTVsubtitlesAdapter.java @@ -10,6 +10,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.JTVSubtitlesApi; @@ -25,20 +27,18 @@ import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@Getter -@ExtensionMethod({ OptionalExtension.class }) -public class JTVsubtitlesAdapter extends AbstractAdapter { +public class JTVsubtitlesAdapter + extends AbstractAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(JTVsubtitlesAdapter.class); + private static LazySupplier jtvapi; + @val @override SubtitleSource subtitleSource = SubtitleSource.TVSUBTITLES; + @val @override String providerName = subtitleSource.name(); public JTVsubtitlesAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -47,7 +47,7 @@ public JTVsubtitlesAdapter(Manager manager, UserInteractionHandler userInteracti try { return new JTVSubtitlesApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } @@ -57,16 +57,6 @@ private JTVSubtitlesApi getApi() { return jtvapi.get(); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.TVSUBTITLES; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name(); - } - @Override public List searchMovieSubtitlesWithHash(String hash, Language language) { // TODO implement this @@ -86,52 +76,55 @@ public List searchMovieSubtitlesWithName(String n } @Override - public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, Language language) { + public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, + Language language) { // TODO implement this return Set.of(); } @Override - public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws TvSubtitlesException { - return getProviderSerieId(tvRelease) - .map(providerSerieId -> tvRelease.getEpisodeNumbers().stream() - .flatMap(episode -> { - try { - return getApi().getSubtitles(providerSerieId, tvRelease.getSeason(), episode, language).stream(); - } catch (TvSubtitlesException e) { - LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(getSubtitleSource().getName(), - TvRelease.formatName(providerSerieId.getProviderName(), tvRelease.getSeason(), episode), - e.getMessage()), e); - return Stream.empty(); - } - }) - .collect(Collectors.toSet())) - .orElseGet(Set::of); + public Set searchSerieSubtitles(TvRelease tvRelease, Language language) + throws TvSubtitlesException { + return getProviderSerieId(tvRelease).map( + providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + try { + return getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); + } catch (TvSubtitlesException e) { + LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(subtitleSource.name, + TvRelease.formatName(providerSerieId.providerName, tvRelease.season, episode), + e.getMessage()), e); + return Stream.empty(); + } + }).collect(Collectors.toSet())).orElseGet(Set::of); } @Override - public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { + public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, + Language language) { return subtitles.stream() - .map(sub -> Subtitle.downloadSource(sub.getUrl()) - .subtitleSource(getSubtitleSource()) - .fileName(sub.getFilename()) + .map(sub -> Subtitle.downloadSource(sub.url) + .subtitleSource(subtitleSource) + .fileName(sub.filename) .language(language) - .quality(ReleaseParser.getQualityKeyword(sub.getFilename() + " " + sub.getRip())) + .quality(ReleaseParser.getQualityKeyword(sub.filename + " " + sub.rip)) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(sub.getFilename(), StringUtils.endsWith(sub.getFilename(), ".srt"))) - .uploader(sub.getAuthor()) + .releaseGroup(ReleaseParser.extractReleaseGroup(sub.filename, + StringUtils.endsWith(sub.filename, ".srt"))) + .uploader(sub.author) .hearingImpaired(false)) .collect(Collectors.toSet()); } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws TvSubtitlesException { + public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + throws TvSubtitlesException { Pattern yearPatter = Pattern.compile("\\((\\d\\d\\d\\d)-(\\d\\d\\d\\d)\\)"); - return getApi().getUrisForSerieName(serieName).stream() - .sorted(Comparator.comparing( - (ProviderSerieId n) -> !serieName.replaceAll("[^A-Za-z]", "").equalsIgnoreCase(n.getName().replaceAll("[^A-Za-z]", ""))) + return getApi().getUrisForSerieName(serieName) + .stream() + .sorted(Comparator.comparing((ProviderSerieId n) -> !serieName.replaceAll("[^A-Za-z]", "") + .equalsIgnoreCase(n.name.replaceAll("[^A-Za-z]", ""))) .thenComparing((ProviderSerieId providerSerieId) -> { - Matcher matcher = yearPatter.matcher(providerSerieId.getName()); + Matcher matcher = yearPatter.matcher(providerSerieId.name); if (matcher.find()) { return Integer.parseInt(matcher.group(2)); } @@ -147,6 +140,6 @@ public boolean useSeasonForSerieId() { @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { - return providerSerieId.getName(); + return providerSerieId.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/JAddic7edApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/JAddic7edApi.java index a70cb8c9..93790ca9 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/JAddic7edApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/JAddic7edApi.java @@ -7,12 +7,12 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -28,19 +28,17 @@ import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.OptionalExtension; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ OptionalExtension.class }) public class JAddic7edApi extends Html implements SubtitleApi { private static final long RATEDURATION = 1; // seconds + private static final String DOMAIN = "https://www.addic7ed.com"; - private final static Pattern TITLE_PATTERN = Pattern.compile(".*? - [0-9]+x[0-9]+ - (.*)"); - private final static Pattern VERSION_PATTERN = Pattern.compile("Version (.+), Duration: ([0-9]+).([0-9])+"); + private static final Pattern TITLE_PATTERN = Pattern.compile(".*? - \\d+x\\d+ - (.*)"); + private static final Pattern VERSION_PATTERN = Pattern.compile("Version (.+), Duration: (\\d+).(\\d)+"); private final boolean speedy; private LocalDateTime lastRequest = LocalDateTime.now(); + @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; public JAddic7edApi(boolean speedy, Manager manager) { super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); @@ -55,7 +53,7 @@ public JAddic7edApi(String username, String password, boolean speedy, Manager ma public void login(String username, String password) throws Addic7edException { try { - getManager().postBuilder() + manager.postBuilder() .url(DOMAIN + "/dologin.php") .addData("username", username) .addData("password", password) @@ -71,14 +69,15 @@ public List getProviderId(String serieName) throws Addic7edExce return List.of(); } try { - List providerSerieIds = getContent(DOMAIN + "/allshows/" + serieName.split(" ")[0]) - .map(doc -> doc.select("table.tabel90 td a").stream() - .map(element -> new ProviderSerieId(element.text(), element.attr("href").split("/")[2])).toList()) - .orElseGet(List::of); + List providerSerieIds = + getContent("$DOMAIN/allshows/" + serieName.split(" ")[0]).selectAllByCss("table.tabel90 td a") + .stream() + .map(elem -> new ProviderSerieId(elem.text(), elem.attr("href").split("/")[2])) + .toList(); String serieNameFormatted = serieName.replaceAll("[^A-Za-z]", ""); List providerSerieIdsFormatted = providerSerieIds.stream().filter(providerId -> { - String formattedSerieName = providerId.getName().replaceAll("[^A-Za-z]", ""); + String formattedSerieName = providerId.name.replaceAll("[^A-Za-z]", ""); return StringUtils.containsIgnoreCase(serieNameFormatted, formattedSerieName) || StringUtils.containsIgnoreCase(formattedSerieName, serieNameFormatted); }).toList(); @@ -99,57 +98,49 @@ public List getProviderId(String serieName) throws Addic7edExce // .toList()) // .orElseGet(List::of)); - public List getSubtitles(SerieMapping addic7edSerieMapping, int season, int episode, Language language) - throws Addic7edException { - return getManager().valueBuilder() + public List getSubtitles(SerieMapping addic7edSerieMapping, int season, int episode, + Language language) throws Addic7edException { + return manager.valueBuilder() .memoryCache() - .key("%s-subtitles-%s-%s-%s-%s".formatted(getSubtitleSource().name(), addic7edSerieMapping.getProviderId(), season, episode, - language)) + .key("%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), addic7edSerieMapping.providerId, + season, episode, language)) .collectionSupplier(Addic7edSubtitleDescriptor.class, () -> { List languageIds = LanguageId.forLanguage(language); - String url = "%s/serie/%s/%s/%s/%s".formatted( - DOMAIN, - URLEncoder.encode(addic7edSerieMapping.getProviderName().replace(" ", "_"), UTF_8), - season, - episode, - languageIds.size() == 1 ? languageIds.get(0).getId() : LanguageId.ALL.getId()); - - Optional doc = getContent(url); - if (doc.isEmpty()) { - return List.of(); - } + String url = "%s/serie/%s/%s/%s/%s".formatted(DOMAIN, + URLEncoder.encode(addic7edSerieMapping.providerName.replace(" ", "_"), UTF_8), season, + episode, languageIds.size() == 1 ? languageIds.first.id : LanguageId.ALL.id); + Document doc = getContent(url); String title = null; - Elements elTitel = doc.get().getElementsByClass("titulo"); - if (elTitel.size() == 1) { - Matcher matcher = TITLE_PATTERN.matcher(elTitel.get(0).html()); + Elements elTitle = doc.getElementsByClass("titulo"); + if (elTitle.size() == 1) { + Matcher matcher = TITLE_PATTERN.matcher(elTitle.first.html()); if (matcher.matches()) { title = matcher.group(1); } } - String uploader, version, lang, download; - boolean hearingImpaired; - Elements blocks = doc.get().select(".tabel95[width='100%']"); + + Elements blocks = doc.select(".tabel95[width='100%']"); List lSubtitles = new ArrayList<>(); for (Element block : blocks) { - uploader = ""; - version = null; - lang = null; - download = null; - hearingImpaired = false; + String uploader = ""; + String version = null; + String lang = null; + String download = null; + boolean hearingImpaired = false; Elements classesNewsTitle = block.getElementsByClass("NewsTitle"); Elements classesNewsDate = block.getElementsByClass("newsDate").select("td[colspan=3]"); if (classesNewsTitle.size() == 1 && classesNewsDate.size() == 1) { - Matcher m = VERSION_PATTERN.matcher(classesNewsTitle.get(0).text().trim()); + Matcher m = VERSION_PATTERN.matcher(classesNewsTitle.first.text().trim()); if (!m.matches()) { break; } else { version = m.group(1).trim(); - uploader = block.selectFirst("a[href*=user/]").text(); + uploader = block.selectFirst("a[href*=user/]").getText(); hearingImpaired = !block.select("img[title~=Hearing]").isEmpty(); } } @@ -163,15 +154,15 @@ public List getSubtitles(SerieMapping addic7edSerieM } // incomplete not wanted - if ((lang != null && td.toString().toLowerCase().contains("completed")) - && td.html().toLowerCase().contains("% completed")) { + if ((lang != null && td.toString().toLowerCase().contains("completed")) && + td.html().toLowerCase().contains("% completed")) { lang = null; } Elements downloadElements = td.getElementsByClass("buttonDownload"); if (lang != null && downloadElements.size() > 0) { if (downloadElements.size() == 1) { - download = DOMAIN + downloadElements.get(0).attr("href"); + download = DOMAIN + downloadElements.first.attr("href"); } if (downloadElements.size() == 2) { download = DOMAIN + downloadElements.get(1).attr("href"); @@ -179,8 +170,7 @@ public List getSubtitles(SerieMapping addic7edSerieM } if (lang != null && download != null && title != null) { Addic7edSubtitleDescriptor sub = - new Addic7edSubtitleDescriptor() - .setUploader(uploader) + new Addic7edSubtitleDescriptor().setUploader(uploader) .setTitle(title.trim()) .setVersion(version.trim()) .setUrl(download) @@ -196,23 +186,19 @@ public List getSubtitles(SerieMapping addic7edSerieM } } return lSubtitles; - }).getCollection(); + }) + .getCollection(); } public boolean isDuplicate(List lSubtitles, Addic7edSubtitleDescriptor sub) { return lSubtitles.stream() - .anyMatch(s -> s.getLanguage() == sub.getLanguage() - && StringUtils.equals(s.getUrl(), sub.getUrl()) - && StringUtils.equals(s.getVersion(), sub.getVersion())); + .anyMatch(s -> s.getLanguage() == sub.getLanguage() && StringUtils.equals(s.getUrl(), sub.getUrl()) && + StringUtils.equals(s.getVersion(), sub.getVersion())); } - private Optional getContent(String url) throws Addic7edException { - return getContent(url, null); - } - - private Optional getContent(String url, Predicate emptyResultPredicate) throws Addic7edException { + private Document getContent(String url) throws Addic7edException { try { - if (!speedy && !getManager().valueBuilder().cacheType(CacheType.MEMORY).key(url).isPresent()) { + if (!speedy && !manager.valueBuilder().cacheType(CacheType.MEMORY).key(url).isPresent()) { // if (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATEDURATION) { // LOGGER.info("RateLimit is reached for ADDIC7ed, please wait {} seconds", RATEDURATION); // } @@ -227,14 +213,9 @@ private Optional getContent(String url, Predicate emptyResultP } lastRequest = LocalDateTime.now(); } - return this.getHtml(url).cacheType(CacheType.NONE).getAsJsoupDocument(emptyResultPredicate); + return this.getHtml(url).cacheType(CacheType.NONE).getAsJsoupDocument(); } catch (Exception e) { throw new Addic7edException(e); } } - - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.ADDIC7ED; - } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/LanguageId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/LanguageId.java index 51abb175..2440789e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/LanguageId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/LanguageId.java @@ -1,16 +1,12 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Language; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor +@AllArgsConstructor public enum LanguageId { ALL(null, 0), @@ -80,10 +76,10 @@ public enum LanguageId { VIETNAMESE(Language.VIETNAMESE, 45), WELSH(Language.WELSH, 65); - private final Language language; - private final int id; + @val Language language; + @val int id; public static List forLanguage(Language language) { - return Arrays.stream(LanguageId.values()).filter(langId -> langId.getLanguage() == language).collect(Collectors.toList()); + return LanguageId.values().stream().filter(langId -> langId.language == language).toList(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/exception/Addic7edException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/exception/Addic7edException.java index eefd94cb..2f1de388 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/exception/Addic7edException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/exception/Addic7edException.java @@ -2,11 +2,10 @@ import java.io.Serial; +import lombok.experimental.StandardException; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import lombok.experimental.StandardException; - @StandardException public class Addic7edException extends SubtitlesProviderException { @@ -15,6 +14,6 @@ public class Addic7edException extends SubtitlesProviderException { @Override public String getSubtitleProvider() { - return SubtitleSource.ADDIC7ED.getName(); + return SubtitleSource.ADDIC7ED.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/model/Addic7edSubtitleDescriptor.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/model/Addic7edSubtitleDescriptor.java index 080f51b1..fa474494 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/model/Addic7edSubtitleDescriptor.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/model/Addic7edSubtitleDescriptor.java @@ -1,11 +1,10 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.model; -import org.lodder.subtools.sublibrary.Language; - import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import org.lodder.subtools.sublibrary.Language; @EqualsAndHashCode @Accessors(chain = true) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/JAddic7edProxyGestdownApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/JAddic7edProxyGestdownApi.java index 163ca312..f22c136b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/JAddic7edProxyGestdownApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/JAddic7edProxyGestdownApi.java @@ -5,6 +5,9 @@ import java.util.Set; import java.util.UUID; +import extensions.java.lang.String.StringExt; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.gestdown.api.SubtitlesApi; import org.gestdown.api.TvShowsApi; import org.gestdown.invoker.ApiException; @@ -21,18 +24,15 @@ import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.OptionalExtension; -import org.lodder.subtools.sublibrary.util.StringUtil; - -import lombok.experimental.ExtensionMethod; // see https://www.gestdown.info/Api -@ExtensionMethod({ OptionalExtension.class }) public class JAddic7edProxyGestdownApi extends Html implements SubtitleApi { private static final String DOMAIN = "https://api.gestdown.info"; + private final TvShowsApi tvShowsApi; private final SubtitlesApi subtitlesApi; + @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; public JAddic7edProxyGestdownApi(Manager manager) { super(manager); @@ -50,16 +50,21 @@ public List getProviderSerieName(int tvdbId) throws ApiExceptio .map(showDto -> new ProviderSerieId(showDto.getName(), showDto.getId().toString())).toList(); } - public Set getSubtitles(SerieMapping providerSerieId, int season, int episode, Language language) throws ApiException { - return getManager().valueBuilder() + public Set getSubtitles(SerieMapping providerSerieId, int season, int episode, Language language) + throws ApiException { + return manager.valueBuilder() .memoryCache() - .key("%s-subtitles-%s-%s-%s-%s".formatted(getSubtitleSource().name(), providerSerieId.getProviderId(), season, episode, language)) + .key("%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), providerSerieId.providerId, + season, episode, language)) .collectionSupplier(Subtitle.class, () -> { Set results = new HashSet<>(); - SubtitleSearchResponse response = subtitlesApi.subtitlesGetShowUniqueIdSeasonEpisodeLanguageGet(language.getName(), - UUID.fromString(providerSerieId.getProviderId()), season, episode); - response.getMatchingSubtitles().stream() - .filter(SubtitleDto::isCompleted).map(sub -> mapToSubtitle(sub, response.getEpisode(), language)) + SubtitleSearchResponse response = + subtitlesApi.subtitlesGetShowUniqueIdSeasonEpisodeLanguageGet(language.getName(), + UUID.fromString(providerSerieId.providerId), season, episode); + response.getMatchingSubtitles() + .stream() + .filter(SubtitleDto::isCompleted) + .map(sub -> mapToSubtitle(sub, response.getEpisode(), language)) .forEach(results::add); return results; }).getCollection(); @@ -67,9 +72,9 @@ public Set getSubtitles(SerieMapping providerSerieId, int season, int private Subtitle mapToSubtitle(SubtitleDto sub, EpisodeDto episodedto, Language language) { return Subtitle.downloadSource(getDownloadUrl(sub.getDownloadUri())) - .subtitleSource(getSubtitleSource()) - .fileName(StringUtil - .removeIllegalFilenameChars("%s - %s - %s".formatted(episodedto.getShow(), episodedto.getTitle(), sub.getVersion()))) + .subtitleSource(subtitleSource) + .fileName(StringExt.removeIllegalFilenameChars( + "${episodedto.show} - ${episodedto.title} - ${sub.version}")) .language(language) .quality(ReleaseParser.getQualityKeyword(episodedto.getTitle() + " " + sub.getVersion())) .subtitleMatchType(SubtitleMatchType.EVERYTHING) @@ -81,9 +86,4 @@ private Subtitle mapToSubtitle(SubtitleDto sub, EpisodeDto episodedto, Language public String getDownloadUrl(String subtitleId) { return DOMAIN + subtitleId; } - - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.ADDIC7ED; - } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/DownloadSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/DownloadSubtitle.java index a093fc4d..147273fd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/DownloadSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/DownloadSubtitle.java @@ -1,15 +1,14 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.exception.OpenSubtitlesException; import org.opensubtitles.api.DownloadApi; import org.opensubtitles.invoker.ApiClient; import org.opensubtitles.model.Download200Response; import org.opensubtitles.model.DownloadRequest; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; - @Accessors(fluent = true, chain = true) @Setter @RequiredArgsConstructor @@ -20,7 +19,8 @@ public class DownloadSubtitle extends OpenSubtitlesExecuter { public Download200Response download() throws OpenSubtitlesException { try { - return execute(() -> new DownloadApi(apiClient).download(new DownloadRequest().fileId(fileId))); + return execute(() -> new DownloadApi(apiClient).download("SubTools", + new DownloadRequest().fileId(fileId))); } catch (Exception e) { throw new OpenSubtitlesException(e); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java index 61f1c084..10d51097 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java @@ -4,15 +4,14 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import org.json.JSONArray; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.exception.OpenSubtitlesException; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.model.OpensubtitleSerieId; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import org.lodder.subtools.sublibrary.util.JSONUtils; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.http.HttpClientException; import org.opensubtitles.api.AuthenticationApi; import org.opensubtitles.invoker.ApiClient; @@ -20,16 +19,13 @@ import org.opensubtitles.model.Login200Response; import org.opensubtitles.model.LoginRequest; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ OptionalExtension.class, JSONUtils.class }) public class OpenSubtitlesApi implements SubtitleApi { - private static final String APIKEY = "lNNp0yv0ah8gytkmYPbHwuaATJqr4rS9"; + private static final String APIKEY = "3IlyaP0KNv6QmJ1gOBX8IXwzD1P9b8c0";//"lNNp0yv0ah8gytkmYPbHwuaATJqr4rS9"; private static final ApiClient API_CLIENT; - @Getter - private final Manager manager; + private static final String USER_AGENT = "SubTools"; + @val Manager manager; + @val @override SubtitleSource subtitleSource = SubtitleSource.OPENSUBTITLES; static { API_CLIENT = new ApiClient(); @@ -48,7 +44,8 @@ public OpenSubtitlesApi(Manager manager, String userName, String password) throw public void login(String userName, String password) throws OpenSubtitlesException { try { Login200Response loginResponse = - new AuthenticationApi(API_CLIENT).login("application/json", new LoginRequest().username(userName).password(password)); + new AuthenticationApi(API_CLIENT).login("application/json", USER_AGENT, + new LoginRequest().username(userName).password(password)); API_CLIENT.setBearerToken(loginResponse.getToken()); } catch (ApiException e) { throw new OpenSubtitlesException(e); @@ -57,7 +54,8 @@ public void login(String userName, String password) throws OpenSubtitlesExceptio public static boolean isValidCredentials(String userName, String password) { try { - new AuthenticationApi(API_CLIENT).login("application/json", new LoginRequest().username(userName).password(password)); + new AuthenticationApi(API_CLIENT).login("application/json", USER_AGENT, + new LoginRequest().username(userName).password(password)); return true; } catch (ApiException e) { return false; @@ -74,26 +72,22 @@ public DownloadSubtitle downloadSubtitle() { public List getProviderSerieIds(String serieName) throws OpenSubtitlesException { try { - JSONArray shows = manager.getPageContentBuilder() + return manager.getPageContentBuilder() .url("https://www.opensubtitles.org/libs/suggest.php?format=json3&MovieName=" + URLEncoder.encode(serieName.toLowerCase(), StandardCharsets.UTF_8)) .userAgent("") .cacheType(CacheType.MEMORY) .retries(1) - .retryPredicate(exception -> exception instanceof HttpClientException e && e.getResponseCode() == 429) + .retryPredicate(exc -> exc instanceof HttpClientException e && e.getResponseCode() == 429) .retryWait(5) - .getAsJsonArray(); - return shows.stream() + .getAsJsonArray() + .stream() .filter(show -> "tv".equals(show.getString("kind"))) - .map(show -> new OpensubtitleSerieId(show.getString("name"), show.getInt("id"), show.getString("year"))) + .map(show -> new OpensubtitleSerieId(show.getString("name"), show.getInt("id"), + show.getString("year"))) .toList(); } catch (Exception e) { throw new OpenSubtitlesException(e); } } - - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.OPENSUBTITLES; - } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesExecuter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesExecuter.java index 1889c005..20fcb6f9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesExecuter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesExecuter.java @@ -1,8 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles; -import org.opensubtitles.invoker.ApiException; - import com.pivovarit.function.ThrowingSupplier; +import org.opensubtitles.invoker.ApiException; public abstract class OpenSubtitlesExecuter { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesHasher.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesHasher.java index 125b247a..f26d8b62 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesHasher.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesHasher.java @@ -13,9 +13,8 @@ import java.nio.file.StandardOpenOption; /** - * Hash code is based on Media Player Classic. In natural language it calculates: size + 64bit - * checksum of the first and last 64k (even if they overlap because the file is smaller than - * 128k). + * Hash code is based on Media Player Classic. In natural language it calculates: size + 64bit checksum of the first and + * last 64k (even if they overlap because the file is smaller than 128k). */ public class OpenSubtitlesHasher { @@ -30,8 +29,9 @@ public static String computeHash(Path path) throws IOException { try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) { long head = computeHashForChunk(fileChannel.map(MapMode.READ_ONLY, 0, chunkSizeForFile)); - long tail = computeHashForChunk(fileChannel.map(MapMode.READ_ONLY, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile)); - return String.format("%016x", size + head + tail); + long tail = computeHashForChunk( + fileChannel.map(MapMode.READ_ONLY, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile)); + return "%016x".formatted(size + head + tail); } } @@ -39,7 +39,8 @@ public static String computeHash(InputStream stream, long length) throws IOExcep int chunkSizeForFile = (int) Math.min(HASH_CHUNK_SIZE, length); - // buffer that will contain the head and the tail chunk, chunks will overlap if length is smaller than two chunks + // buffer that will contain the head and the tail chunk, chunks will overlap if length is smaller than two + // chunks byte[] chunkBytes = new byte[(int) Math.min(2 * HASH_CHUNK_SIZE, length)]; try (DataInputStream in = new DataInputStream(stream)) { @@ -59,9 +60,10 @@ public static String computeHash(InputStream stream, long length) throws IOExcep in.readFully(chunkBytes, chunkSizeForFile, chunkBytes.length - chunkSizeForFile); long head = computeHashForChunk(ByteBuffer.wrap(chunkBytes, 0, chunkSizeForFile)); - long tail = computeHashForChunk(ByteBuffer.wrap(chunkBytes, chunkBytes.length - chunkSizeForFile, chunkSizeForFile)); + long tail = computeHashForChunk( + ByteBuffer.wrap(chunkBytes, chunkBytes.length - chunkSizeForFile, chunkSizeForFile)); - return String.format("%016x", length + head + tail); + return "%016x".formatted(length + head + tail); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/SearchSubtitles.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/SearchSubtitles.java index 50592fed..1e2a9ff1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/SearchSubtitles.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/SearchSubtitles.java @@ -1,5 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.exception.OpenSubtitlesException; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param.AiTranslatedEnum; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param.ForeignPartsOnlyEnum; @@ -18,11 +22,6 @@ import org.opensubtitles.invoker.ApiClient; import org.opensubtitles.model.Subtitles200Response; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; - @Accessors(fluent = true, chain = true) @Getter @Setter @@ -77,20 +76,25 @@ public class SearchSubtitles extends OpenSubtitlesExecuter { private Integer year; + private String userAgent = "SubTools"; // should be set + public Subtitles200Response searchSubtitles() throws OpenSubtitlesException { return manager.valueBuilder() .cacheType(CacheType.MEMORY) - .key("OpenSubtitles-subtitles-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s" - .formatted(id, imdbId, tmdbId, type, query, language, movieHash, userId, hearingImpaired, - foreignPartsOnly, trustedSources, machineTranslated, aiTranslated, orderBy, orderDirection, - parentFeatureId, parentImdbId, parentTmdbId, season, episode, year, movieHashMatch, page)) + .key("OpenSubtitles-subtitles-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s".formatted( + id, imdbId, tmdbId, type, query, language, movieHash, userId, hearingImpaired, foreignPartsOnly, + trustedSources, machineTranslated, aiTranslated, orderBy, orderDirection, parentFeatureId, + parentImdbId, parentTmdbId, season, episode, year, movieHashMatch, page)) .valueSupplier(() -> { try { - return execute(() -> new SubtitlesApi(apiClient).subtitles(id, imdbId, tmdbId, getValue(type), query, - language != null ? language.getLangCode() : null, movieHash, - userId, getValue(hearingImpaired), getValue(foreignPartsOnly), getValue(trustedSources), getValue(machineTranslated), - getValue(aiTranslated), orderBy == null ? null : orderBy.getParamName(), getValue(orderDirection), parentFeatureId, - parentImdbId, parentTmdbId, season, episode, year, getValue(movieHashMatch), page)); + return execute( + () -> new SubtitlesApi(apiClient).subtitles(id, imdbId, tmdbId, getValue(type), query, + language != null ? language.langCode : null, movieHash, userId, + getValue(hearingImpaired), getValue(foreignPartsOnly), getValue(trustedSources), + getValue(machineTranslated), getValue(aiTranslated), + orderBy == null ? null : orderBy.paramName, getValue(orderDirection), + parentFeatureId, parentImdbId, parentTmdbId, season, episode, year, + getValue(movieHashMatch), page, userAgent)); } catch (Exception e) { throw new OpenSubtitlesException(e); } @@ -99,6 +103,6 @@ userId, getValue(hearingImpaired), getValue(foreignPartsOnly), getValue(trustedS } private String getValue(ParamIntf param) { - return param == null ? null : param.getValue(); + return param == null ? null : param.value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitlesException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitlesException.java index e863b27a..2044554b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitlesException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitlesException.java @@ -2,11 +2,10 @@ import java.io.Serial; +import lombok.experimental.StandardException; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import lombok.experimental.StandardException; - @StandardException public class OpenSubtitlesException extends SubtitlesProviderException { @@ -15,6 +14,6 @@ public class OpenSubtitlesException extends SubtitlesProviderException { @Override public String getSubtitleProvider() { - return SubtitleSource.OPENSUBTITLES.getName(); + return SubtitleSource.OPENSUBTITLES.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieDescriptor.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieDescriptor.java index 74602779..06cd0c00 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieDescriptor.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieDescriptor.java @@ -2,27 +2,21 @@ import java.util.Objects; -import lombok.Getter; -import lombok.Setter; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; -@Getter -@Setter +@AllArgsConstructor public class OpenSubtitlesMovieDescriptor { - private final int year; - private final int imdbId; - private String name; + @var String name; + @val int year; + @val int imdbId; public OpenSubtitlesMovieDescriptor(String name, int imdbId) { this(name, -1, imdbId); } - public OpenSubtitlesMovieDescriptor(String name, int year, int imdbId) { - this.name = name; - this.year = year; - this.imdbId = imdbId; - } - @Override public boolean equals(Object object) { return object instanceof OpenSubtitlesMovieDescriptor other @@ -39,7 +33,6 @@ public String toString() { if (year < 0) { return name; } - - return String.format("%s (%d)", name, year); + return "$name ($year)"; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleDescriptor.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleDescriptor.java index 8f083d5c..92597a50 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleDescriptor.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleDescriptor.java @@ -1,45 +1,42 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.model; -import lombok.Getter; -import lombok.Setter; +import manifold.ext.props.rt.api.var; -@Getter -@Setter public class OpenSubtitlesSubtitleDescriptor { - private String userNickName; - private String subFormat; - private int idSubtitle; - private int idMovie; - private String subBad; - private int userId; - private String zipDownloadLink; - private long subSize; - private String subFileName; - private String subDownloadLink; - private String userRank; - private String subActualCD; - private String movieImdbRating; - private String subAuthorComment; - private String subRating; - private String subtitlesLink; - private String subHearingImpaired; - private String subHash; - private int idSubMovieFile; - private String ISO639; - private int subDownloadsCnt; - private String movieHash; - private int subSumCD; - private String subComments; - private long movieByteSize; - private String languageName; - private int movieYear; - private String subLanguageID; - private String movieReleaseName; - private String movieTimeMS; - private String matchedBy; - private String movieName; - private String subAddDate; - private int idMovieImdb; - private String MovieNameEng; - private int idSubtitleFile; + @var String userNickName; + @var String subFormat; + @var int idSubtitle; + @var int idMovie; + @var String subBad; + @var int userId; + @var String zipDownloadLink; + @var long subSize; + @var String subFileName; + @var String subDownloadLink; + @var String userRank; + @var String subActualCD; + @var String movieImdbRating; + @var String subAuthorComment; + @var String subRating; + @var String subtitlesLink; + @var String subHearingImpaired; + @var String subHash; + @var int idSubMovieFile; + @var String ISO639; + @var int subDownloadsCnt; + @var String movieHash; + @var int subSumCD; + @var String subComments; + @var long movieByteSize; + @var String languageName; + @var int movieYear; + @var String subLanguageID; + @var String movieReleaseName; + @var String movieTimeMS; + @var String matchedBy; + @var String movieName; + @var String subAddDate; + @var int idMovieImdb; + @var String MovieNameEng; + @var int idSubtitleFile; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleSerieId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleSerieId.java index 123dcfac..02132a33 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleSerieId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleSerieId.java @@ -2,16 +2,13 @@ import java.io.Serial; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.ProviderSerieId; -import lombok.Getter; - -@Getter public class OpensubtitleSerieId extends ProviderSerieId { - @Serial - private static final long serialVersionUID = 5858875211782260667L; - private final String year; + @Serial private static final long serialVersionUID = 5858875211782260667L; + @val String year; public OpensubtitleSerieId(String name, int id, String year) { super(name, String.valueOf(id)); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java index 3c1f18c9..079e729d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum AiTranslatedEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java index 7a6ba5d0..545b26da 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum ForeignPartsOnlyEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"), ONLY("only"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java index 8586a61e..44d74b55 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum HearingImpairedEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"), ONLY("only"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java index 4795d938..ef98bf0d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java @@ -1,19 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum MachineTranslatedEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"); - private final String value; - + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java index 40ad9842..0731a418 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum MoviehashMatchEnum implements ParamIntf { INCLUDE("include"), ONLY("only"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java index 3076c02c..c248164c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum OrderDirectionEnum implements ParamIntf { ASCENDING("asc"), DESCENDING("desc"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java index e0ef2281..0282aaa9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; +import manifold.ext.props.rt.api.val; + public interface ParamIntf { - String getValue(); + @val String value; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java index 36cd0d61..510e6819 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java @@ -1,11 +1,10 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum SearchSubtitlesEnum { // exclude, include (default: exclude) @@ -80,5 +79,5 @@ public enum SearchSubtitlesEnum { // Filter by movie/episode year YEAR("year"); - private final String paramName; + @val String paramName; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java index a2e7bcbb..ed2292d5 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java @@ -1,18 +1,19 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum TrustedSourcesEnum implements ParamIntf { - INCLUDE("include"), ONLY("only"); + INCLUDE("include"), + ONLY("only"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java index 2cdbfb27..38de4c7d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum TypeEnum implements ParamIntf { MOVIE("movie"), EPISODE("episode"), ALL("all"); - private final String value; + @val @override String value; @Override public String toString() { - return getValue(); + return value; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/JPodnapisiApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/JPodnapisiApi.java index 1b36f7eb..b27fc8b6 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/JPodnapisiApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/JPodnapisiApi.java @@ -12,8 +12,12 @@ import java.util.Optional; import java.util.function.Function; +import lombok.RequiredArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.exception.PodnapisiException; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.model.PodnapisiSubtitleDescriptor; @@ -23,54 +27,50 @@ import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.OptionalExtension; -import org.lodder.subtools.sublibrary.util.StringUtil; import org.lodder.subtools.sublibrary.util.http.HttpClientException; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; - -@Getter(value = AccessLevel.PRIVATE) @RequiredArgsConstructor -@ExtensionMethod({ OptionalExtension.class, StringUtil.class }) public class JPodnapisiApi implements SubtitleApi { - public static final int maxAge = 90; private static final String DOMAIN = "https://www.podnapisi.net"; private final Manager manager; private final String userAgent; private LocalDateTime nextCheck; + @val @override SubtitleSource subtitleSource = SubtitleSource.PODNAPISI; public Optional getPodnapisiShowName(String showName) throws PodnapisiException { String url = DOMAIN + "/sl/ppodnapisi/search?sK=" + showName.trim().toLowerCase().urlEncode(); - return getXml(url).selectFirst(".subtitle-entry") != null + return getXml(url).selectFirstByClass("subtitle-entry") != null ? Optional.of(new ProviderSerieId(showName, showName)) : Optional.empty(); } - public List getMovieSubtitles(String movieName, int year, int season, int episode, Language language) + public List getMovieSubtitles(String movieName, int year, int season, int episode, + Language language) throws PodnapisiException { return getSubtitles(new SerieMapping(movieName, movieName, movieName, season), year, season, episode, language); } - public List getSerieSubtitles(SerieMapping providerSerieId, int season, int episode, Language language) + public List getSerieSubtitles(SerieMapping providerSerieId, int season, int episode, + Language language) throws PodnapisiException { return getSubtitles(providerSerieId, null, season, episode, language); } - private List getSubtitles(SerieMapping providerSerieId, Integer year, int season, int episode, Language language) + private List getSubtitles(SerieMapping providerSerieId, Integer year, int season, + int episode, Language language) throws PodnapisiException { - return getManager().valueBuilder() + return manager.valueBuilder() .memoryCache() - .key("%s-subtitles-%s-%s-%s-%s".formatted(getSubtitleSource().name(), providerSerieId.getProviderId(), season, episode, language)) + .key("%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), providerSerieId.providerId, + season, episode, language)) .collectionSupplier(PodnapisiSubtitleDescriptor.class, () -> { try { - StringBuilder url = new StringBuilder(DOMAIN + "/sl/ppodnapisi/search?sK=") - .append(URLEncoder.encode(providerSerieId.getProviderId().trim().toLowerCase(), StandardCharsets.UTF_8)); + StringBuilder url = new StringBuilder("$DOMAIN/sl/ppodnapisi/search?sK=") + .append(URLEncoder.encode(providerSerieId.providerId.trim().toLowerCase(), + StandardCharsets.UTF_8)); if (PODNAPISI_LANGS.containsKey(language)) { url.append("&sJ=").append(PODNAPISI_LANGS.get(language)); } @@ -87,7 +87,10 @@ private List getSubtitles(SerieMapping providerSeri } url.append("&sXML=1"); - return getXml(url.toString()).select("subtitle").stream().map(this::parsePodnapisiSubtitle).toList(); + return getXml(url.toString()).selectAllByTag("subtitle") + .stream() + .map(this::parsePodnapisiSubtitle) + .toList(); } catch (Exception e) { throw new PodnapisiException(e); } @@ -96,10 +99,11 @@ private List getSubtitles(SerieMapping providerSeri } - protected Document getXml(String url) throws PodnapisiException { + protected @Nullable Document getXml(String url) throws PodnapisiException { try { - return manager.getPageContentBuilder().url(url).userAgent(getUserAgent()).cacheType(CacheType.MEMORY).retries(1) - .retryPredicate(e -> e instanceof HttpClientException httpClientException && httpClientException.getResponseCode() >= 500 + return manager.getPageContentBuilder().url(url).userAgent(userAgent).cacheType(CacheType.MEMORY).retries(1) + .retryPredicate(e -> e instanceof HttpClientException httpClientException && + httpClientException.getResponseCode() >= 500 && httpClientException.getResponseCode() < 600) .retryWait(5).getAsJsoupDocument(); } catch (Exception e) { @@ -110,26 +114,30 @@ protected Document getXml(String url) throws PodnapisiException { private PodnapisiSubtitleDescriptor parsePodnapisiSubtitle(Element elem) { Function getText = e -> e == null ? null : e.text(); return PodnapisiSubtitleDescriptor.builder() - .hearingImpaired(elem.select("new_flags flags").stream().anyMatch(flagElem -> "hearing_impaired".equals(flagElem.text()))) - .language(languageIdToLanguage(elem.selectFirst("languageId").text())) - .releaseString(elem.selectFirst("release").text().length() > 10 ? elem.selectFirst("release").text() - : elem.selectFirst("title").text().replace(":", "") + " " + elem.selectFirst("release").text()) - .uploaderName(elem.selectFirst("uploaderName").text()) - .url(elem.selectFirst("url").text() + "/download?") - .subtitleId(elem.selectFirst("id").text()) + .hearingImpaired(elem.select("new_flags flags") + .stream() + .anyMatch(flagElem -> "hearing_impaired".equals(flagElem.text()))) + .language(languageIdToLanguage(elem.selectFirst("languageId").getText())) + .releaseString( + elem.selectFirst("release").getText().length() > 10 ? elem.selectFirst("release").getText() + : elem.selectFirst("title").getText().replace(":", "") + " " + + elem.selectFirst("release").getText()) + .uploaderName(elem.selectFirst("uploaderName").getText()) + .url(elem.selectFirst("url").getText() + "/download?") + .subtitleId(elem.selectFirst("id").getText()) .year(getText.apply(elem.selectFirst("year"))) .imdb(getText.apply(elem.selectFirst("imdb"))) .omdb(getText.apply(elem.selectFirst("omdb"))) .build(); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.PODNAPISI; - } - private Language languageIdToLanguage(String languageId) { - return PODNAPISI_LANGS.entrySet().stream().filter(entry -> entry.getValue().equals(languageId)).map(Entry::getKey).findFirst().orElse(null); + return PODNAPISI_LANGS.entrySet() + .stream() + .filter(entry -> entry.getValue().equals(languageId)) + .map(Entry::getKey) + .findFirst() + .orElse(null); } private static final Map PODNAPISI_LANGS = Collections diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/exception/PodnapisiException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/exception/PodnapisiException.java index 97d96dd9..578d08bc 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/exception/PodnapisiException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/exception/PodnapisiException.java @@ -2,11 +2,10 @@ import java.io.Serial; +import lombok.experimental.StandardException; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import lombok.experimental.StandardException; - @StandardException public class PodnapisiException extends SubtitlesProviderException { @@ -15,6 +14,6 @@ public class PodnapisiException extends SubtitlesProviderException { @Override public String getSubtitleProvider() { - return SubtitleSource.PODNAPISI.getName(); + return SubtitleSource.PODNAPISI.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleDescriptor.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleDescriptor.java index ada3e7c0..4431b5a2 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleDescriptor.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleDescriptor.java @@ -1,34 +1,27 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.model; -import org.lodder.subtools.sublibrary.Language; - import lombok.Builder; -import lombok.Getter; -import lombok.Setter; +import manifold.ext.props.rt.api.var; +import org.lodder.subtools.sublibrary.Language; /** - * Created by IntelliJ IDEA. - * User: lodder - * Date: 20/08/11 - * Time: 13:44 - * To change this template use Path | Settings | Path Templates. + * Created by IntelliJ IDEA. User: lodder Date: 20/08/11 Time: 13:44 To change this template use Path | Settings | Path + * Templates. */ -@Getter -@Setter @Builder public class PodnapisiSubtitleDescriptor { - private String subtitleId; - private Language language; - private String uploaderName; - // private String uploaderUid; - // private String matchRanking; - private String releaseString; - // private String subtitleRating; - private String url; - private boolean hearingImpaired; - private boolean isInexact; - private String year; - private String omdb; - private String imdb; + @var String subtitleId; + @var Language language; + @var String uploaderName; + // @var String uploaderUid; + // @var String matchRanking; + @var String releaseString; + // @var String subtitleRating; + @var String url; + @var boolean hearingImpaired; + @var boolean isInexact; + @var String year; + @var String omdb; + @var String imdb; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java index 400b85c3..8b5ab732 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java @@ -1,8 +1,6 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.subscene; import java.io.Serial; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -13,11 +11,13 @@ import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; @@ -32,31 +32,28 @@ import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.http.HttpClientException; +import util.Utils; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ OptionalExtension.class }) public class SubsceneApi extends Html implements SubtitleApi { - private static final int RATEDURATION_SHORT = 1; // seconds - private static final int RATEDURATION_LONG = 5; // seconds + private static final int RATE_DURATION_SHORT = 1; // seconds + private static final int RATE_DURATION_LONG = 5; // seconds private static final String DOMAIN = "https://subscene.com"; - // private static final String SERIE_URL_PREFIX = DOMAIN + "/subtitles/"; private static final Pattern SERIE_NAME_PATTERN = Pattern.compile(".*? - ([A-Z][a-z]*) Season.*"); - private static final Predicate RETRY_PREDICATE = - exception -> (exception instanceof HttpClientException httpClientException - && (httpClientException.getResponseCode() == 409 || httpClientException.getResponseCode() == 429)) - || (exception instanceof ManagerException managerException && managerException.getMessage().contains("409 Conflict")); + private static final Predicate RETRY_PREDICATE = exception -> switch (exception) { + case HttpClientException httpClientException -> + httpClientException.getResponseCode() == 409 || httpClientException.getResponseCode() == 429; + case ManagerException managerException -> managerException.getMessage().contains("409 Conflict"); + default -> false; + }; private int selectedLanguage; private boolean selectedIncludeHearingImpaired; private LocalDateTime lastRequest = LocalDateTime.now(); + @val @override SubtitleSource subtitleSource = SubtitleSource.SUBSCENE; public SubsceneApi(Manager manager) { super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); @@ -73,63 +70,78 @@ public Map> getSubSceneSerieNames(String serieName if (StringUtils.isBlank(serieName)) { return Map.of(); } - String url = DOMAIN + "/subtitles/searchbytitle?query=" + URLEncoder.encode(serieName, StandardCharsets.UTF_8); - Element searchResultElement = getJsoupDocument(url).selectFirst(".search-result"); - - return searchResultElement.select("h2").stream() - .map(titleElement -> Pair.of(titleElement.text(), titleElement.nextElementSibling().select("a").stream() - .map(aElem -> { - Matcher matcher = SERIE_NAME_PATTERN.matcher(aElem.text()); + String url = "$DOMAIN/subtitles/searchbytitle?query=" + serieName.urlEncode(); + return getJsoupDocument(url).selectFirstByClass("search-result").selectAllByTag("h2") + .stream() + .collect(Utils.mapCollector((map, titleElement) -> map.put(titleElement.text(), + titleElement.nextElementSibling().selectAllByTag("a").stream().map(elem -> { + Matcher matcher = SERIE_NAME_PATTERN.matcher(elem.text()); int season = 0; if (matcher.matches()) { - season = OrdinalNumber.optionalFromValue(matcher.group(1)).mapToInt(OrdinalNumber::getNumber).orElse(-1); + season = OrdinalNumber.optionalFromValue(matcher.group(1)) + .mapToInt(OrdinalNumber::getNumber).orElse(-1); } - return new SubSceneSerieId(aElem.text(), aElem.attr("href"), season); - }).toList())) - .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + return new SubSceneSerieId(elem.text(), elem.attr("href"), season); + }).toList()))); } catch (Exception e) { throw new SubsceneException(e); } } - public List getSubtitles(SerieMapping providerSerieId, int season, int episode, Language language) - throws SubsceneException { - return getManager().valueBuilder().memoryCache() - .key("%s-subtitles-%s-%s-%s-%s".formatted(getSubtitleSource().getName(), providerSerieId.getProviderId(), season, episode, language)) + public List getSubtitles(SerieMapping providerSerieId, int season, int episode, + Language language) throws SubsceneException { + return manager.valueBuilder() + .memoryCache() + .key("%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name, providerSerieId.providerId, season, + episode, language)) .collectionSupplier(SubsceneSubtitleDescriptor.class, () -> { setLanguageWithCookie(language); try { - return getJsoupDocument(DOMAIN + providerSerieId.getProviderId()) - .select("td.a1").stream().map(Element::parent) - .map(row -> new SubsceneSubtitleDescriptor() - .setLanguage(Language.fromValueOptional(row.select(".a1 span.l").text().trim()).orElse(null)) - .setUrlSupplier(() -> getDownloadUrl(DOMAIN + row.select(".a1 > a").attr("href").trim())) - .setName(row.select(".a1 span:not(.l)").text().trim()) - .setHearingImpaired(!row.select(".a41").isEmpty()) - .setUploader(row.select(".a5 > a").text().trim()) - .setComment(row.select(".a6 > div").text().trim())) - .filter(subDescriptor -> subDescriptor.getSeasonEpisode() != null - && subDescriptor.getSeasonEpisode().getEpisodes().stream().anyMatch(ep -> ep == episode)) + return getJsoupDocument(DOMAIN + providerSerieId.providerId) + .selectAllByCss("td.a1") + .stream() + .map(Element::parent) + .map(row -> new SubsceneSubtitleDescriptor().setLanguage( + Language.fromValueOptional(row.selectAllByCss(".a1 span.l").getText().trim()) + .orElse(null)) + .setUrlSupplier(() -> getDownloadUrl( + DOMAIN + row.selectAllByCss(".a1 > a").getAttr("href").trim())) + .setName(row.selectAllByCss(".a1 span:not(.l)").getText().trim()) + .setHearingImpaired(row.selectFirstByCss(".a41") != null) + .setUploader(row.selectFirstByCss(".a5 > a").getText().trim()) + .setComment(row.selectFirstByCss(".a6 > div").getText().trim())) + .filter(subDescriptor -> subDescriptor.getSeasonEpisode() != null && + subDescriptor.getSeasonEpisode().episodes.stream() + .anyMatch(ep -> ep == episode)) .toList(); } catch (Exception e) { throw new SubsceneException(e); } - }).getCollection(); + }) + .getCollection(); } private String getDownloadUrl(String seriePageUrl) throws SubsceneException { try { - return DOMAIN + getJsoupDocument(seriePageUrl).selectFirst("#downloadButton").attr("href"); + String href = getJsoupDocument(seriePageUrl).selectFirstById("downloadButton").getAttr("href"); + if (StringUtils.isBlank(href)) { + throw new SubsceneException("href for $seriePageUrl is blank"); + } + return DOMAIN + href; } catch (ManagerException e) { throw new SubsceneException(e); } } private Document getJsoupDocument(String url) throws ManagerException { - while (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATEDURATION_SHORT) { + while (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATE_DURATION_SHORT) { sleepSeconds(1); } - Document document = super.getHtml(url).retries(1).retryPredicate(RETRY_PREDICATE).retryWait(RATEDURATION_LONG).getAsJsoupDocument(); + Document document = super.getHtml(url) + .retries(1) + .retryPredicate(RETRY_PREDICATE) + .retryWait(RATE_DURATION_LONG) + .getAsJsoupDocument(); lastRequest = LocalDateTime.now(); return document; } @@ -156,210 +168,138 @@ private void setIncludeHearingImpairedWithCookie(boolean includeHearingImpaired) } private void addCookie(String cookieName, String cookieValue) { - getManager().storeCookies("subscene.com", Map.of(cookieName, cookieValue)); + manager.storeCookies("subscene.com", Map.of(cookieName, cookieValue)); } - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.SUBSCENE; - } + private static final Map SUBSCENE_LANGS = + Collections.unmodifiableMap(new EnumMap<>(Language.class) { + @Serial private static final long serialVersionUID = 2950169212654074275L; - private static final Map SUBSCENE_LANGS = Collections.unmodifiableMap(new EnumMap<>(Language.class) { - @Serial - private static final long serialVersionUID = 2950169212654074275L; + { + put(Language.ARABIC, 2); + put(Language.BENGALI, 54); + put(Language.PORTUGUESE, 4); // BRAZILLIAN PORTUGUESE + put(Language.CHINESE_SIMPLIFIED, 7); + put(Language.CZECH, 9); + put(Language.DANISH, 10); + put(Language.DUTCH, 11); + put(Language.ENGLISH, 13); + // put(Language.FARSI / PERSIAN, 46); + put(Language.FINNISH, 17); + put(Language.FRENCH, 18); + put(Language.GERMAN, 19); + put(Language.GREEK, 21); + put(Language.HEBREW, 22); + put(Language.INDONESIAN, 44); + put(Language.ITALIAN, 26); + put(Language.KOREAN, 28); + put(Language.MALAY, 50); + put(Language.NORWEGIAN, 30); + put(Language.POLISH, 31); + put(Language.PORTUGUESE, 32); + put(Language.ROMANIAN, 33); + put(Language.SPANISH, 38); + put(Language.SWEDISH, 39); + put(Language.THAI, 40); + put(Language.TURKISH, 41); + put(Language.VIETNAMESE, 45); + put(Language.ALBANIAN, 1); + put(Language.ARMENIAN, 73); + put(Language.AZERBAIJANI, 55); + // put(Language.BASQUE, 74); + put(Language.BELARUSIAN, 68); + put(Language.CHINESE_SIMPLIFIED, 3); // BIG 5 CODE + put(Language.BOSNIAN, 60); + put(Language.BULGARIAN, 5); + // put(Language.BULGARIAN / ENGLISH, 6); + // put(Language.BURMESE, 61); + // put(Language.CAMBODIAN / KHMER, 79); + put(Language.CATALAN, 49); + put(Language.CROATIAN, 8); + // put(Language.DUTCH / ENGLISH, 12); + // put(Language.ENGLISH / GERMAN, 15); + // put(Language.ESPERANTO, 47); + put(Language.ESTONIAN, 16); + // put(Language.GEORGIAN, 62); + // put(Language.GREENLANDIC, 57); + put(Language.HINDI, 51); + put(Language.HUNGARIAN, 23); + // put(Language.HUNGARIAN / ENGLISH, 24); + put(Language.ICELANDIC, 25); + put(Language.JAPANESE, 27); + put(Language.KANNADA, 78); + // put(Language.KINYARWANDA, 81); + // put(Language.KURDISH, 52); + put(Language.LATVIAN, 29); + put(Language.LITHUANIAN, 43); + put(Language.MACEDONIAN, 48); + put(Language.MALAYALAM, 64); + // put(Language.MANIPURI, 65); + // put(Language.MONGOLIAN, 72); + // put(Language.NEPALI, 80); + // put(Language.PASHTO, 67); + // put(Language.PUNJABI, 66); + put(Language.RUSSIAN, 34); + put(Language.SERBIAN, 35); + put(Language.SINHALA, 58); + put(Language.SLOVAK, 36); + put(Language.SLOVENIAN, 37); + // put(Language.SOMALI, 70); + // put(Language.SUNDANESE, 76); + // put(Language.SWAHILI, 75); + put(Language.TAGALOG, 53); + put(Language.TAMIL, 59); + put(Language.TELUGU, 63); + put(Language.UKRAINIAN, 56); + // put(Language.URDU, 42); + // put(Language.YORUBA, 71); - { - put(Language.ARABIC, 2); - put(Language.BENGALI, 54); - put(Language.PORTUGUESE, 4); // BRAZILLIAN PORTUGUESE - put(Language.CHINESE_SIMPLIFIED, 7); - put(Language.CZECH, 9); - put(Language.DANISH, 10); - put(Language.DUTCH, 11); - put(Language.ENGLISH, 13); - // put(Language.FARSI / PERSIAN, 46); - put(Language.FINNISH, 17); - put(Language.FRENCH, 18); - put(Language.GERMAN, 19); - put(Language.GREEK, 21); - put(Language.HEBREW, 22); - put(Language.INDONESIAN, 44); - put(Language.ITALIAN, 26); - put(Language.KOREAN, 28); - put(Language.MALAY, 50); - put(Language.NORWEGIAN, 30); - put(Language.POLISH, 31); - put(Language.PORTUGUESE, 32); - put(Language.ROMANIAN, 33); - put(Language.SPANISH, 38); - put(Language.SWEDISH, 39); - put(Language.THAI, 40); - put(Language.TURKISH, 41); - put(Language.VIETNAMESE, 45); - put(Language.ALBANIAN, 1); - put(Language.ARMENIAN, 73); - put(Language.AZERBAIJANI, 55); - // put(Language.BASQUE, 74); - put(Language.BELARUSIAN, 68); - put(Language.CHINESE_SIMPLIFIED, 3); // BIG 5 CODE - put(Language.BOSNIAN, 60); - put(Language.BULGARIAN, 5); - // put(Language.BULGARIAN / ENGLISH, 6); - // put(Language.BURMESE, 61); - // put(Language.CAMBODIAN / KHMER, 79); - put(Language.CATALAN, 49); - put(Language.CROATIAN, 8); - // put(Language.DUTCH / ENGLISH, 12); - // put(Language.ENGLISH / GERMAN, 15); - // put(Language.ESPERANTO, 47); - put(Language.ESTONIAN, 16); - // put(Language.GEORGIAN, 62); - // put(Language.GREENLANDIC, 57); - put(Language.HINDI, 51); - put(Language.HUNGARIAN, 23); - // put(Language.HUNGARIAN / ENGLISH, 24); - put(Language.ICELANDIC, 25); - put(Language.JAPANESE, 27); - put(Language.KANNADA, 78); - // put(Language.KINYARWANDA, 81); - // put(Language.KURDISH, 52); - put(Language.LATVIAN, 29); - put(Language.LITHUANIAN, 43); - put(Language.MACEDONIAN, 48); - put(Language.MALAYALAM, 64); - // put(Language.MANIPURI, 65); - // put(Language.MONGOLIAN, 72); - // put(Language.NEPALI, 80); - // put(Language.PASHTO, 67); - // put(Language.PUNJABI, 66); - put(Language.RUSSIAN, 34); - put(Language.SERBIAN, 35); - put(Language.SINHALA, 58); - put(Language.SLOVAK, 36); - put(Language.SLOVENIAN, 37); - // put(Language.SOMALI, 70); - // put(Language.SUNDANESE, 76); - // put(Language.SWAHILI, 75); - put(Language.TAGALOG, 53); - put(Language.TAMIL, 59); - put(Language.TELUGU, 63); - put(Language.UKRAINIAN, 56); - // put(Language.URDU, 42); - // put(Language.YORUBA, 71); - - } - }); + } + }); @Getter @RequiredArgsConstructor private enum OrdinalNumber { - ZEROTH(0, "Zeroth"), - FIRST(1, "First"), - SECOND(2, "Second"), - THIRD(3, "Third"), - FOURTH(4, "Fourth"), - FIFTH(5, "Fifth"), - SIXTH(6, "Sixth"), - SEVENTH(7, "Seventh"), - EIGHTH(8, "Eighth"), - NINTH(9, "Ninth"), - TENTH(10, "Tenth"), - ELEVENTH(11, "Eleventh"), - TWELFTH(12, "Twelfth"), - THIRTEENTH(13, "Thirteenth"), - FOURTEENTH(14, "Fourteenth"), - FIFTEENTH(15, "Fifteenth"), - SIXTEENTH(16, "Sixteenth"), - SEVENTEENTH(17, "Seventeenth"), - EIGHTEENTH(18, "Eighteenth"), - NINETEENTH(19, "Nineteenth"), - TWENTIETH(20, "Twentieth"), - TWENTY_FIRST(21, "Twenty-First"), - TWENTY_SECOND(22, "Twenty-Second"), - TWENTY_THIRD(23, "Twenty-Third"), - TWENTY_FOURTH(24, "Twenty-Fourth"), - TWENTY_FIFTH(25, "Twenty-Fifth"), - TWENTY_SIXTH(26, "Twenty-Sixth"), - TWENTY_SEVENTH(27, "Twenty-Seventh"), - TWENTY_EIGHTH(28, "Twenty-Eighth"), - TWENTY_NINTH(29, "Twenty-Ninth"), - THIRTIETH(30, "Thirtieth"), - THIRTHY_FIRST(31, "Thirty-First"), - THIRTHY_SECOND(32, "Thirty-Second"), - THIRTHY_THIRD(33, "Thirty-Third"), - THIRTHY_FOURTH(34, "Thirty-Fourth"), - THIRTHY_FIFTH(35, "Thirty-Fifth"), - THIRTHY_SIXTH(36, "Thirty-Sixth"), - THIRTHY_SEVENTH(37, "Thirty-Seventh"), - THIRTHY_EIGHTH(38, "Thirty-Eighth"), - THIRTHY_NINTH(39, "Thirty-Ninth"), - FORTIETH(40, "Fortieth"), - FORTY_FIRST(41, "Forty-First"), - FORTY_SECOND(42, "Forty-Second"), - FORTY_THIRD(43, "Forty-Third"), - FORTY_FOURTH(44, "Forty-Fourth"), - FORTY_FIFTH(45, "Forty-Fifth"), - FORTY_SIXTH(46, "Forty-Sixth"), - FORTY_SEVENTH(47, "Forty-Seventh"), - FORTY_EIGHTH(48, "Forty-Eighth"), - FORTY_NINTH(49, "Forty-Ninth"), - FIFTIETH(50, "Fiftieth"), - FIFTY_FIRST(51, "Fifty-First"), - FIFTY_SECOND(52, "Fifty-Second"), - FIFTY_THIRD(53, "Fifty-Third"), - FIFTY_FOURTH(54, "Fifty-Fourth"), - FIFTY_FIFTH(55, "Fifty-Fifth"), - FIFTY_SIXTH(56, "Fifty-Sixth"), - FIFTY_SEVENTH(57, "Fifty-Seventh"), - FIFTY_EIGHTH(58, "Fifty-Eighth"), - FIFTY_NINTH(59, "Fifty-Ninth"), - SIXTIETH(60, "Sixtieth"), - SIXTY_FIRST(61, "Sixty-First"), - SIXTY_SECOND(62, "Sixty-Second"), - SIXTY_THIRD(63, "Sixty-Third"), - SIXTY_FOURTH(64, "Sixty-Fourth"), - SIXTY_FIFTH(65, "Sixty-Fifth"), - SIXTY_SIXTH(66, "Sixty-Sixth"), - SIXTY_SEVENTH(67, "Sixty-Seventh"), - SIXTY_EIGHTH(68, "Sixty-Eighth"), - SIXTY_NINTH(69, "Sixty-Ninth"), - SEVENTIETH(70, "Seventieth"), - SEVENTY_FIRST(71, "Seventy-First"), - SEVENTY_SECOND(72, "Seventy-Second"), - SEVENTY_THIRD(73, "Seventy-Third"), - SEVENTY_FOURTH(74, "Seventy-Fourth"), - SEVENTY_FIFTH(75, "Seventy-Fifth"), - SEVENTY_SIXTH(76, "Seventy-Sixth"), - SEVENTY_SEVENTH(77, "Seventy-Seventh"), - SEVENTY_EIGHTH(78, "Seventy-Eighth"), - SEVENTY_NINTH(79, "Seventy-Ninth"), - EIGHTIETH(80, "Eightieth"), - EIGHTY_FIRST(81, "Eighty-First"), - EIGHTY_SECOND(82, "Eighty-Second"), - EIGHTY_THIRD(83, "Eighty-Third"), - EIGHTY_FOURTH(84, "Eighty-Fourth"), - EIGHTY_FIFTH(85, "Eighty-Fifth"), - EIGHTY_SIXTH(86, "Eighty-Sixth"), - EIGHTY_SEVENTH(87, "Eighty-Seventh"), - EIGHTY_EIGHTH(88, "Eighty-Eighth"), - EIGHTY_NINTH(89, "Eighty-Ninth"), - NINETIETH(90, "Ninetieth"), - NINETY_FIRST(91, "Ninety-First"), - NINETY_SECOND(92, "Ninety-Second"), - NINETY_THIRD(93, "Ninety-Third"), - NINETY_FOURTH(94, "Ninety-Fourth"), - NINETY_FIFTH(95, "Ninety-Fifth"), - NINETY_SIXTH(96, "Ninety-Sixth"), - NINETY_SEVENTH(97, "Ninety-Seventh"), - NINETY_EIGHTH(98, "Ninety-Eighth"), - NINETY_NINTH(99, "Ninety-Ninth"), + ZEROTH(0, "Zeroth"), FIRST(1, "First"), SECOND(2, "Second"), THIRD(3, "Third"), FOURTH(4, "Fourth"), + FIFTH(5, "Fifth"), SIXTH(6, "Sixth"), SEVENTH(7, "Seventh"), EIGHTH(8, "Eighth"), NINTH(9, "Ninth"), + TENTH(10, "Tenth"), ELEVENTH(11, "Eleventh"), TWELFTH(12, "Twelfth"), THIRTEENTH(13, "Thirteenth"), + FOURTEENTH(14, "Fourteenth"), FIFTEENTH(15, "Fifteenth"), SIXTEENTH(16, "Sixteenth"), + SEVENTEENTH(17, "Seventeenth"), EIGHTEENTH(18, "Eighteenth"), NINETEENTH(19, "Nineteenth"), + TWENTIETH(20, "Twentieth"), TWENTY_FIRST(21, "Twenty-First"), TWENTY_SECOND(22, "Twenty-Second"), + TWENTY_THIRD(23, "Twenty-Third"), TWENTY_FOURTH(24, "Twenty-Fourth"), TWENTY_FIFTH(25, "Twenty-Fifth"), + TWENTY_SIXTH(26, "Twenty-Sixth"), TWENTY_SEVENTH(27, "Twenty-Seventh"), TWENTY_EIGHTH(28, "Twenty-Eighth"), + TWENTY_NINTH(29, "Twenty-Ninth"), THIRTIETH(30, "Thirtieth"), THIRTHY_FIRST(31, "Thirty-First"), + THIRTHY_SECOND(32, "Thirty-Second"), THIRTHY_THIRD(33, "Thirty-Third"), THIRTHY_FOURTH(34, "Thirty-Fourth"), + THIRTHY_FIFTH(35, "Thirty-Fifth"), THIRTHY_SIXTH(36, "Thirty-Sixth"), THIRTHY_SEVENTH(37, "Thirty-Seventh"), + THIRTHY_EIGHTH(38, "Thirty-Eighth"), THIRTHY_NINTH(39, "Thirty-Ninth"), FORTIETH(40, "Fortieth"), + FORTY_FIRST(41, "Forty-First"), FORTY_SECOND(42, "Forty-Second"), FORTY_THIRD(43, "Forty-Third"), + FORTY_FOURTH(44, "Forty-Fourth"), FORTY_FIFTH(45, "Forty-Fifth"), FORTY_SIXTH(46, "Forty-Sixth"), + FORTY_SEVENTH(47, "Forty-Seventh"), FORTY_EIGHTH(48, "Forty-Eighth"), FORTY_NINTH(49, "Forty-Ninth"), + FIFTIETH(50, "Fiftieth"), FIFTY_FIRST(51, "Fifty-First"), FIFTY_SECOND(52, "Fifty-Second"), + FIFTY_THIRD(53, "Fifty-Third"), FIFTY_FOURTH(54, "Fifty-Fourth"), FIFTY_FIFTH(55, "Fifty-Fifth"), + FIFTY_SIXTH(56, "Fifty-Sixth"), FIFTY_SEVENTH(57, "Fifty-Seventh"), FIFTY_EIGHTH(58, "Fifty-Eighth"), + FIFTY_NINTH(59, "Fifty-Ninth"), SIXTIETH(60, "Sixtieth"), SIXTY_FIRST(61, "Sixty-First"), + SIXTY_SECOND(62, "Sixty-Second"), SIXTY_THIRD(63, "Sixty-Third"), SIXTY_FOURTH(64, "Sixty-Fourth"), + SIXTY_FIFTH(65, "Sixty-Fifth"), SIXTY_SIXTH(66, "Sixty-Sixth"), SIXTY_SEVENTH(67, "Sixty-Seventh"), + SIXTY_EIGHTH(68, "Sixty-Eighth"), SIXTY_NINTH(69, "Sixty-Ninth"), SEVENTIETH(70, "Seventieth"), + SEVENTY_FIRST(71, "Seventy-First"), SEVENTY_SECOND(72, "Seventy-Second"), SEVENTY_THIRD(73, "Seventy-Third"), + SEVENTY_FOURTH(74, "Seventy-Fourth"), SEVENTY_FIFTH(75, "Seventy-Fifth"), SEVENTY_SIXTH(76, "Seventy-Sixth"), + SEVENTY_SEVENTH(77, "Seventy-Seventh"), SEVENTY_EIGHTH(78, "Seventy-Eighth"), + SEVENTY_NINTH(79, "Seventy-Ninth"), EIGHTIETH(80, "Eightieth"), EIGHTY_FIRST(81, "Eighty-First"), + EIGHTY_SECOND(82, "Eighty-Second"), EIGHTY_THIRD(83, "Eighty-Third"), EIGHTY_FOURTH(84, "Eighty-Fourth"), + EIGHTY_FIFTH(85, "Eighty-Fifth"), EIGHTY_SIXTH(86, "Eighty-Sixth"), EIGHTY_SEVENTH(87, "Eighty-Seventh"), + EIGHTY_EIGHTH(88, "Eighty-Eighth"), EIGHTY_NINTH(89, "Eighty-Ninth"), NINETIETH(90, "Ninetieth"), + NINETY_FIRST(91, "Ninety-First"), NINETY_SECOND(92, "Ninety-Second"), NINETY_THIRD(93, "Ninety-Third"), + NINETY_FOURTH(94, "Ninety-Fourth"), NINETY_FIFTH(95, "Ninety-Fifth"), NINETY_SIXTH(96, "Ninety-Sixth"), + NINETY_SEVENTH(97, "Ninety-Seventh"), NINETY_EIGHTH(98, "Ninety-Eighth"), NINETY_NINTH(99, "Ninety-Ninth"), HUNDREDTH(100, "Hundredth"); private final int number; private final String value; public static Optional optionalFromValue(String value) { - return Stream.of(OrdinalNumber.values()).filter(ordinalNumber -> StringUtils.equalsIgnoreCase(value, ordinalNumber.getValue())) + return Stream.of(OrdinalNumber.values()) + .filter(ordinalNumber -> StringUtils.equalsIgnoreCase(value, ordinalNumber.getValue())) .findAny(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java index 4f3d97fd..ddb91b2c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java @@ -2,11 +2,10 @@ import java.io.Serial; +import lombok.experimental.StandardException; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import lombok.experimental.StandardException; - @StandardException public class SubsceneException extends SubtitlesProviderException { @@ -15,6 +14,6 @@ public class SubsceneException extends SubtitlesProviderException { @Override public String getSubtitleProvider() { - return SubtitleSource.SUBSCENE.getName(); + return SubtitleSource.SUBSCENE.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java index 3ee49710..a452dc4d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java @@ -2,16 +2,15 @@ import java.io.Serial; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.ProviderSerieId; -import lombok.Getter; - -@Getter public class SubSceneSerieId extends ProviderSerieId { @Serial private static final long serialVersionUID = 5858875211782260667L; - private final int season; + + @val int season; public SubSceneSerieId(String name, String id, int season) { super(name, id); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleDescriptor.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleDescriptor.java index a0026048..fe0ecaa6 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleDescriptor.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleDescriptor.java @@ -1,16 +1,14 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.model; -import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.exception.SubsceneException; -import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.model.SeasonEpisode; - import com.pivovarit.function.ThrowingSupplier; - import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.exception.SubsceneException; +import org.lodder.subtools.sublibrary.Language; +import org.lodder.subtools.sublibrary.model.SeasonEpisode; @EqualsAndHashCode @Accessors(chain = true) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/JTVSubtitlesApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/JTVSubtitlesApi.java index 5e4ff030..ef9311dc 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/JTVSubtitlesApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/JTVSubtitlesApi.java @@ -9,6 +9,8 @@ import java.util.function.BiPredicate; import java.util.function.Function; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -23,16 +25,12 @@ import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.OptionalExtension; -import org.lodder.subtools.sublibrary.util.StreamExtension; -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ OptionalExtension.class, StreamExtension.class }) public class JTVSubtitlesApi extends Html implements SubtitleApi { private static final String DOMAIN = "https://www.tvsubtitles.net"; private static final String SERIE_URL_PREFIX = DOMAIN + "/"; + @val @override SubtitleSource subtitleSource = SubtitleSource.TVSUBTITLES; public JTVSubtitlesApi(Manager manager) { super(manager); @@ -40,76 +38,85 @@ public JTVSubtitlesApi(Manager manager) { public List getUrisForSerieName(String serieName) throws TvSubtitlesException { try { - return getManager().postBuilder() + return manager.postBuilder() .url(DOMAIN + "/search.php") .addData("qs", serieName) .postAsJsoupDocument() - .select(".left_articles > ul > li a").stream() - .map(element -> new ProviderSerieId(element.text(), StringUtils.substringAfterLast(element.attr("href"), "/"))) + .select(".left_articles > ul > li a") + .stream() + .map(element -> new ProviderSerieId(element.text(), + StringUtils.substringAfterLast(element.attr("href"), "/"))) .toList(); } catch (Exception e) { throw new TvSubtitlesException(e); } } - public Set getSubtitles(SerieMapping providerSerieId, int season, int episode, Language language) - throws TvSubtitlesException { - return getEpisodeUrl(SERIE_URL_PREFIX + providerSerieId.getProviderId(), season, episode) - .mapToObj(episodeUrl -> getSubtitles(episodeUrl, language)) - .orElseGet(Set::of); + public Set getSubtitles(SerieMapping providerSerieId, int season, int episode, + Language language) throws TvSubtitlesException { + return getEpisodeUrl(SERIE_URL_PREFIX + providerSerieId.providerId, season, episode).mapThrowing( + (String episodeUrl) -> getSubtitles(episodeUrl, language)).orElseGet(Set::of); } - private Set getSubtitles(String episodeUrl, Language language) throws TvSubtitlesException { - return getManager().valueBuilder() + private Set getSubtitles(String episodeUrl, Language language) + throws TvSubtitlesException { + return manager.valueBuilder() .memoryCache() - .key("%s-subtitles-%s-%s".formatted(getSubtitleSource().name(), episodeUrl, language)) + .key("%s-subtitles-%s-%s".formatted(subtitleSource.name(), episodeUrl, language)) .collectionSupplier(TVsubtitlesSubtitleDescriptor.class, () -> { Set lSubtitles = new HashSet<>(); try { - Document searchEpisodeDoc = - this.getHtml(episodeUrl.replace(".html", "-" + language.getLangCode() + ".html")).cacheType(CacheType.NONE) - .getAsJsoupDocument(); - Elements searchEpisodes = searchEpisodeDoc.select(".left_articles > a"); - + Elements searchEpisodes = + this.getHtml(episodeUrl.replace(".html", "-" + language.langCode + ".html")) + .cacheType(CacheType.NONE) + .getAsJsoupDocument() + .selectAllByCss(".left_articles > a"); BiPredicate isRowWithText = (row, text) -> row.get(1).text().contains(text); Function getRowValue = row -> row.get(2).text(); for (Element ep : searchEpisodes) { String url = ep.attr("href"); - if (url.contains("subtitle-")) { - Document subtitlePageDoc = this.getHtml(DOMAIN + url).cacheType(CacheType.NONE).getAsJsoupDocument(); - String filename = null, rip = null, title = null, author = null; - Elements subtitlePageTableDoc = subtitlePageDoc.getElementsByClass("subtitle1"); - if (subtitlePageTableDoc.size() == 1) { - for (Element item : subtitlePageTableDoc.get(0).getElementsByTag("tr")) { - Elements row = item.getElementsByTag("td"); - if (row.size() != 3) { - continue; - } - if (isRowWithText.test(row, "episode title:")) { - title = getRowValue.apply(row); - } else if (isRowWithText.test(row, "filename:")) { - filename = getRowValue.apply(row); - } else if (isRowWithText.test(row, "rip:")) { - rip = getRowValue.apply(row); - } else if (isRowWithText.test(row, "author:")) { - author = getRowValue.apply(row); - } - if (filename != null && rip != null) { - TVsubtitlesSubtitleDescriptor sub = TVsubtitlesSubtitleDescriptor.builder() - .filename(filename) - .url(DOMAIN + "/files/" + URLEncoder.encode( - filename.replace(title + ".", "").replace(".srt", ".zip").replace(" - ", "_"), - StandardCharsets.UTF_8)) - .rip(rip) - .author(author) - .build(); - lSubtitles.add(sub); - rip = null; - filename = null; - title = null; - author = null; - } - } + if (!url.contains("subtitle-")) { + continue; + } + Document subtitlePageDoc = + this.getHtml(DOMAIN + url).cacheType(CacheType.NONE).getAsJsoupDocument(); + String filename = null; + String rip = null; + String title = null; + String author = null; + Elements subtitlePageTableDoc = subtitlePageDoc.selectAllByClass("subtitle1"); + if (subtitlePageTableDoc.getSize() != 1) { + continue; + } + for (Element item : subtitlePageTableDoc.getFirstElement().selectAllByTag("tr")) { + Elements row = item.getElementsByTag("td"); + if (row.size() != 3) { + continue; + } + if (isRowWithText.test(row, "episode title:")) { + title = getRowValue.apply(row); + } else if (isRowWithText.test(row, "filename:")) { + filename = getRowValue.apply(row); + } else if (isRowWithText.test(row, "rip:")) { + rip = getRowValue.apply(row); + } else if (isRowWithText.test(row, "author:")) { + author = getRowValue.apply(row); + } + if (filename != null && rip != null) { + TVsubtitlesSubtitleDescriptor sub = TVsubtitlesSubtitleDescriptor.builder() + .filename(filename) + .url("$DOMAIN/files/" + URLEncoder.encode( + filename.replace(title + ".", "") + .replace(".srt", ".zip") + .replace(" - ", "_"), StandardCharsets.UTF_8)) + .rip(rip) + .author(author) + .build(); + lSubtitles.add(sub); + rip = null; + filename = null; + title = null; + author = null; } } } @@ -122,27 +129,29 @@ private Set getSubtitles(String episodeUrl, Langu } private Optional getEpisodeUrl(String showUrl, int season, int episode) throws TvSubtitlesException { - return getManager().valueBuilder() + return manager.valueBuilder() .memoryCache() - .key("%s-episodeUrl-%s-%s-%s".formatted(getSubtitleSource().name(), showUrl, season, episode)) + .key("%s-episodeUrl-%s-%s-%s".formatted(subtitleSource.name(), showUrl, season, episode)) .optionalSupplier(() -> { try { - String formattedSeasonEpisode = season + "x" + (episode < 10 ? "0" + episode : String.valueOf(episode)); - return getHtml(showUrl.replace(".html", "-" + season + ".html")) + String formattedSeasonEpisode = + season + "x" + (episode < 10 ? "0" + episode : String.valueOf(episode)); + return getHtml(showUrl.replace(".html", "-$season.html")) .getAsJsoupDocument() - .getElementById("table5").getElementsByTag("tr").stream().skip(1) + .selectFirstById("table5") + .selectAllByTag("tr") + .stream() + .skip(1) .filter(row -> Optional.ofNullable(row.selectFirst("td")) .map(element -> formattedSeasonEpisode.equals(element.text())) .orElse(false)) - .map(element -> DOMAIN + "/" + element.select("td").get(1).selectFirst("a").attr("href")).findAny(); + .map(element -> DOMAIN + "/" + + element.selectNthByTag("td", 2).selectFirstByTag("a").getAttr("href")) + .findAny(); } catch (Exception e) { throw new TvSubtitlesException(e); } - }).getOptional(); - } - - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.TVSUBTITLES; + }) + .getOptional(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitlesException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitlesException.java index 4cf4df61..4d0e5cfc 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitlesException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitlesException.java @@ -2,11 +2,10 @@ import java.io.Serial; +import lombok.experimental.StandardException; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import lombok.experimental.StandardException; - @StandardException public class TvSubtitlesException extends SubtitlesProviderException { @@ -15,6 +14,6 @@ public class TvSubtitlesException extends SubtitlesProviderException { @Override public String getSubtitleProvider() { - return SubtitleSource.TVSUBTITLES.getName(); + return SubtitleSource.TVSUBTITLES.name; } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVsubtitlesSubtitleDescriptor.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVsubtitlesSubtitleDescriptor.java index 679728e8..2d65efff 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVsubtitlesSubtitleDescriptor.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVsubtitlesSubtitleDescriptor.java @@ -4,17 +4,16 @@ import java.io.Serializable; import lombok.Builder; -import lombok.Getter; +import manifold.ext.props.rt.api.val; -@Getter @Builder public class TVsubtitlesSubtitleDescriptor implements Serializable { @Serial private static final long serialVersionUID = 6423513286301479905L; - private final String filename; - private final String url; - private final String rip; - private final String author; + @val String filename; + @val String url; + @val String rip; + @val String author; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/CLIExtension.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/CLIExtension.java deleted file mode 100644 index bacc0eab..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/CLIExtension.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.lodder.subtools.multisubdownloader.util; - -import org.apache.commons.cli.CommandLine; -import org.lodder.subtools.multisubdownloader.cli.CliOption; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class CLIExtension { - public static boolean hasCliOption(CommandLine line, CliOption cliOption) { - return line.hasOption(cliOption.getValue()); - } - - public static String getCliOptionValue(CommandLine line, CliOption cliOption) { - return line.getOptionValue(cliOption.getValue()); - } -} \ No newline at end of file diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java index a3cbcaeb..725f3e60 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.util; +import javax.swing.*; +import java.awt.*; import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; @@ -10,8 +12,14 @@ import java.util.prefs.BackingStoreException; import java.util.prefs.InvalidPreferencesFormatException; -import javax.swing.JFileChooser; - +import com.google.gson.GsonBuilder; +import io.gsonfire.GsonFireBuilder; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.experimental.StandardException; +import lombok.experimental.UtilityClass; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.gui.dialog.MappingEpisodeNameDialog.MappingType; @@ -20,24 +28,10 @@ import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler.MessageSeverity; -import org.lodder.subtools.sublibrary.util.StreamExtension; import org.lodder.subtools.sublibrary.util.filefilter.ExtensionFileFilter; import org.lodder.subtools.sublibrary.util.filefilter.JsonFileFilter; import org.lodder.subtools.sublibrary.util.filefilter.XmlFileFilter; -import com.google.gson.GsonBuilder; - -import java.awt.Component; - -import io.gsonfire.GsonFireBuilder; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; -import lombok.experimental.StandardException; -import lombok.experimental.UtilityClass; - @RequiredArgsConstructor public class ExportImport { @@ -46,54 +40,49 @@ public class ExportImport { private final UserInteractionHandler userInteractionHandler; private final Component parent; - @RequiredArgsConstructor - @Getter + @AllArgsConstructor public enum SettingsType { - PREFERENCES(FileType.XML), - SERIE_MAPPING(FileType.JSON); + PREFERENCES(FileType.XML), SERIE_MAPPING(FileType.JSON); - private final FileType fileType; + @val FileType fileType; } - @RequiredArgsConstructor - @Getter + @AllArgsConstructor private enum FileType { - XML(".xml", new XmlFileFilter()), - JSON(".json", new JsonFileFilter()); + XML(".xml", new XmlFileFilter()), JSON(".json", new JsonFileFilter()); - private final String extension; - private final ExtensionFileFilter fileFilter; + @val String extension; + @val ExtensionFileFilter fileFilter; } public void importSettings(SettingsType listType) { - chooseFile(listType.getFileType()).ifPresent(path -> { + chooseFile(listType.fileType).ifPresent(path -> { if (Files.notExists(path)) { - userInteractionHandler.showMessage(Messages.getString("ImportExport.FileDoesNotExist"), - Messages.getString("ImportExport.ErrorWhileImporting"), MessageSeverity.WARNING); + userInteractionHandler.showMessage(Messages.getText("ImportExport.FileDoesNotExist"), + Messages.getText("ImportExport.ErrorWhileImporting"), MessageSeverity.WARNING); return; } try { switch (listType) { - case PREFERENCES -> ExportImportPreferences.importSettings(path, userInteractionHandler, settingsControl); - case SERIE_MAPPING -> ExportImportSerieMapping.importSettings(path, userInteractionHandler, manager); + case PREFERENCES -> + ExportImportPreferences.importSettings(path, userInteractionHandler, settingsControl); + case SERIE_MAPPING -> + ExportImportSerieMapping.importSettings(path, userInteractionHandler, manager); default -> throw new IllegalArgumentException("Unexpected value: " + listType); } } catch (CorruptSettingsFileException e) { - userInteractionHandler.showMessage( - Messages.getString("ImportExport.ImportCorruptFile"), - Messages.getString("ImportExport.ErrorWhileImporting"), - MessageSeverity.ERROR); + userInteractionHandler.showMessage(Messages.getText("ImportExport.ImportCorruptFile"), + Messages.getText("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); } catch (Exception e) { - userInteractionHandler.showMessage(Messages.getString("ImportExport.ErrorWhileImporting"), - Messages.getString("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); + userInteractionHandler.showMessage(Messages.getText("ImportExport.ErrorWhileImporting"), + Messages.getText("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); } }); } public void exportSettings(SettingsType listType) { - chooseFile(listType.getFileType()) - .map(path -> path.toString().endsWith(listType.getFileType().getExtension()) ? path - : path.getParent().resolve(path.getFileName().toString() + listType.getFileType().getExtension())) + chooseFile(listType.fileType).map(path -> path.toString().endsWith(listType.fileType.extension) ? path : + path.getParent().resolve(path.getFileName().toString() + listType.fileType.extension)) .ifPresent(path -> { try { switch (listType) { @@ -102,13 +91,12 @@ public void exportSettings(SettingsType listType) { default -> throw new IllegalArgumentException("Unexpected value: " + listType); } } catch (Exception e) { - userInteractionHandler.showMessage(Messages.getString("ImportExport.ErrorWhileExporting"), - Messages.getString("ImportExport.ErrorWhileExporting"), MessageSeverity.ERROR); + userInteractionHandler.showMessage(Messages.getText("ImportExport.ErrorWhileExporting"), + Messages.getText("ImportExport.ErrorWhileExporting"), MessageSeverity.ERROR); } }); } - @ExtensionMethod({ StreamExtension.class }) @UtilityClass public static class ExportImportPreferences { @@ -116,8 +104,8 @@ public void exportSettings(Path path, SettingsControl settingsControl) throws Ex settingsControl.exportPreferences(path); } - public void importSettings(Path path, UserInteractionHandler userInteractionHandler, SettingsControl settingsControl) - throws CorruptSettingsFileException { + public void importSettings(Path path, UserInteractionHandler userInteractionHandler, + SettingsControl settingsControl) throws CorruptSettingsFileException { try { settingsControl.importPreferences(path); } catch (IOException | BackingStoreException | InvalidPreferencesFormatException e) { @@ -130,29 +118,33 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand public static class ExportImportSerieMapping { public void exportSettings(Path path, Manager manager) throws IOException { - List serieMappingsWithKey = Arrays.stream(MappingType.values()) + List serieMappingsWithKey = MappingType.values().stream() .map(MappingType::getSelectionForKeyPrefixList) .flatMap(Arrays::stream) .flatMap(selectionForKeyPrefix -> manager.valueBuilder() .cacheType(CacheType.DISK) .keyFilter(k -> k.startsWith(selectionForKeyPrefix.keyPrefix())) .returnType(SerieMapping.class) - .getEntries().stream().map(pair -> new SeriemappingWithKey(pair.getKey(), pair.getValue()))) + .getEntries() + .stream() + .map(pair -> new SeriemappingWithKey(pair.getKey(), pair.getValue()))) .toList(); Files.writeString(path, new GsonBuilder().setPrettyPrinting().create().toJson(serieMappingsWithKey)); } - public void importSettings(Path path, UserInteractionHandler userInteractionHandler, Manager manager) throws CorruptSettingsFileException { + public void importSettings(Path path, UserInteractionHandler userInteractionHandler, Manager manager) + throws CorruptSettingsFileException { SeriemappingWithKey[] serieMappings; try { - serieMappings = new GsonFireBuilder().enableHooks(SerieMapping.class).createGson().fromJson(Files.readString(path), - SeriemappingWithKey[].class); + serieMappings = new GsonFireBuilder().enableHooks(SerieMapping.class) + .createGson() + .fromJson(Files.readString(path), SeriemappingWithKey[].class); } catch (IOException e) { throw new CorruptSettingsFileException(e); } getImportStyle(userInteractionHandler).ifPresent(importStyle -> { if (importStyle == ImportStyle.OVERWRITE) { - Arrays.stream(MappingType.values()) + MappingType.values().stream() .map(MappingType::getSelectionForKeyPrefixList) .flatMap(Arrays::stream) .forEach(selectionForKeyPrefix -> manager.clearExpiredCacheBuilder() @@ -160,7 +152,7 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand .keyFilter((String k) -> k.startsWith(selectionForKeyPrefix.keyPrefix())) .clear()); } - Arrays.stream(serieMappings).forEach(serieMapping -> manager.valueBuilder() + serieMappings.forEach(serieMapping -> manager.valueBuilder() .cacheType(CacheType.DISK) .key(serieMapping.key) .value(serieMapping.serieMapping) @@ -181,7 +173,7 @@ private Optional chooseFile(FileType fileType) { JFileChooser fc = new JFileChooser(); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fc.setAcceptAllFileFilterUsed(false); - fc.setFileFilter(fileType.getFileFilter()); + fc.setFileFilter(fileType.fileFilter); int returnVal = fc.showOpenDialog(parent); if (returnVal == JFileChooser.APPROVE_OPTION) { return Optional.of(fc.getSelectedFile().toPath()); @@ -192,10 +184,10 @@ private Optional chooseFile(FileType fileType) { private static Optional getImportStyle(UserInteractionHandler userInteractionHandler) { return userInteractionHandler.choice(Arrays.asList(ImportStyle.values()), - Messages.getString("ImportExport.OverwriteOrAdd"), Messages.getString("ImportExport.OverwriteOrAddTitle"), - option -> switch (option) { - case OVERWRITE -> Messages.getString("ImportExport.Overwrite"); - case APPEND -> Messages.getString("ImportExport.Add"); + Messages.getText("ImportExport.OverwriteOrAdd"), + Messages.getText("ImportExport.OverwriteOrAddTitle"), option -> switch (option) { + case OVERWRITE -> Messages.getText("ImportExport.Overwrite"); + case APPEND -> Messages.getText("ImportExport.Add"); }); } @@ -204,6 +196,5 @@ private enum ImportStyle { } @StandardException - private static class CorruptSettingsFileException extends Exception { - } + private static class CorruptSettingsFileException extends Exception {} } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java index 95fa9971..f3fb5dbe 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java @@ -6,8 +6,8 @@ public class PropertiesReader { - private final Properties properties; private static PropertiesReader propertiesReaderInstance; + private final Properties properties; public PropertiesReader() throws IOException { try (InputStream is = getClass().getClassLoader().getResourceAsStream("properties-from-pom.properties")) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java index d999a26a..9db27803 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.workers; +import static manifold.ext.props.rt.api.PropOption.*; + import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; @@ -8,6 +10,12 @@ import java.util.Map.Entry; import java.util.Queue; +import lombok.NonNull; +import lombok.Setter; +import lombok.experimental.Accessors; +import manifold.ext.props.rt.api.set; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.ScoreCalculator; @@ -19,13 +27,6 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; - -@RequiredArgsConstructor public class SearchManager implements Cancelable { public interface SearchManagerLanguage { @@ -47,7 +48,8 @@ public interface SearchManagerOnFound { @Setter @Accessors(fluent = true) public static class SearchManagerBuilder - implements SearchManagerOnFound, SearchManagerUserInteractionHandler, SearchManagerProgressListener, SearchManagerLanguage { + implements SearchManagerOnFound, SearchManagerUserInteractionHandler, SearchManagerProgressListener, + SearchManagerLanguage { private Settings settings; private Language language; private SearchProgressListener progressListener; @@ -63,16 +65,22 @@ public SearchManager onFound(SearchHandler onFound) { private final Map workers = new HashMap<>(); private final Map scoreCalculators = new HashMap<>(); private final Settings settings; - @Getter - private int progress = 0; + @var @set(Private) int progress = 0; private int totalJobs; private final SearchHandler onFound; - @Getter - private final Language language; + @val Language language; private final SearchProgressListener progressListener; - @Getter - private final UserInteractionHandler userInteractionHandler; + @val UserInteractionHandler userInteractionHandler; + + public SearchManager(Settings settings, SearchHandler onFound, Language language, + SearchProgressListener progressListener, UserInteractionHandler userInteractionHandler) { + this.settings = settings; + this.onFound = onFound; + this.language = language; + this.progressListener = progressListener; + this.userInteractionHandler = userInteractionHandler; + } public static SearchManagerLanguage createWithSettings(Settings settings) { return new SearchManagerBuilder().settings(settings); @@ -90,7 +98,7 @@ public void addRelease(Release release) { this.queue.forEach((key, value) -> queue.get(key).add(release)); /* Create a scoreCalculator so we can score subtitles for this release */ // TODO: extract to factory - SortWeight weights = new SortWeight(release, this.settings.getSortWeights()); + SortWeight weights = new SortWeight(release, this.settings.sortWeights); this.scoreCalculators.put(release, new ScoreCalculator(weights)); } @@ -111,12 +119,12 @@ public void onCompleted(SearchWorker worker) { /* set the score of the found subtitles */ ScoreCalculator calculator = this.scoreCalculators.get(release); - subtitles.forEach(subtitle -> subtitle.setScore(calculator.calculate(subtitle))); + subtitles.forEach(subtitle -> subtitle.score = calculator.calculate(subtitle)); synchronized (this) { calculateProgress(); /* Tell the progress listener our total progress */ - this.progressListener.progress(this.getProgress()); + this.progressListener.progress(this.progress); } onFound.onFound(release, subtitles); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java index eb1ceb64..8e333cdd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java @@ -12,13 +12,14 @@ public class SearchWorker extends Thread { - protected final SubtitleProvider provider; + private static final Logger LOGGER = LoggerFactory.getLogger(SearchWorker.class); + private final SearchManager scheduler; + protected final SubtitleProvider provider; private boolean busy = false; private boolean isInterrupted = false; private Release release; private Set subtitles; - private static final Logger LOGGER = LoggerFactory.getLogger(SearchWorker.class); public SearchWorker(SubtitleProvider provider, SearchManager scheduler) { this.provider = provider; @@ -27,7 +28,7 @@ public SearchWorker(SubtitleProvider provider, SearchManager scheduler) { @Override public void run() { - Language language = this.scheduler.getLanguage(); + Language language = this.scheduler.language; this.busy = false; try { while (!this.isInterrupted()) { @@ -47,14 +48,15 @@ public void run() { this.subtitles = Set.copyOf(subtitles); this.busy = false; - LOGGER.debug("[Search] {} found {} subtitles for {} ", this.provider.getName(), subtitles.size(), release); + LOGGER.debug("[Search] {} found {} subtitles for {} ", this.provider.getName(), subtitles.size(), + release); if (!this.isInterrupted()) { this.scheduler.onCompleted(this); } } } catch (SubtitlesProviderInitException e) { - LOGGER.error("API %s INIT (%s)".formatted(e.getProviderName(), e.getMessage()), e); + LOGGER.error("API %s INIT (%s)".formatted(e.providerName, e.getMessage()), e); } } diff --git a/MultiSubDownloader/src/main/resources/gestdown/gestdown-swagger.json b/MultiSubDownloader/src/main/resources/gestdown/gestdown-swagger.json index 9cd2fa77..bceb0226 100644 --- a/MultiSubDownloader/src/main/resources/gestdown/gestdown-swagger.json +++ b/MultiSubDownloader/src/main/resources/gestdown/gestdown-swagger.json @@ -3,7 +3,7 @@ "info": { "title": "Gestdown: Addicted Proxy", "description": "Provide a full api to search and download subtitles from Addic7ed website.", - "version": "4.22.5" + "version": "4.29.11" }, "servers": [ { @@ -46,7 +46,7 @@ "required": true, "schema": { "maximum": 50, - "minimum": 0, + "minimum": 1, "type": "integer", "format": "int32" } @@ -125,6 +125,60 @@ } } }, + "/media/{showId}/episodes/{language}": { + "get": { + "tags": [ + "Media" + ], + "summary": "Get the show details with the last season and episodes", + "parameters": [ + { + "name": "showId", + "in": "path", + "description": "", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "language", + "in": "path", + "description": "", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MediaDetailsWithEpisodeAndSubtitlesDto" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + } + }, "/stats/downloads/{top}": { "get": { "tags": [ @@ -970,6 +1024,33 @@ }, "additionalProperties": false }, + "MediaDetailsWithEpisodeAndSubtitlesDto": { + "required": [ + "details", + "episodeWithSubtitles" + ], + "type": "object", + "properties": { + "details": { + "$ref": "#/components/schemas/MediaDetailsDto" + }, + "episodeWithSubtitles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EpisodeWithSubtitlesDto" + }, + "description": "" + }, + "lastSeasonNumber": { + "type": "integer", + "description": "", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Represent a media with its details and episodes with subtitles" + }, "MediaType": { "enum": [ "Show", diff --git a/MultiSubDownloader/src/main/resources/opensubtitles/open-api.json b/MultiSubDownloader/src/main/resources/opensubtitles/open-api.json new file mode 100644 index 00000000..9e0483ae --- /dev/null +++ b/MultiSubDownloader/src/main/resources/opensubtitles/open-api.json @@ -0,0 +1,5842 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "OpenSubtitles API", + "description": "Explore subtitles API here", + "contact": { + "name": "OpenSubtitles API Support", + "url": "https://www.opensubtitles.com/en/contact/", + "email": "support@opensubtitles.org" + }, + "license": { + "name": "MIT", + "url": "https://opensource.org/licenses/MIT" + }, + "version": "1.0.1", + "termsOfService": "https://www.opensubtitles.com/en/tos/" + }, + "servers": [ + { + "url": "https://api.opensubtitles.com/api/v1", + "description": "Default server" + }, + { + "url": "https://vip-api.opensubtitles.com/api/v1", + "description": "VIP server" + } + ], + "tags": [ + { + "name": "Infos", + "description": "General API infos" + }, + { + "name": "Authentication", + "description": "Authentification of user" + }, + { + "name": "Discover", + "description": "Discover popular, latest and most downloaded subtitles" + }, + { + "name": "Download", + "description": "Download of subtitles" + }, + { + "name": "Subtitles", + "description": "Subtitles search for a specific release" + }, + { + "name": "Features", + "description": "Search for feature" + }, + { + "name": "Utilities", + "description": "Various utilities" + } + ], + "paths": { + "/login": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "Login", + "description": "Create a token to authenticate a user. If response code is ```401 Unathorized``` stop sending further requests with the same credentials, login is \"expensive\" operation.\n\nRequest rate limit is 1 request per 1 second.\n\nFurther API requests must continue on returned ```base_url``` host, which can have different cache time for search results and different request rate limits. If ```base_url``` equals ```vip-api.opensubtitles.com``` make sure you always send with every request JWT token (if available), otherwise request might fail with 4xx code.", + "operationId": "login", + "parameters": [ + { + "name": "Content-Type", + "in": "header", + "description": "application/json", + "required": true, + "schema": { + "type": "string", + "default": "application/json" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "x-examples": { + "example": { + "username": "", + "password": "" + } + }, + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "username", + "password" + ] + } + } + }, + "required": false + }, + "responses": { + "200": { + "description": "Create session and token", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "user": { + "type": "object", + "required": [ + "allowed_translations", + "allowed_downloads", + "level", + "user_id", + "ext_installed", + "vip" + ], + "properties": { + "allowed_translations": { + "type": "number" + }, + "allowed_downloads": { + "type": "number" + }, + "level": { + "minLength": 1, + "type": "string", + "example": "VIP Member" + }, + "user_id": { + "type": "number" + }, + "ext_installed": { + "type": "boolean" + }, + "vip": { + "type": "boolean" + } + } + }, + "base_url": { + "type": "string", + "format": "hostname", + "default": "api.opensubtitles.com", + "example": "api.opensubtitles.com", + "enum": [ + "api.opensubtitles.com", + "vip-api.opensubtitles.com" + ] + }, + "token": { + "minLength": 1, + "type": "string" + }, + "status": { + "type": "number" + } + }, + "required": [ + "user", + "base_url", + "token", + "status" + ] + }, + "example": { + "user": { + "allowed_downloads": 100, + "allowed_translations": 5, + "level": "Sub leecher", + "user_id": 66, + "ext_installed": false, + "vip": false + }, + "base_url": "api.opensubtitles.com", + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEOU5aaWUyVjhWOU1hTnJVZWVvcEEwWUNoWEt6Wkx3NiIsImV4cCI6MTYwNDM1ODAwMH0.sMibjAFnkcs-HJ4zhdCwBeGrZ_UvzMbgl5NxYV2uALM", + "status": 200 + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ] + } + }, + "/logout": { + "delete": { + "tags": [ + "Authentication" + ], + "summary": "Logout", + "description": "Destroy a user token to end a session. Bearer token is required for this endpoint.", + "operationId": "logout", + "responses": { + "200": { + "description": "Destroy session and current token", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "message": "token successfully destroyed", + "status": 200 + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Bearer": [] + }, + { + "Api-Key": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ] + } + }, + "/infos/formats": { + "get": { + "tags": [ + "Infos" + ], + "summary": "Subtitle Formats", + "description": "List subtitle formats recognized by the API ", + "operationId": "formats", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "description": "", + "type": "object", + "x-examples": { + "example-1": { + "data": { + "output_formats": [ + "srt", + "sub", + "mpl", + "webvtt", + "dfxp", + "txt" + ] + } + } + }, + "properties": { + "data": { + "type": "object", + "required": [ + "output_formats" + ], + "properties": { + "output_formats": { + "type": "array", + "items": {} + } + } + } + }, + "required": [ + "data" + ] + }, + "examples": { + "example": { + "value": { + "data": { + "output_formats": [ + "srt", + "sub", + "mpl", + "webvtt", + "dfxp", + "txt" + ] + } + } + } + } + }, + "example": { + "example": { + "data": { + "output_formats": [ + "srt", + "sub", + "mpl", + "webvtt", + "dfxp", + "txt" + ] + }, + "value": { + "data": { + "output_formats": [ + "srt", + "sub", + "mpl", + "webvtt", + "dfxp", + "txt" + ] + } + } + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ] + } + }, + "/infos/languages": { + "get": { + "tags": [ + "Infos" + ], + "summary": "Languages", + "description": "Get the languages information", + "operationId": "languages", + "responses": { + "200": { + "description": "Get the languages table containing the codes and names used through the API", + "content": { + "application/json": { + "schema": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "language_code", + "language_name" + ], + "type": "object", + "properties": { + "language_code": { + "minLength": 1, + "type": "string" + }, + "language_name": { + "minLength": 1, + "type": "string" + } + } + } + } + }, + "description": "" + }, + "example": { + "data": [ + { + "language_code": "af", + "language_name": "Afrikaans" + }, + { + "language_code": "sq", + "language_name": "Albanian" + }, + { + "language_code": "ar", + "language_name": "Arabic" + }, + { + "language_code": "an", + "language_name": "Aragonese" + }, + { + "language_code": "hy", + "language_name": "Armenian" + }, + { + "language_code": "at", + "language_name": "Asturian" + }, + { + "language_code": "eu", + "language_name": "Basque" + }, + { + "language_code": "be", + "language_name": "Belarusian" + }, + { + "language_code": "bn", + "language_name": "Bengali" + }, + { + "language_code": "bs", + "language_name": "Bosnian" + }, + { + "language_code": "br", + "language_name": "Breton" + }, + { + "language_code": "bg", + "language_name": "Bulgarian" + }, + { + "language_code": "my", + "language_name": "Burmese" + }, + { + "language_code": "ca", + "language_name": "Catalan" + }, + { + "language_code": "zh-cn", + "language_name": "Chinese (simplified)" + }, + { + "language_code": "cs", + "language_name": "Czech" + }, + { + "language_code": "da", + "language_name": "Danish" + }, + { + "language_code": "nl", + "language_name": "Dutch" + }, + { + "language_code": "en", + "language_name": "English" + }, + { + "language_code": "eo", + "language_name": "Esperanto" + }, + { + "language_code": "et", + "language_name": "Estonian" + }, + { + "language_code": "fi", + "language_name": "Finnish" + }, + { + "language_code": "fr", + "language_name": "French" + }, + { + "language_code": "ka", + "language_name": "Georgian" + }, + { + "language_code": "de", + "language_name": "German" + }, + { + "language_code": "gl", + "language_name": "Galician" + }, + { + "language_code": "el", + "language_name": "Greek" + }, + { + "language_code": "he", + "language_name": "Hebrew" + }, + { + "language_code": "hi", + "language_name": "Hindi" + }, + { + "language_code": "hr", + "language_name": "Croatian" + }, + { + "language_code": "hu", + "language_name": "Hungarian" + }, + { + "language_code": "is", + "language_name": "Icelandic" + }, + { + "language_code": "id", + "language_name": "Indonesian" + }, + { + "language_code": "it", + "language_name": "Italian" + }, + { + "language_code": "ja", + "language_name": "Japanese" + }, + { + "language_code": "kk", + "language_name": "Kazakh" + }, + { + "language_code": "km", + "language_name": "Khmer" + }, + { + "language_code": "ko", + "language_name": "Korean" + }, + { + "language_code": "lv", + "language_name": "Latvian" + }, + { + "language_code": "lt", + "language_name": "Lithuanian" + }, + { + "language_code": "lb", + "language_name": "Luxembourgish" + }, + { + "language_code": "mk", + "language_name": "Macedonian" + }, + { + "language_code": "ml", + "language_name": "Malayalam" + }, + { + "language_code": "ms", + "language_name": "Malay" + }, + { + "language_code": "ma", + "language_name": "Manipuri" + }, + { + "language_code": "mn", + "language_name": "Mongolian" + }, + { + "language_code": "no", + "language_name": "Norwegian" + }, + { + "language_code": "oc", + "language_name": "Occitan" + }, + { + "language_code": "fa", + "language_name": "Persian" + }, + { + "language_code": "pl", + "language_name": "Polish" + }, + { + "language_code": "pt-pt", + "language_name": "Portuguese" + }, + { + "language_code": "ru", + "language_name": "Russian" + }, + { + "language_code": "sr", + "language_name": "Serbian" + }, + { + "language_code": "si", + "language_name": "Sinhalese" + }, + { + "language_code": "sk", + "language_name": "Slovak" + }, + { + "language_code": "sl", + "language_name": "Slovenian" + }, + { + "language_code": "es", + "language_name": "Spanish" + }, + { + "language_code": "sw", + "language_name": "Swahili" + }, + { + "language_code": "sv", + "language_name": "Swedish" + }, + { + "language_code": "sy", + "language_name": "Syriac" + }, + { + "language_code": "ta", + "language_name": "Tamil" + }, + { + "language_code": "te", + "language_name": "Telugu" + }, + { + "language_code": "tl", + "language_name": "Tagalog" + }, + { + "language_code": "th", + "language_name": "Thai" + }, + { + "language_code": "tr", + "language_name": "Turkish" + }, + { + "language_code": "uk", + "language_name": "Ukrainian" + }, + { + "language_code": "ur", + "language_name": "Urdu" + }, + { + "language_code": "uz", + "language_name": "Uzbek" + }, + { + "language_code": "vi", + "language_name": "Vietnamese" + }, + { + "language_code": "ro", + "language_name": "Romanian" + }, + { + "language_code": "pt-br", + "language_name": "Portuguese (Brazilian)" + }, + { + "language_code": "me", + "language_name": "Montenegrin" + }, + { + "language_code": "zh-tw", + "language_name": "Chinese (traditional)" + }, + { + "language_code": "ze", + "language_name": "Chinese bilingual" + } + ] + } + }, + "example": { + "example": { + "data": [ + { + "language_code": "af", + "language_name": "Afrikaans" + }, + { + "language_code": "sq", + "language_name": "Albanian" + }, + { + "language_code": "ar", + "language_name": "Arabic" + }, + { + "language_code": "an", + "language_name": "Aragonese" + }, + { + "language_code": "hy", + "language_name": "Armenian" + }, + { + "language_code": "at", + "language_name": "Asturian" + }, + { + "language_code": "eu", + "language_name": "Basque" + }, + { + "language_code": "be", + "language_name": "Belarusian" + }, + { + "language_code": "bn", + "language_name": "Bengali" + }, + { + "language_code": "bs", + "language_name": "Bosnian" + }, + { + "language_code": "br", + "language_name": "Breton" + }, + { + "language_code": "bg", + "language_name": "Bulgarian" + }, + { + "language_code": "my", + "language_name": "Burmese" + }, + { + "language_code": "ca", + "language_name": "Catalan" + }, + { + "language_code": "zh-cn", + "language_name": "Chinese (simplified)" + }, + { + "language_code": "cs", + "language_name": "Czech" + }, + { + "language_code": "da", + "language_name": "Danish" + }, + { + "language_code": "nl", + "language_name": "Dutch" + }, + { + "language_code": "en", + "language_name": "English" + }, + { + "language_code": "eo", + "language_name": "Esperanto" + }, + { + "language_code": "et", + "language_name": "Estonian" + }, + { + "language_code": "fi", + "language_name": "Finnish" + }, + { + "language_code": "fr", + "language_name": "French" + }, + { + "language_code": "ka", + "language_name": "Georgian" + }, + { + "language_code": "de", + "language_name": "German" + }, + { + "language_code": "gl", + "language_name": "Galician" + }, + { + "language_code": "el", + "language_name": "Greek" + }, + { + "language_code": "he", + "language_name": "Hebrew" + }, + { + "language_code": "hi", + "language_name": "Hindi" + }, + { + "language_code": "hr", + "language_name": "Croatian" + }, + { + "language_code": "hu", + "language_name": "Hungarian" + }, + { + "language_code": "is", + "language_name": "Icelandic" + }, + { + "language_code": "id", + "language_name": "Indonesian" + }, + { + "language_code": "it", + "language_name": "Italian" + }, + { + "language_code": "ja", + "language_name": "Japanese" + }, + { + "language_code": "kk", + "language_name": "Kazakh" + }, + { + "language_code": "km", + "language_name": "Khmer" + }, + { + "language_code": "ko", + "language_name": "Korean" + }, + { + "language_code": "lv", + "language_name": "Latvian" + }, + { + "language_code": "lt", + "language_name": "Lithuanian" + }, + { + "language_code": "lb", + "language_name": "Luxembourgish" + }, + { + "language_code": "mk", + "language_name": "Macedonian" + }, + { + "language_code": "ml", + "language_name": "Malayalam" + }, + { + "language_code": "ms", + "language_name": "Malay" + }, + { + "language_code": "ma", + "language_name": "Manipuri" + }, + { + "language_code": "mn", + "language_name": "Mongolian" + }, + { + "language_code": "no", + "language_name": "Norwegian" + }, + { + "language_code": "oc", + "language_name": "Occitan" + }, + { + "language_code": "fa", + "language_name": "Persian" + }, + { + "language_code": "pl", + "language_name": "Polish" + }, + { + "language_code": "pt-pt", + "language_name": "Portuguese" + }, + { + "language_code": "ru", + "language_name": "Russian" + }, + { + "language_code": "sr", + "language_name": "Serbian" + }, + { + "language_code": "si", + "language_name": "Sinhalese" + }, + { + "language_code": "sk", + "language_name": "Slovak" + }, + { + "language_code": "sl", + "language_name": "Slovenian" + }, + { + "language_code": "es", + "language_name": "Spanish" + }, + { + "language_code": "sw", + "language_name": "Swahili" + }, + { + "language_code": "sv", + "language_name": "Swedish" + }, + { + "language_code": "sy", + "language_name": "Syriac" + }, + { + "language_code": "ta", + "language_name": "Tamil" + }, + { + "language_code": "te", + "language_name": "Telugu" + }, + { + "language_code": "tl", + "language_name": "Tagalog" + }, + { + "language_code": "th", + "language_name": "Thai" + }, + { + "language_code": "tr", + "language_name": "Turkish" + }, + { + "language_code": "uk", + "language_name": "Ukrainian" + }, + { + "language_code": "ur", + "language_name": "Urdu" + }, + { + "language_code": "uz", + "language_name": "Uzbek" + }, + { + "language_code": "vi", + "language_name": "Vietnamese" + }, + { + "language_code": "ro", + "language_name": "Romanian" + }, + { + "language_code": "pt-br", + "language_name": "Portuguese (Brazilian)" + }, + { + "language_code": "me", + "language_name": "Montenegrin" + }, + { + "language_code": "zh-tw", + "language_name": "Chinese (traditional)" + }, + { + "language_code": "ze", + "language_name": "Chinese bilingual" + }, + { + "language_code": "se", + "language_name": "Northern Sami" + } + ] + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ] + } + }, + "/infos/user": { + "get": { + "tags": [ + "Infos" + ], + "summary": "User Informations", + "description": "Gather informations about the user authenticated by a bearer token. User information are already sent when user is authenticated, and the remaining downloads is returned with each download, but you can also get these information here.", + "operationId": "userinfo", + "responses": { + "200": { + "description": "Get user data", + "content": { + "application/json": { + "schema": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "required": [ + "allowed_downloads", + "downloads_count", + "ext_installed", + "level", + "remaining_downloads", + "user_id", + "vip" + ], + "type": "object", + "properties": { + "allowed_downloads": { + "type": "number" + }, + "level": { + "minLength": 1, + "type": "string" + }, + "user_id": { + "type": "number" + }, + "ext_installed": { + "type": "boolean" + }, + "vip": { + "type": "boolean" + }, + "downloads_count": { + "type": "number" + }, + "remaining_downloads": { + "type": "number" + } + } + } + }, + "description": "" + }, + "example": { + "data": { + "allowed_downloads": 100, + "level": "Sub leecher", + "user_id": 66, + "ext_installed": false, + "vip": false, + "downloads_count": 1, + "remaining_downloads": 99 + } + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Bearer": [] + }, + { + "Api-Key": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ] + } + }, + "/discover/popular": { + "get": { + "tags": [ + "Discover" + ], + "summary": "Popular features", + "description": "Discover popular features on opensubtitles.com, according to last 30 days downloads.", + "operationId": "popular", + "parameters": [ + { + "name": "language", + "in": "query", + "description": "Language code, 1 language per query, or \"all\"", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "Type (movie or tvshow)", + "schema": { + "type": "string" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Subtitle" + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ] + } + }, + "/discover/latest": { + "get": { + "tags": [ + "Discover" + ], + "summary": "Latest subtitles", + "description": "Lists 60 latest uploaded subtitles", + "operationId": "latest", + "parameters": [ + { + "name": "language", + "in": "query", + "description": "Language code, 1 language per query, or \"all\"", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "Type (movie or tvshow)", + "schema": { + "type": "string" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "required": [ + "data", + "page", + "total_count", + "total_pages" + ], + "type": "object", + "properties": { + "total_pages": { + "type": "number" + }, + "total_count": { + "type": "number" + }, + "page": { + "type": "number" + }, + "data": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "required": [ + "ai_translated", + "comments", + "download_count", + "feature_details", + "files", + "foreign_parts_only", + "fps", + "from_trusted", + "hd", + "hearing_impaired", + "language", + "legacy_subtitle_id", + "new_download_count", + "points", + "ratings", + "related_links", + "release", + "subtitle_id", + "upload_date", + "uploader", + "url", + "votes" + ], + "type": "object", + "properties": { + "subtitle_id": { + "minLength": 1, + "type": "string" + }, + "language": { + "minLength": 1, + "type": "string" + }, + "download_count": { + "type": "number" + }, + "new_download_count": { + "type": "number" + }, + "hearing_impaired": { + "type": "boolean" + }, + "hd": { + "type": "boolean" + }, + "format": { + "type": "object" + }, + "fps": { + "type": "number" + }, + "votes": { + "type": "number" + }, + "points": { + "type": "number" + }, + "ratings": { + "type": "number" + }, + "from_trusted": { + "type": "boolean" + }, + "foreign_parts_only": { + "type": "boolean" + }, + "ai_translated": { + "type": "boolean" + }, + "machine_translated": { + "type": "object" + }, + "upload_date": { + "minLength": 1, + "type": "string" + }, + "release": { + "minLength": 1, + "type": "string" + }, + "comments": { + "type": "string" + }, + "legacy_subtitle_id": { + "type": "number" + }, + "uploader": { + "required": [ + "name", + "rank", + "uploader_id" + ], + "type": "object", + "properties": { + "uploader_id": { + "type": "number" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "rank": { + "minLength": 1, + "type": "string" + } + } + }, + "feature_details": { + "required": [ + "feature_id", + "feature_type", + "imdb_id", + "movie_name", + "title", + "year" + ], + "type": "object", + "properties": { + "feature_id": { + "type": "number" + }, + "feature_type": { + "minLength": 1, + "type": "string" + }, + "year": { + "type": "number" + }, + "title": { + "minLength": 1, + "type": "string" + }, + "movie_name": { + "minLength": 1, + "type": "string" + }, + "imdb_id": { + "type": "number" + }, + "tmdb_id": { + "type": "object" + } + } + }, + "url": { + "minLength": 1, + "type": "string" + }, + "related_links": { + "required": [ + "img_url", + "label", + "url" + ], + "type": "object", + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "url": { + "minLength": 1, + "type": "string" + }, + "img_url": { + "minLength": 1, + "type": "string" + } + } + }, + "files": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "cd_number", + "file_id", + "file_name" + ], + "type": "object", + "properties": { + "file_id": { + "type": "number" + }, + "cd_number": { + "type": "number" + }, + "file_name": { + "minLength": 1, + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "description": "" + }, + "examples": { + "example": { + "value": { + "total_pages": 1, + "total_count": 10, + "page": 1, + "data": [ + { + "id": "string", + "type": "string", + "attributes": { + "subtitle_id": "string", + "language": "string", + "download_count": 0, + "new_download_count": 0, + "hearing_impaired": true, + "hd": true, + "format": {}, + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": true, + "foreign_parts_only": true, + "ai_translated": true, + "machine_translated": {}, + "upload_date": "string", + "release": "string", + "comments": "string", + "legacy_subtitle_id": 0, + "uploader": { + "uploader_id": 0, + "name": "string", + "rank": "string" + }, + "feature_details": { + "feature_id": 0, + "feature_type": "string", + "year": 0, + "title": "string", + "movie_name": "string", + "imdb_id": 0, + "tmdb_id": {} + }, + "url": "string", + "related_links": { + "label": "string", + "url": "string", + "img_url": "string" + }, + "files": [ + { + "file_id": 0, + "cd_number": 0, + "file_name": "string" + } + ] + } + } + ] + } + } + } + }, + "example": { + "example": { + "total_pages": 1, + "total_count": 60, + "page": 1, + "data": [ + { + "id": "6150116", + "type": "subtitle", + "attributes": { + "subtitle_id": "6150116", + "language": "sv", + "download_count": 0, + "new_download_count": 0, + "hearing_impaired": false, + "hd": true, + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": true, + "foreign_parts_only": false, + "ai_translated": false, + "upload_date": "2021-11-10T13:48:58.000Z", + "release": "No.Sudden.Move.2021.1080p.WEB.H264-TIMECUT", + "comments": "", + "legacy_subtitle_id": 8871896, + "uploader": { + "uploader_id": 83676, + "name": "Nemo_", + "rank": "Trusted member" + }, + "feature_details": { + "feature_id": 1226096, + "feature_type": "Movie", + "year": 2021, + "title": "No Sudden Move", + "movie_name": "2021 - No Sudden Move", + "imdb_id": 11525644, + "tmdb_id": 649409 + }, + "url": "https://www.opensubtitles.com/sv/subtitles/legacy/8871896", + "related_links": { + "label": "All subtitles for No Sudden Move", + "url": "https://www.opensubtitles.com/sv/movies/2021-no-sudden-move", + "img_url": "https://s9.osdb.link/features/6/9/0/1226096.jpg" + }, + "files": [ + { + "file_id": 7086008, + "cd_number": 1, + "file_name": "No.Sudden.Move.2021.1080p.WEB.H264-TIMECUT.srt" + } + ] + } + }, + { + "id": "6150127", + "type": "subtitle", + "attributes": { + "subtitle_id": "6150127", + "language": "zh-CN", + "download_count": 12, + "new_download_count": 2, + "hearing_impaired": false, + "hd": true, + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "upload_date": "2021-11-10T13:45:35.000Z", + "release": "Shang.Chi.and.the.Legend.of.the.Ten.Rings.2021.COMPLETE.UHD.BLURAY-SURCODE", + "comments": "", + "legacy_subtitle_id": 8871895, + "uploader": { + "uploader_id": 0, + "name": "Anonymous", + "rank": "anonymous" + }, + "feature_details": { + "feature_id": 1218697, + "feature_type": "Movie", + "year": 2021, + "title": "Shang-Chi and the Legend of the Ten Rings", + "movie_name": "2021 - Shang-Chi and the Legend of the Ten Rings", + "imdb_id": 9376612, + "tmdb_id": 566525 + }, + "url": "https://www.opensubtitles.com/zh-CN/subtitles/legacy/8871895", + "related_links": { + "label": "All subtitles for Shang-Chi and the Legend of the Ten Rings", + "url": "https://www.opensubtitles.com/zh-CN/movies/2021-shang-chi-and-the-legend-of-the-ten-rings", + "img_url": "https://s9.osdb.link/features/7/9/6/1218697.jpg" + }, + "files": [ + { + "file_id": 7086014, + "cd_number": 1, + "file_name": "Shang.Chi.and.the.Legend.of.the.Ten.Rings.2021.COMPLETE.UHD.BLURAY-SURCODE.ass" + } + ] + } + } + ], + "value": { + "total_pages": 1, + "total_count": 60, + "page": 1, + "data": [ + { + "id": "6150116", + "type": "subtitle", + "attributes": { + "subtitle_id": "6150116", + "language": "sv", + "download_count": 0, + "new_download_count": 0, + "hearing_impaired": false, + "hd": true, + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": true, + "foreign_parts_only": false, + "ai_translated": false, + "upload_date": "2021-11-10T13:48:58.000Z", + "release": "No.Sudden.Move.2021.1080p.WEB.H264-TIMECUT", + "comments": "", + "legacy_subtitle_id": 8871896, + "uploader": { + "uploader_id": 83676, + "name": "Nemo_", + "rank": "Trusted member" + }, + "feature_details": { + "feature_id": 1226096, + "feature_type": "Movie", + "year": 2021, + "title": "No Sudden Move", + "movie_name": "2021 - No Sudden Move", + "imdb_id": 11525644, + "tmdb_id": 649409 + }, + "url": "https://www.opensubtitles.com/sv/subtitles/legacy/8871896", + "related_links": { + "label": "All subtitles for No Sudden Move", + "url": "https://www.opensubtitles.com/sv/movies/2021-no-sudden-move", + "img_url": "https://s9.osdb.link/features/6/9/0/1226096.jpg" + }, + "files": [ + { + "file_id": 7086008, + "cd_number": 1, + "file_name": "No.Sudden.Move.2021.1080p.WEB.H264-TIMECUT.srt" + } + ] + } + }, + { + "id": "6150127", + "type": "subtitle", + "attributes": { + "subtitle_id": "6150127", + "language": "zh-CN", + "download_count": 12, + "new_download_count": 2, + "hearing_impaired": false, + "hd": true, + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "upload_date": "2021-11-10T13:45:35.000Z", + "release": "Shang.Chi.and.the.Legend.of.the.Ten.Rings.2021.COMPLETE.UHD.BLURAY-SURCODE", + "comments": "", + "legacy_subtitle_id": 8871895, + "uploader": { + "name": "Anonymous", + "rank": "anonymous" + }, + "feature_details": { + "feature_id": 1218697, + "feature_type": "Movie", + "year": 2021, + "title": "Shang-Chi and the Legend of the Ten Rings", + "movie_name": "2021 - Shang-Chi and the Legend of the Ten Rings", + "imdb_id": 9376612, + "tmdb_id": 566525 + }, + "url": "https://www.opensubtitles.com/zh-CN/subtitles/legacy/8871895", + "related_links": { + "label": "All subtitles for Shang-Chi and the Legend of the Ten Rings", + "url": "https://www.opensubtitles.com/zh-CN/movies/2021-shang-chi-and-the-legend-of-the-ten-rings", + "img_url": "https://s9.osdb.link/features/7/9/6/1218697.jpg" + }, + "files": [ + { + "file_id": 7086014, + "cd_number": 1, + "file_name": "Shang.Chi.and.the.Legend.of.the.Ten.Rings.2021.COMPLETE.UHD.BLURAY-SURCODE.ass" + } + ] + } + } + ] + } + } + } + } + } + }, + "security": [ + { + "Api-Key": [] + } + ] + } + }, + "/discover/most_downloaded": { + "get": { + "tags": [ + "Discover" + ], + "summary": "Most downloaded subtitles", + "description": "Discover popular subtitles, according to last 30 days downloads on opensubtitles.com. This list can be filtered by language code or feature type (movie, episode)", + "operationId": "most_downloaded", + "parameters": [ + { + "name": "language", + "in": "query", + "description": "Language code, 1 language per query, or \"all\"", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "Type (movie or tvshow)", + "schema": { + "type": "string" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "responses": { + "200": { + "description": "Lists most downloaded movie subtitles ", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "", + "properties": { + "total_pages": { + "type": "number" + }, + "total_count": { + "type": "number" + }, + "page": { + "type": "number" + }, + "data": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "$ref": "#/components/schemas/Subtitle" + } + } + }, + "required": [ + "total_pages", + "total_count", + "page", + "data" + ] + }, + "example": { + "total_pages": 1, + "total_count": 46, + "page": 1, + "data": [ + { + "id": "493023", + "type": "subtitle", + "attributes": { + "subtitle_id": "493023", + "language": "nl", + "download_count": 3889, + "new_download_count": 11, + "hearing_impaired": false, + "hd": false, + "format": "srt", + "fps": 29.97, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2010-11-15T14:55:39.000Z", + "release": "Major League WS DVDRip", + "comments": "", + "legacy_subtitle_id": 3956266, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 518105, + "feature_type": "Movie", + "year": 1989, + "title": "Major League", + "movie_name": "1989 - Major League", + "imdb_id": 97815, + "tmdb_id": 9942 + }, + "url": "https://www.opensubtitles.com/nl/subtitles/legacy/3956266", + "related_links": [ + { + "label": "All subtitles for Major League", + "url": "https://www.opensubtitles.com/nl/movies/1989-major-league", + "img_url": "https://s9.osdb.link/features/5/0/1/518105.jpg" + } + ], + "files": [ + { + "file_id": 544077, + "cd_number": 1, + "file_name": "Major League WS DVDRip.srt" + } + ] + } + }, + { + "id": "496423", + "type": "subtitle", + "attributes": { + "subtitle_id": "496423", + "language": "es", + "download_count": 674, + "new_download_count": 5, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 29.97, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2008-03-23T21:04:04.000Z", + "release": "Le mépris", + "comments": "", + "legacy_subtitle_id": 3264195, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 518020, + "feature_type": "Movie", + "year": 1963, + "title": "Contempt", + "movie_name": "1963 - Contempt", + "imdb_id": 57345, + "tmdb_id": 266 + }, + "url": "https://www.opensubtitles.com/es/subtitles/legacy/3264195", + "related_links": [ + { + "label": "All subtitles for Contempt", + "url": "https://www.opensubtitles.com/es/movies/1963-contempt", + "img_url": "https://s9.osdb.link/features/0/2/0/518020.jpg" + } + ], + "files": [ + { + "file_id": 6028018, + "cd_number": 1, + "file_name": "Le mepris-es.srt" + } + ] + } + }, + { + "id": "495449", + "type": "subtitle", + "attributes": { + "subtitle_id": "495449", + "language": "pt-BR", + "download_count": 189, + "new_download_count": 1, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 23.976, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2012-07-14T19:47:18.000Z", + "release": "Gnger Snaps", + "comments": "", + "legacy_subtitle_id": 4617243, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 518342, + "feature_type": "Movie", + "year": 2000, + "title": "Ginger Snaps", + "movie_name": "2000 - Ginger Snaps", + "imdb_id": 210070, + "tmdb_id": 9871 + }, + "url": "https://www.opensubtitles.com/pt-BR/subtitles/legacy/4617243", + "related_links": [ + { + "label": "All subtitles for Ginger Snaps", + "url": "https://www.opensubtitles.com/pt-BR/movies/2000-ginger-snaps", + "img_url": "https://s9.osdb.link/features/2/4/3/518342.jpg" + } + ], + "files": [ + { + "file_id": 546827, + "cd_number": 1, + "file_name": "Ginger Snaps [2000] DvDrip [Eng] Bugz.srt" + } + ] + } + }, + { + "id": "496964", + "type": "subtitle", + "attributes": { + "subtitle_id": "496964", + "language": "es", + "download_count": 1190, + "new_download_count": 7, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 23.976, + "votes": 1, + "points": 10, + "ratings": 10, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2012-05-18T00:13:46.000Z", + "release": "1963.El desprecio (subt)", + "comments": "", + "legacy_subtitle_id": 4552383, + "uploader": { + "uploader_id": 63170, + "name": "robot2xl", + "rank": "read only" + }, + "feature_details": { + "feature_id": 518020, + "feature_type": "Movie", + "year": 1963, + "title": "Contempt", + "movie_name": "1963 - Contempt", + "imdb_id": 57345, + "tmdb_id": 266 + }, + "url": "https://www.opensubtitles.com/es/subtitles/legacy/4552383", + "related_links": [ + { + "label": "All subtitles for Contempt", + "url": "https://www.opensubtitles.com/es/movies/1963-contempt", + "img_url": "https://s9.osdb.link/features/0/2/0/518020.jpg" + } + ], + "files": [ + { + "file_id": 548446, + "cd_number": 1, + "file_name": "1963.El desprecio (subt).srt" + } + ] + } + }, + { + "id": "492641", + "type": "subtitle", + "attributes": { + "subtitle_id": "492641", + "language": "es", + "download_count": 1477, + "new_download_count": 2, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2005-08-19T22:00:00.000Z", + "release": "Philadelphia Story, The (1940)", + "comments": "", + "legacy_subtitle_id": 101000, + "uploader": { + "uploader_id": 14715, + "name": "marlowe62 (a)", + "rank": "bronze member" + }, + "feature_details": { + "feature_id": 518136, + "feature_type": "Movie", + "year": 1940, + "title": "The Philadelphia Story", + "movie_name": "1940 - The Philadelphia Story", + "imdb_id": 32904, + "tmdb_id": 981 + }, + "url": "https://www.opensubtitles.com/es/subtitles/legacy/101000", + "related_links": [ + { + "label": "All subtitles for The Philadelphia Story", + "url": "https://www.opensubtitles.com/es/movies/1940-the-philadelphia-story", + "img_url": "https://s9.osdb.link/features/6/3/1/518136.jpg" + } + ], + "files": [ + { + "file_id": 6026506, + "cd_number": 1, + "file_name": "George Cukor - Historias de Filadelfia (1940) DvdRip XviD Mp3 Dual Divxclasico.Esp.srt" + } + ] + } + }, + { + "id": "493247", + "type": "subtitle", + "attributes": { + "subtitle_id": "493247", + "language": "el", + "download_count": 1052, + "new_download_count": 16, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 23.976, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2011-05-17T07:28:54.000Z", + "release": "Superman.1978.720.BluRay.x264-VarK", + "comments": "", + "legacy_subtitle_id": 4179141, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 517956, + "feature_type": "Movie", + "year": 1978, + "title": "Superman", + "movie_name": "1978 - Superman", + "imdb_id": 78346, + "tmdb_id": 1924 + }, + "url": "https://www.opensubtitles.com/el/subtitles/legacy/4179141", + "related_links": [ + { + "label": "All subtitles for Superman", + "url": "https://www.opensubtitles.com/el/movies/1978-superman", + "img_url": "https://s9.osdb.link/features/6/5/9/517956.jpg" + } + ], + "files": [ + { + "file_id": 544327, + "cd_number": 1, + "file_name": "Superman.1978.720.BluRay.x264-VarK.srt" + } + ] + } + }, + { + "id": "496017", + "type": "subtitle", + "attributes": { + "subtitle_id": "496017", + "language": "en", + "download_count": 24642, + "new_download_count": 8, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 23.98, + "votes": 13, + "points": 81, + "ratings": 6.2, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2002-10-10T22:00:00.000Z", + "release": "Jingle All the Way (1996)", + "comments": "none", + "legacy_subtitle_id": 32268, + "uploader": { + "uploader_id": 6872, + "name": "alxmota (a)", + "rank": "bronze member" + }, + "feature_details": { + "feature_id": 518213, + "feature_type": "Movie", + "year": 1996, + "title": "Jingle All the Way", + "movie_name": "1996 - Jingle All the Way", + "imdb_id": 116705, + "tmdb_id": 9279 + }, + "url": "https://www.opensubtitles.com/en/subtitles/legacy/32268", + "related_links": [ + { + "label": "All subtitles for Jingle All the Way", + "url": "https://www.opensubtitles.com/en/movies/1996-jingle-all-the-way", + "img_url": "https://s9.osdb.link/features/3/1/2/518213.jpg" + } + ], + "files": [ + { + "file_id": 547446, + "cd_number": 1, + "file_name": "ingles.srt" + } + ] + } + }, + { + "id": "496396", + "type": "subtitle", + "attributes": { + "subtitle_id": "496396", + "language": "nl", + "download_count": 1993, + "new_download_count": 16, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 25, + "votes": 1, + "points": 7, + "ratings": 7, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2013-10-10T22:54:26.000Z", + "release": "Come and See (Idi i smotri) (1985)", + "comments": "", + "legacy_subtitle_id": 5217608, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 518417, + "feature_type": "Movie", + "year": 1985, + "title": "Come and See", + "movie_name": "1985 - Come and See", + "imdb_id": 91251, + "tmdb_id": 25237 + }, + "url": "https://www.opensubtitles.com/nl/subtitles/legacy/5217608", + "related_links": [ + { + "label": "All subtitles for Come and See", + "url": "https://www.opensubtitles.com/nl/movies/1985-come-and-see", + "img_url": "https://s9.osdb.link/features/7/1/4/518417.jpg" + } + ], + "files": [ + { + "file_id": 547857, + "cd_number": 1, + "file_name": "Come and See (Idi i smotri) (1985)-dut(1).srt" + } + ] + } + }, + { + "id": "492427", + "type": "subtitle", + "attributes": { + "subtitle_id": "492427", + "language": "pt-BR", + "download_count": 358, + "new_download_count": 3, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 23.98, + "votes": 1, + "points": 1, + "ratings": 1, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2015-12-26T15:19:56.000Z", + "release": "Rambo.First.Blood.II.1985.Ultimate.Uncut.Remastered.Edition.1080p.BluRay.x264.AAC-ETRG", + "comments": "PT-BR subtitle uploaded by saidleugim", + "legacy_subtitle_id": 6437707, + "uploader": { + "uploader_id": 71162, + "name": "saidleugim", + "rank": "bronze member" + }, + "feature_details": { + "feature_id": 517867, + "feature_type": "Movie", + "year": 1985, + "title": "Rambo: First Blood Part II", + "movie_name": "1985 - Rambo: First Blood Part II", + "imdb_id": 89880, + "tmdb_id": 1369 + }, + "url": "https://www.opensubtitles.com/pt-BR/subtitles/legacy/6437707", + "related_links": [ + { + "label": "All subtitles for Rambo: First Blood Part II", + "url": "https://www.opensubtitles.com/pt-BR/movies/1985-rambo-first-blood-part-ii", + "img_url": "https://s9.osdb.link/features/7/6/8/517867.jpg" + } + ], + "files": [ + { + "file_id": 6026410, + "cd_number": 1, + "file_name": "Rambo.First.Blood.II.1985.Ultimate.Uncut.Remastered.Edition.1080p.BluRay.x264.AAC-ETRG.srt" + } + ] + } + }, + { + "id": "493721", + "type": "subtitle", + "attributes": { + "subtitle_id": "493721", + "language": "en", + "download_count": 16956, + "new_download_count": 4, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 25, + "votes": 7, + "points": 67, + "ratings": 9.6, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2007-05-16T09:07:38.000Z", + "release": "Hannibal", + "comments": "", + "legacy_subtitle_id": 3123830, + "uploader": { + "uploader_id": 34153, + "name": "arkymedes", + "rank": "bronze member" + }, + "feature_details": { + "feature_id": 517991, + "feature_type": "Movie", + "year": 2001, + "title": "Hannibal", + "movie_name": "2001 - Hannibal", + "imdb_id": 212985, + "tmdb_id": 9740 + }, + "url": "https://www.opensubtitles.com/en/subtitles/legacy/3123830", + "related_links": [ + { + "label": "All subtitles for Hannibal", + "url": "https://www.opensubtitles.com/en/movies/2001-hannibal", + "img_url": "https://s9.osdb.link/features/1/9/9/517991.jpg" + } + ], + "files": [ + { + "file_id": 544852, + "cd_number": 1, + "file_name": "Hannibal.en.srt" + } + ] + } + }, + { + "id": "496722", + "type": "subtitle", + "attributes": { + "subtitle_id": "496722", + "language": "es", + "download_count": 544, + "new_download_count": 21, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 25, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": true, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2011-05-28T08:48:56.000Z", + "release": "Le Mepris (1963) HDRip Dual XviD Ac3 by FitoCorleone", + "comments": "El.Desprecio.(Le.Mepris).(1963).HDRip.Dual.(Spa.Fr.).(Xvid+2Ac3).(proteinicos.es)...FitoCorleone.avi [1.47 Gb]/ Corregidos por el GTC de DivXClasico para Proteinicos y DXC", + "legacy_subtitle_id": 4184944, + "uploader": { + "uploader_id": 32127, + "name": "marlowe62", + "rank": "trusted" + }, + "feature_details": { + "feature_id": 518020, + "feature_type": "Movie", + "year": 1963, + "title": "Contempt", + "movie_name": "1963 - Contempt", + "imdb_id": 57345, + "tmdb_id": 266 + }, + "url": "https://www.opensubtitles.com/es/subtitles/legacy/4184944", + "related_links": [ + { + "label": "All subtitles for Contempt", + "url": "https://www.opensubtitles.com/es/movies/1963-contempt", + "img_url": "https://s9.osdb.link/features/0/2/0/518020.jpg" + } + ], + "files": [ + { + "file_id": 548202, + "cd_number": 1, + "file_name": "Le Mepris (1963) HDRip Dual XviD Ac3 by FitoCorleone.Esp.srt" + } + ] + } + }, + { + "id": "492688", + "type": "subtitle", + "attributes": { + "subtitle_id": "492688", + "language": "en", + "download_count": 72506, + "new_download_count": 62, + "hearing_impaired": true, + "hd": false, + "format": "", + "fps": 25, + "votes": 2, + "points": 20, + "ratings": 10, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2008-07-09T13:19:22.000Z", + "release": "Rocky.I[1976]DvDrip-aXXo", + "comments": "Rocky-The.Complete.Saga[2007]DvDrip-aXXo", + "legacy_subtitle_id": 3302711, + "uploader": { + "uploader_id": 119465, + "name": "os_robot", + "rank": "bronze member" + }, + "feature_details": { + "feature_id": 517932, + "feature_type": "Movie", + "year": 1976, + "title": "Rocky", + "movie_name": "1976 - Rocky", + "imdb_id": 75148, + "tmdb_id": 1366 + }, + "url": "https://www.opensubtitles.com/en/subtitles/legacy/3302711", + "related_links": [ + { + "label": "All subtitles for Rocky", + "url": "https://www.opensubtitles.com/en/movies/1976-rocky", + "img_url": "https://s9.osdb.link/features/2/3/9/517932.jpg" + } + ], + "files": [ + { + "file_id": 543715, + "cd_number": 1, + "file_name": "Rocky.I[1976]DvDrip-aXXo.srt" + } + ] + } + }, + { + "id": "490625", + "type": "subtitle", + "attributes": { + "subtitle_id": "490625", + "language": "en", + "download_count": 7989, + "new_download_count": 20, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2013-07-06T11:07:28.000Z", + "release": "9.Songs.2004.720p.BluRay.x264.anoXmous", + "comments": "", + "legacy_subtitle_id": 5075303, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 518052, + "feature_type": "Movie", + "year": 2004, + "title": "9 Songs", + "movie_name": "2004 - 9 Songs", + "imdb_id": 411705, + "tmdb_id": 27 + }, + "url": "https://www.opensubtitles.com/en/subtitles/legacy/5075303", + "related_links": [ + { + "label": "All subtitles for 9 Songs", + "url": "https://www.opensubtitles.com/en/movies/2004-9-songs", + "img_url": "https://s9.osdb.link/features/2/5/0/518052.jpg" + } + ], + "files": [ + { + "file_id": 541296, + "cd_number": 1, + "file_name": "9.Songs.2004.720p.BluRay.x264.anoXmous_eng.srt" + } + ] + } + }, + { + "id": "494835", + "type": "subtitle", + "attributes": { + "subtitle_id": "494835", + "language": "pt-BR", + "download_count": 2028, + "new_download_count": 10, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 24, + "votes": 2, + "points": 20, + "ratings": 10, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2014-09-07T13:57:21.000Z", + "release": "Red Sonja.1985.BDRip.720p.MultiLang.MultiSub-Pitt", + "comments": "", + "legacy_subtitle_id": 5815278, + "uploader": { + "uploader_id": 62530, + "name": "fjones1979", + "rank": "trusted" + }, + "feature_details": { + "feature_id": 518067, + "feature_type": "Movie", + "year": 1985, + "title": "Red Sonja", + "movie_name": "1985 - Red Sonja", + "imdb_id": 89893, + "tmdb_id": 9626 + }, + "url": "https://www.opensubtitles.com/pt-BR/subtitles/legacy/5815278", + "related_links": [ + { + "label": "All subtitles for Red Sonja", + "url": "https://www.opensubtitles.com/pt-BR/movies/1985-red-sonja", + "img_url": "https://s9.osdb.link/features/7/6/0/518067.jpg" + } + ], + "files": [ + { + "file_id": 546129, + "cd_number": 1, + "file_name": "Red Sonja.1985.BDRip.720p.MultiLang.MultiSub-Pitt.por.srt" + } + ] + } + }, + { + "id": "492618", + "type": "subtitle", + "attributes": { + "subtitle_id": "492618", + "language": "pl", + "download_count": 147, + "new_download_count": 4, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2015-07-08T20:47:42.000Z", + "release": "Presumed.Innocent.1990.720p.BRRip.XviD.AC3-RARBG", + "comments": "", + "legacy_subtitle_id": 6228423, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 517957, + "feature_type": "Movie", + "year": 1990, + "title": "Presumed Innocent", + "movie_name": "1990 - Presumed Innocent", + "imdb_id": 100404, + "tmdb_id": 11092 + }, + "url": "https://www.opensubtitles.com/pl/subtitles/legacy/6228423", + "related_links": [ + { + "label": "All subtitles for Presumed Innocent", + "url": "https://www.opensubtitles.com/pl/movies/1990-presumed-innocent", + "img_url": "https://s9.osdb.link/features/7/5/9/517957.jpg" + } + ], + "files": [ + { + "file_id": 6026497, + "cd_number": 1, + "file_name": "Presumed.Innocent.1990.720p.BRRip.XviD.AC3-RARBG.txt" + } + ] + } + }, + { + "id": "496727", + "type": "subtitle", + "attributes": { + "subtitle_id": "496727", + "language": "en", + "download_count": 1912, + "new_download_count": 63, + "hearing_impaired": false, + "hd": true, + "format": "", + "fps": 23.976, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2016-08-12T06:53:45.000Z", + "release": "Idi.i.smotri.AKA.Come.and.See.1985.IVC.1080p.BluRay.Remux.AVC.FLAC.2.0-oddset", + "comments": "", + "legacy_subtitle_id": 6708061, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 518417, + "feature_type": "Movie", + "year": 1985, + "title": "Come and See", + "movie_name": "1985 - Come and See", + "imdb_id": 91251, + "tmdb_id": 25237 + }, + "url": "https://www.opensubtitles.com/en/subtitles/legacy/6708061", + "related_links": [ + { + "label": "All subtitles for Come and See", + "url": "https://www.opensubtitles.com/en/movies/1985-come-and-see", + "img_url": "https://s9.osdb.link/features/7/1/4/518417.jpg" + } + ], + "files": [ + { + "file_id": 6028156, + "cd_number": 1, + "file_name": "Idi.i.smotri.AKA.Come.and.See.1985.IVC.1080p.BluRay.Remux.AVC.FLAC.2.0-oddset.srt" + } + ] + } + }, + { + "id": "493563", + "type": "subtitle", + "attributes": { + "subtitle_id": "493563", + "language": "en", + "download_count": 14796, + "new_download_count": 12, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 23.976, + "votes": 1, + "points": 10, + "ratings": 10, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2001-12-04T23:00:00.000Z", + "release": "The Secret Garden", + "comments": "", + "legacy_subtitle_id": 106540, + "uploader": { + "uploader_id": 4143, + "name": "Panayot (a)", + "rank": "bronze member" + }, + "feature_details": { + "feature_id": 518028, + "feature_type": "Movie", + "year": 1993, + "title": "The Secret Garden", + "movie_name": "1993 - The Secret Garden", + "imdb_id": 108071, + "tmdb_id": 11236 + }, + "url": "https://www.opensubtitles.com/en/subtitles/legacy/106540", + "related_links": [ + { + "label": "All subtitles for The Secret Garden", + "url": "https://www.opensubtitles.com/en/movies/1993-the-secret-garden", + "img_url": "https://s9.osdb.link/features/8/2/0/518028.jpg" + } + ], + "files": [ + { + "file_id": 544691, + "cd_number": 1, + "file_name": "TheSecretGarden_EN.sub" + } + ] + } + }, + { + "id": "492466", + "type": "subtitle", + "attributes": { + "subtitle_id": "492466", + "language": "he", + "download_count": 128, + "new_download_count": 1, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2008-02-08T02:00:42.000Z", + "release": "Rocky.1976.DVDRip.XviD.AC3.iNTERNAL-QiM", + "comments": "", + "legacy_subtitle_id": 3246129, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 517932, + "feature_type": "Movie", + "year": 1976, + "title": "Rocky", + "movie_name": "1976 - Rocky", + "imdb_id": 75148, + "tmdb_id": 1366 + }, + "url": "https://www.opensubtitles.com/he/subtitles/legacy/3246129", + "related_links": [ + { + "label": "All subtitles for Rocky", + "url": "https://www.opensubtitles.com/he/movies/1976-rocky", + "img_url": "https://s9.osdb.link/features/2/3/9/517932.jpg" + } + ], + "files": [ + { + "file_id": 6026439, + "cd_number": 2, + "file_name": "qim-rockyb.srt" + }, + { + "file_id": 6026437, + "cd_number": 1, + "file_name": "qim-rockya.srt" + } + ] + } + }, + { + "id": "492560", + "type": "subtitle", + "attributes": { + "subtitle_id": "492560", + "language": "pt-BR", + "download_count": 64, + "new_download_count": 2, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 0, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2017-07-29T22:49:40.000Z", + "release": "jornada", + "comments": "", + "legacy_subtitle_id": 7052825, + "uploader": { + "uploader_id": 3282, + "name": "COF7CJpS", + "rank": "app developer" + }, + "feature_details": { + "feature_id": 517867, + "feature_type": "Movie", + "year": 1985, + "title": "Rambo: First Blood Part II", + "movie_name": "1985 - Rambo: First Blood Part II", + "imdb_id": 89880, + "tmdb_id": 1369 + }, + "url": "https://www.opensubtitles.com/pt-BR/subtitles/legacy/7052825", + "related_links": [ + { + "label": "All subtitles for Rambo: First Blood Part II", + "url": "https://www.opensubtitles.com/pt-BR/movies/1985-rambo-first-blood-part-ii", + "img_url": "https://s9.osdb.link/features/7/6/8/517867.jpg" + } + ], + "files": [ + { + "file_id": 6026472, + "cd_number": 1, + "file_name": "jornada.srt" + } + ] + } + }, + { + "id": "496159", + "type": "subtitle", + "attributes": { + "subtitle_id": "496159", + "language": "de", + "download_count": 226, + "new_download_count": 7, + "hearing_impaired": false, + "hd": false, + "format": "", + "fps": 25, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": true, + "foreign_parts_only": false, + "ai_translated": false, + "machine_translated": false, + "upload_date": "2012-07-21T17:11:45.000Z", + "release": "Komm.und.sieh.1985.German.DVDRip.Retail", + "comments": "RUSCICO / Russian Cinema Council", + "legacy_subtitle_id": 4622375, + "uploader": { + "uploader_id": 41812, + "name": "Ralle1", + "rank": "administrator" + }, + "feature_details": { + "feature_id": 518417, + "feature_type": "Movie", + "year": 1985, + "title": "Come and See", + "movie_name": "1985 - Come and See", + "imdb_id": 91251, + "tmdb_id": 25237 + }, + "url": "https://www.opensubtitles.com/de/subtitles/legacy/4622375", + "related_links": [ + { + "label": "All subtitles for Come and See", + "url": "https://www.opensubtitles.com/de/movies/1985-come-and-see", + "img_url": "https://s9.osdb.link/features/7/1/4/518417.jpg" + } + ], + "files": [ + { + "file_id": 6027938, + "cd_number": 2, + "file_name": "Komm.und.sieh.1985.German.DVDRip.CD2.Retail.srt" + }, + { + "file_id": 6027936, + "cd_number": 1, + "file_name": "Komm.und.sieh.1985.German.DVDRip.CD1.Retail.srt" + } + ] + } + } + ] + } + }, + "example": { + "example": { + "total_pages": 1, + "total_count": 60, + "page": 1, + "data": [ + { + "id": "766099", + "type": "subtitle", + "attributes": { + "subtitle_id": "766099", + "language": "ko", + "download_count": 2836, + "new_download_count": 190, + "hearing_impaired": false, + "hd": true, + "fps": 23.976, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "upload_date": "2016-08-24T17:25:17.000Z", + "release": "The.Twilight.Saga.Breaking.Dawn.Part 1.2011.720p.BrRip.x264.YIFY", + "comments": "", + "legacy_subtitle_id": 6717006, + "uploader": { + "uploader_id": 3282, + "name": "os-auto", + "rank": "Application Developers" + }, + "feature_details": { + "feature_id": 568028, + "feature_type": "Movie", + "year": 2011, + "title": "The Twilight Saga: Breaking Dawn - Part 1", + "movie_name": "2011 - The Twilight Saga: Breaking Dawn - Part 1", + "imdb_id": 1324999, + "tmdb_id": 50619 + }, + "url": "https://www.opensubtitles.com/ko/subtitles/legacy/6717006", + "related_links": { + "label": "All subtitles for The Twilight Saga: Breaking Dawn - Part 1", + "url": "https://www.opensubtitles.com/ko/movies/2011-the-twilight-saga-breaking-dawn-part-1", + "img_url": "https://s9.osdb.link/features/8/2/0/568028.jpg" + }, + "files": [ + { + "file_id": 841356, + "cd_number": 1, + "file_name": "The.Twilight.Saga.Breaking.Dawn.Part 1.2011.720p.BrRip.x264.YIFY.smi" + } + ] + } + }, + { + "id": "4885905", + "type": "subtitle", + "attributes": { + "subtitle_id": "4885905", + "language": "ko", + "download_count": 3838, + "new_download_count": 78, + "hearing_impaired": false, + "hd": true, + "fps": 23.976, + "votes": 0, + "points": 0, + "ratings": 0, + "from_trusted": false, + "foreign_parts_only": false, + "ai_translated": false, + "upload_date": "2019-07-28T19:18:09.000Z", + "release": "The.Wolf.of.Wall.Street.2013.1080p.BluRay.x264-AMIABLE", + "comments": "ksub", + "legacy_subtitle_id": 7845982, + "uploader": { + "uploader_id": 82010, + "name": "ksubscene", + "rank": "Gold member" + }, + "feature_details": { + "feature_id": 586864, + "feature_type": "Movie", + "year": 2013, + "title": "The Wolf of Wall Street", + "movie_name": "2013 - The Wolf of Wall Street", + "imdb_id": 993846, + "tmdb_id": 106646 + }, + "url": "https://www.opensubtitles.com/ko/subtitles/legacy/7845982", + "related_links": { + "label": "All subtitles for The Wolf of Wall Street", + "url": "https://www.opensubtitles.com/ko/movies/2013-the-wolf-of-wall-street", + "img_url": "https://s9.osdb.link/features/4/6/8/586864.jpg" + }, + "files": [ + { + "file_id": 5009225, + "cd_number": 1, + "file_name": "The.Wolf.of.Wall.Street.2013.1080p.BluRay.x264-AMIABLE.smi" + } + ] + } + } + ] + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ] + } + }, + "/features": { + "get": { + "tags": [ + "Features" + ], + "summary": "Search for features", + "description": "With the \"query\" parameter, search for a Feature from a simple text input. Typically used for a text search or autocomplete.\n\nWith an ID, get basic information and subtitles count for a specific title.\n\nWith the \"query_match\" you can define the matched applied to the query: \n - \"start\" is the default behavior, it will query on the first letter entered to offer suggestions\n - \"word\" will return the match on the word, but not always matching the fulll title, for example searching \"roma\" will return \"holiday in roma\"\n - \"exact\" will exactly match the title, so here searching for \"roma\" will only return the movie(s) named \"roma\" \n\nWith the \"full_search\" you can extend the search to the translations of the title, so \"roma\" will also return \"rome\" \n\n\n\n> ### Watch Out!\n>\n> If you create an autocomplete, don't set a too small refresh limit, remember you must not go over 40 requests per 10 seconds!", + "operationId": "features", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "query to search, release/file name accepted", + "schema": { + "minLength": 3, + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "empty to list all or **movie**, **tvshow** or **episode**.", + "schema": { + "type": "string" + } + }, + { + "name": "feature_id", + "in": "query", + "description": "opensubtitles **feature_id**", + "schema": { + "type": "integer" + } + }, + { + "name": "imdb_id", + "in": "query", + "description": "IMDB ID, delete leading zeroes", + "schema": { + "type": "string" + } + }, + { + "name": "tmdb_id", + "in": "query", + "description": "TheMovieDB ID - combine with type to avoid errors", + "schema": { + "type": "string" + } + }, + { + "name": "year", + "in": "query", + "description": "Filter by year. Can only be used in combination with a query", + "schema": { + "type": "integer" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "query_match", + "description": "Type of matching applied to the query: **start** (default), **word**, **exact** " + }, + { + "schema": { + "type": "boolean" + }, + "in": "query", + "name": "full_search", + "description": "Search on original title and title aka (translations) (default false)" + } + ], + "responses": { + "200": { + "description": "Search for a feature", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "movie": { + "$ref": "#/components/schemas/Feature-Movie" + }, + "episode": { + "$ref": "#/components/schemas/Feature-Episode" + }, + "tv": { + "$ref": "#/components/schemas/Feature-Tvshow" + } + }, + "description": "" + }, + "example": { + "data": [ + { + "id": "9803", + "type": "feature", + "attributes": { + "title": "Waking the Dead", + "original_title": "", + "year": "2000", + "subtitles_counts": { + "en": 68, + "es": 41, + "nl": 40, + "ro": 40, + "ru": 30, + "sr": 21, + "pl": 7, + "pt-BR": 4, + "hr": 3, + "pt-PT": 3, + "bg": 2, + "el": 2, + "he": 2, + "fa": 2, + "sl": 2, + "tr": 2, + "ar": 1, + "bs": 1, + "cs": 1, + "et": 1, + "fi": 1, + "fr": 1, + "hu": 1, + "id": 1, + "ja": 1, + "sk": 1, + "th": 1, + "vi": 1 + }, + "subtitles_count": 11, + "seasons_count": 9, + "parent_title": "", + "season_number": 0, + "episode_number": "", + "imdb_id": 259733, + "tmdb_id": 4860, + "parent_imdb_id": "", + "feature_id": "9803", + "title_aka": [ + "Waking the Dead", + " Waking the Dead – Im Auftrag der Toten" + ], + "feature_type": "Tvshow", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead", + "img_url": "https://s9.osdb.link/features/3/0/8/9803.jpg", + "seasons": [ + { + "season_number": 1, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Pilot: Part 1", + "feature_id": 126854, + "feature_imdb_id": 743365 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Pilot: Part 2", + "feature_id": 126863, + "feature_imdb_id": 936019 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Burn Out: Part 1", + "feature_id": 126861, + "feature_imdb_id": 743355 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Burn Out: Part 2", + "feature_id": 126858, + "feature_imdb_id": 936016 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Blind Beggar: Part 1", + "feature_id": 126862, + "feature_imdb_id": 743353 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Blind Beggar: Part 2", + "feature_id": 126859, + "feature_imdb_id": 936017 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" A Simple Sacrifice: Part 1", + "feature_id": 126860, + "feature_imdb_id": 743350 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" A Simple Sacrifice: Part 2", + "feature_id": 126864, + "feature_imdb_id": 936015 + }, + { + "episode_number": 9, + "title": "\"Waking the Dead\" Every Breath You Take: Part 1", + "feature_id": 126865, + "feature_imdb_id": 743358 + }, + { + "episode_number": 10, + "title": "\"Waking the Dead\" Every Breath You Take: Part 2", + "feature_id": 126869, + "feature_imdb_id": 936018 + } + ] + }, + { + "season_number": 2, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Life Sentence: Part 1", + "feature_id": 126868, + "feature_imdb_id": 743363 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Life Sentence: Part 2", + "feature_id": 126871, + "feature_imdb_id": 1108804 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Deathwatch: Part 1", + "feature_id": 126867, + "feature_imdb_id": 743357 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Deathwatch: Part 2", + "feature_id": 126870, + "feature_imdb_id": 1108805 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Special Relationship: Part 1", + "feature_id": 126872, + "feature_imdb_id": 743367 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Special Relationship: Part 2", + "feature_id": 126878, + "feature_imdb_id": 1091606 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Thin Air: Part 1", + "feature_id": 126875, + "feature_imdb_id": 743371 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Thin Air: Part 2", + "feature_id": 126877, + "feature_imdb_id": 1167299 + } + ] + }, + { + "season_number": 3, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Multistorey: Part 1", + "feature_id": 126876, + "feature_imdb_id": 743364 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Multistorey: Part 2", + "feature_id": 126883, + "feature_imdb_id": 1167294 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Walking on Water: Part 1", + "feature_id": 126884, + "feature_imdb_id": 743374 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Walking on Water: Part 2", + "feature_id": 126879, + "feature_imdb_id": 1167302 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Breaking Glass: Part 1", + "feature_id": 126885, + "feature_imdb_id": 743354 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Breaking Glass: Part 2", + "feature_id": 126881, + "feature_imdb_id": 1167289 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Final Cut: Part 1", + "feature_id": 126887, + "feature_imdb_id": 743360 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Final Cut: Part 2", + "feature_id": 126882, + "feature_imdb_id": 1167292 + } + ] + }, + { + "season_number": 4, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" In Sight of the Lord: Part 1", + "feature_id": 126888, + "feature_imdb_id": 743362 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" In Sight of the Lord: Part 2", + "feature_id": 126866, + "feature_imdb_id": 1103924 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" False Flag: Part 1", + "feature_id": 126852, + "feature_imdb_id": 743359 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" False Flag: Part 2", + "feature_id": 126857, + "feature_imdb_id": 1167291 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Fugue States: Part 1", + "feature_id": 126880, + "feature_imdb_id": 743361 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Fugue States: Part 2", + "feature_id": 126886, + "feature_imdb_id": 1167293 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Anger Management: Part 1", + "feature_id": 126889, + "feature_imdb_id": 743351 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Anger Management: Part 2", + "feature_id": 126874, + "feature_imdb_id": 1167287 + }, + { + "episode_number": 9, + "title": "\"Waking the Dead\" The Hardest Word: Part 1", + "feature_id": 126891, + "feature_imdb_id": 743370 + }, + { + "episode_number": 10, + "title": "\"Waking the Dead\" The Hardest Word: Part 2", + "feature_id": 126890, + "feature_imdb_id": 1167298 + }, + { + "episode_number": 11, + "title": "\"Waking the Dead\" Shadowplay: Part 1", + "feature_id": 126893, + "feature_imdb_id": 743366 + }, + { + "episode_number": 12, + "title": "\"Waking the Dead\" Shadowplay: Part 2", + "feature_id": 126892, + "feature_imdb_id": 1167295 + } + ] + }, + { + "season_number": 5, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Towers of Silence: Part 1", + "feature_id": 126810, + "feature_imdb_id": 743372 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Towers of Silence: Part 2", + "feature_id": 126811, + "feature_imdb_id": 1167300 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Black Run: Part 1", + "feature_id": 126814, + "feature_imdb_id": 743352 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Black Run: Part 2", + "feature_id": 126808, + "feature_imdb_id": 1167288 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Subterraneans: Part 1", + "feature_id": 126816, + "feature_imdb_id": 743369 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Subterraneans: Part 2", + "feature_id": 126818, + "feature_imdb_id": 1167297 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Straw Dog: Part 1", + "feature_id": 126820, + "feature_imdb_id": 743368 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Straw Dog: Part 2", + "feature_id": 126822, + "feature_imdb_id": 1167296 + }, + { + "episode_number": 9, + "title": "\"Waking the Dead\" Undertow: Part 1", + "feature_id": 126819, + "feature_imdb_id": 743373 + }, + { + "episode_number": 10, + "title": "\"Waking the Dead\" Undertow: Part 2", + "feature_id": 126821, + "feature_imdb_id": 1167301 + }, + { + "episode_number": 11, + "title": "\"Waking the Dead\" Cold Fusion: Part 1", + "feature_id": 126823, + "feature_imdb_id": 743356 + }, + { + "episode_number": 12, + "title": "\"Waking the Dead\" Cold Fusion: Part 2", + "feature_id": 126817, + "feature_imdb_id": 1167290 + } + ] + }, + { + "season_number": 6, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Wren Boys: Part 1", + "feature_id": 126895, + "feature_imdb_id": 930851 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Wren Boys: Part 2", + "feature_id": 126855, + "feature_imdb_id": 932475 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Deus Ex Machina: Part 1", + "feature_id": 126894, + "feature_imdb_id": 932472 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Deus Ex Machina: Part 2", + "feature_id": 126896, + "feature_imdb_id": 932473 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" The Fall: Part 1", + "feature_id": 126898, + "feature_imdb_id": 875569 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" The Fall: Part 2", + "feature_id": 126873, + "feature_imdb_id": 938259 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Mask of Sanity: Part 1", + "feature_id": 126899, + "feature_imdb_id": 952542 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Mask of Sanity: Part 2", + "feature_id": 126902, + "feature_imdb_id": 952543 + }, + { + "episode_number": 9, + "title": "\"Waking the Dead\" Double Bind: Part 1", + "feature_id": 126897, + "feature_imdb_id": 952540 + }, + { + "episode_number": 10, + "title": "\"Waking the Dead\" Double Bind: Part 2", + "feature_id": 126903, + "feature_imdb_id": 952541 + }, + { + "episode_number": 11, + "title": "\"Waking the Dead\" Yahrzeit: Part 1", + "feature_id": 126901, + "feature_imdb_id": 892727 + }, + { + "episode_number": 12, + "title": "\"Waking the Dead\" Yahrzeit: Part 2", + "feature_id": 126900, + "feature_imdb_id": 942279 + } + ] + }, + { + "season_number": 7, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Missing Persons: Part 1", + "feature_id": 126825, + "feature_imdb_id": 1215441 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Missing Persons: Part 2", + "feature_id": 126824, + "feature_imdb_id": 1215442 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Sins: Part 1", + "feature_id": 126815, + "feature_imdb_id": 1218284 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Sins: Part 2", + "feature_id": 126826, + "feature_imdb_id": 1218285 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Duty and Honour: Part 1", + "feature_id": 126828, + "feature_imdb_id": 1221781 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Duty and Honour: Part 2", + "feature_id": 126830, + "feature_imdb_id": 1221782 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Skin: Part 1", + "feature_id": 126829, + "feature_imdb_id": 1225236 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Skin: Part 2", + "feature_id": 126831, + "feature_imdb_id": 1225237 + }, + { + "episode_number": 9, + "title": "\"Waking the Dead\" Wounds: Part 1", + "feature_id": 126827, + "feature_imdb_id": 1227115 + }, + { + "episode_number": 10, + "title": "\"Waking the Dead\" Wounds: Part 2", + "feature_id": 126834, + "feature_imdb_id": 1227116 + }, + { + "episode_number": 11, + "title": "\"Waking the Dead\" Pieta: Part 1", + "feature_id": 126833, + "feature_imdb_id": 1231221 + }, + { + "episode_number": 12, + "title": "\"Waking the Dead\" Pietà: Part 2", + "feature_id": 126832, + "feature_imdb_id": 1231222 + } + ] + }, + { + "season_number": 8, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Magdalene 26: Part 1", + "feature_id": 126836, + "feature_imdb_id": 1506431 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Magdalene 26: Part 2", + "feature_id": 126839, + "feature_imdb_id": 1506432 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" End of the Night: Part 1", + "feature_id": 126837, + "feature_imdb_id": 1509622 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" End of the Night: Part 2", + "feature_id": 126838, + "feature_imdb_id": 1509623 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Substitute: Part 1", + "feature_id": 126840, + "feature_imdb_id": 1513669 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Substitute: Part 2", + "feature_id": 126842, + "feature_imdb_id": 1514406 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" End Game: Part 1", + "feature_id": 126846, + "feature_imdb_id": 1519231 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" End Game: Part 2", + "feature_id": 126843, + "feature_imdb_id": 1519232 + } + ] + }, + { + "season_number": 9, + "episodes": [ + { + "episode_number": 1, + "title": "\"Waking the Dead\" Harbinger: Part 1", + "feature_id": 126856, + "feature_imdb_id": 1862440 + }, + { + "episode_number": 2, + "title": "\"Waking the Dead\" Harbinger: Part 2", + "feature_id": 126853, + "feature_imdb_id": 1862441 + }, + { + "episode_number": 3, + "title": "\"Waking the Dead\" Care: Part 1", + "feature_id": 126844, + "feature_imdb_id": 1865234 + }, + { + "episode_number": 4, + "title": "\"Waking the Dead\" Care: Part 2", + "feature_id": 126845, + "feature_imdb_id": 1865235 + }, + { + "episode_number": 5, + "title": "\"Waking the Dead\" Solidarity: Part 1", + "feature_id": 126849, + "feature_imdb_id": 1869183 + }, + { + "episode_number": 6, + "title": "\"Waking the Dead\" Solidarity: Part 2", + "feature_id": 126851, + "feature_imdb_id": 1869184 + }, + { + "episode_number": 7, + "title": "\"Waking the Dead\" Conviction: Part 1", + "feature_id": 126848, + "feature_imdb_id": 1877523 + }, + { + "episode_number": 8, + "title": "\"Waking the Dead\" Conviction: Part 2", + "feature_id": 126841, + "feature_imdb_id": 1877524 + }, + { + "episode_number": 9, + "title": "\"Waking the Dead\" Waterloo, Part 1", + "feature_id": 126850, + "feature_imdb_id": 1886367 + }, + { + "episode_number": 10, + "title": "\"Waking the Dead\" Waterloo, Part 2", + "feature_id": 126847, + "feature_imdb_id": 1886368 + } + ] + } + ] + } + }, + { + "id": "126842", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" Substitute: Part 2", + "original_title": "", + "year": "2009", + "subtitles_counts": { + "nl": 1, + "en": 1, + "sr": 1, + "es": 1 + }, + "subtitles_count": 4, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 8, + "episode_number": 6, + "imdb_id": 1514406, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126842", + "title_aka": [ + "\"Waking the Dead\" Substitute: Part 2" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/8/episodes/6-waking-the-dead-substitute-part-2", + "img_url": "https://s9.osdb.link/features/2/4/8/126842.jpg", + "seasons": [] + } + }, + { + "id": "126810", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" Towers of Silence: Part 1", + "original_title": "", + "year": "2005", + "subtitles_counts": { + "es": 10, + "en": 4, + "pl": 3, + "pt-PT": 3, + "bg": 2, + "he": 2, + "fa": 2, + "ru": 2, + "sr": 2, + "tr": 2, + "ro": 2, + "pt-BR": 2, + "ar": 1, + "bs": 1, + "cs": 1, + "nl": 1, + "et": 1, + "fi": 1, + "fr": 1, + "el": 1, + "hr": 1, + "hu": 1, + "id": 1, + "ja": 1, + "sk": 1, + "sl": 1, + "th": 1, + "vi": 1 + }, + "subtitles_count": 52, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 5, + "episode_number": 1, + "imdb_id": 743372, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126810", + "title_aka": [ + "\"Waking the Dead\" Towers of Silence: Part 1" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/5/episodes/1-waking-the-dead-towers-of-silence-part-1", + "img_url": "https://s9.osdb.link/features/0/1/8/126810.jpg", + "seasons": [] + } + }, + { + "id": "646786", + "type": "feature", + "attributes": { + "title": "Waking the Dead", + "original_title": "Waking the Dead", + "year": "2000", + "subtitles_counts": { + "en": 68, + "es": 41, + "nl": 40, + "ro": 40, + "ru": 30, + "sr": 21, + "pl": 7, + "pt-BR": 4, + "hr": 3, + "pt-PT": 3, + "bg": 2, + "el": 2, + "he": 2, + "fa": 2, + "sl": 2, + "tr": 2, + "ar": 1, + "bs": 1, + "cs": 1, + "et": 1, + "fi": 1, + "fr": 1, + "hu": 1, + "id": 1, + "ja": 1, + "sk": 1, + "th": 1, + "vi": 1 + }, + "subtitles_count": 25, + "seasons_count": 0, + "parent_title": "", + "season_number": 0, + "episode_number": "", + "imdb_id": 127349, + "tmdb_id": 37722, + "parent_imdb_id": "", + "feature_id": "646786", + "title_aka": [ + "Waking the Dead", + " Resucitar un amor", + " Le Fantôme de Sarah Williams", + " Szerelmem szelleme", + " Amor Maior que a Vida", + " Пробуждая мертвецов", + " 死亡中惊醒" + ], + "feature_type": "Movie", + "url": "https://www.opensubtitles.com/en/movies/2000-waking-the-dead-5559", + "img_url": "https://s9.osdb.link/features/6/8/7/646786.jpg", + "seasons": [] + } + }, + { + "id": "126847", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" Waterloo, Part 2", + "original_title": "", + "year": "2011", + "subtitles_counts": { + "nl": 1, + "en": 1, + "sr": 1, + "ro": 1 + }, + "subtitles_count": 4, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 9, + "episode_number": 10, + "imdb_id": 1886368, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126847", + "title_aka": [ + "\"Waking the Dead\" Waterloo", + " Part 2" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/9/episodes/10-waking-the-dead-waterloo-part-2", + "img_url": "", + "seasons": [] + } + }, + { + "id": "126857", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" False Flag: Part 2", + "original_title": "", + "year": "2004", + "subtitles_counts": { + "ru": 2, + "ro": 2, + "en": 1 + }, + "subtitles_count": 5, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 4, + "episode_number": 4, + "imdb_id": 1167291, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126857", + "title_aka": [ + "\"Waking the Dead\" False Flag: Part 2" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/4/episodes/4-waking-the-dead-false-flag-part-2", + "img_url": "https://s9.osdb.link/features/7/5/8/126857.jpg", + "seasons": [] + } + }, + { + "id": "62212", + "type": "feature", + "attributes": { + "title": "\"Point Pleasant\" Waking the Dead", + "original_title": "", + "year": "2005", + "subtitles_counts": { + "pl": 3, + "en": 2, + "hu": 2, + "cs": 1, + "nl": 1, + "et": 1, + "fi": 1, + "fr": 1, + "el": 1, + "pt-PT": 1, + "ro": 1, + "pt-BR": 1 + }, + "subtitles_count": 16, + "seasons_count": 0, + "parent_title": "Point Pleasant", + "season_number": 1, + "episode_number": 9, + "imdb_id": 676101, + "tmdb_id": "", + "parent_imdb_id": 435576, + "feature_id": "62212", + "title_aka": [ + "\"Point Pleasant\" Waking the Dead" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2005-point-pleasant/seasons/1/episodes/9-point-pleasant-waking-the-dead", + "img_url": "https://s9.osdb.link/features/2/1/2/62212.jpg", + "seasons": [] + } + }, + { + "id": "126869", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" Every Breath You Take: Part 2", + "original_title": "", + "year": "2001", + "subtitles_counts": { + "en": 1, + "es": 1, + "ro": 1 + }, + "subtitles_count": 3, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 1, + "episode_number": 10, + "imdb_id": 936018, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126869", + "title_aka": [ + "\"Waking the Dead\" Every Breath You Take: Part 2" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/1/episodes/10-waking-the-dead-every-breath-you-take-part-2", + "img_url": "", + "seasons": [] + } + }, + { + "id": "126826", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" Sins: Part 2", + "original_title": "", + "year": "2008", + "subtitles_counts": { + "ru": 2, + "nl": 1, + "en": 1 + }, + "subtitles_count": 4, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 7, + "episode_number": 4, + "imdb_id": 1218285, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126826", + "title_aka": [ + "\"Waking the Dead\" Sins: Part 2" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/7/episodes/4-waking-the-dead-sins-part-2", + "img_url": "", + "seasons": [] + } + }, + { + "id": "126837", + "type": "feature", + "attributes": { + "title": "\"Waking the Dead\" End of the Night: Part 1", + "original_title": "", + "year": "2009", + "subtitles_counts": { + "nl": 2, + "en": 1, + "sr": 1, + "es": 1 + }, + "subtitles_count": 5, + "seasons_count": 0, + "parent_title": "Waking the Dead", + "season_number": 8, + "episode_number": 3, + "imdb_id": 1509622, + "tmdb_id": "", + "parent_imdb_id": 259733, + "feature_id": "126837", + "title_aka": [ + "\"Waking the Dead\" End of the Night: Part 1" + ], + "feature_type": "Episode", + "url": "https://www.opensubtitles.com/en/tvshows/2000-waking-the-dead/seasons/8/episodes/3-waking-the-dead-end-of-the-night-part-1", + "img_url": "https://s9.osdb.link/features/7/3/8/126837.jpg", + "seasons": [] + } + } + ], + "value": "this is test how it is rendered" + } + }, + "example": { + "example": { + "value": { + "data": [ + { + "id": "646193", + "type": "feature", + "attributes": { + "title": "The Matrix", + "original_title": "The Matrix", + "year": "1999", + "subtitles_counts": { + "pl": 151, + "en": 120, + "tr": 68, + "ro": 57, + "es": 55, + "cs": 54, + "pt-BR": 49, + "sl": 41, + "pt-PT": 39, + "sr": 39, + "el": 38, + "he": 31, + "bg": 30, + "nl": 24, + "fr": 20, + "fi": 19, + "hu": 17, + "ar": 16, + "ru": 15, + "hr": 14, + "da": 13, + "et": 11, + "sv": 11, + "sq": 10, + "de": 8, + "bs": 7, + "it": 7, + "ko": 7, + "no": 7, + "fa": 7, + "sk": 7, + "zh-CN": 6, + "mk": 6, + "ms": 4, + "zh-TW": 4, + "bn": 3, + "id": 3, + "lt": 3, + "is": 2, + "ja": 2, + "th": 2, + "my": 1, + "ca": 1, + "hi": 1, + "ml": 1, + "mn": 1, + "uk": 1, + "vi": 1 + }, + "subtitles_count": 1034, + "seasons_count": 0, + "parent_title": "", + "season_number": 0, + "imdb_id": 133093, + "tmdb_id": 603, + "feature_id": "646193", + "title_aka": [ + "Матрицата", + " The Matrix", + " Matrix", + " ماتریکس", + " מטריקס", + " Mátrix", + " マトリックス", + " 매트릭스", + " Matrica", + " 黑客帝国", + " Матрица", + " Матрикс", + " เดอะ เมทริกซ์ : เพาะพันธุ์มนุษย์เหนือโลก 2199", + " Матриця" + ], + "feature_type": "Movie", + "url": "https://www.opensubtitles.com/en/movies/1999-the-matrix", + "img_url": "https://m.media-amazon.com/images/M/MV5BNzQzOTk3OTAtNDQ0Zi00ZTVkLWI0MTEtMDllZjNkYzNjNTc4L2ltYWdlXkEyXkFqcGdeQXVyNjU0OTQ0OTY@._V1_UX182_CR0,0,182,268_AL_.jpg", + "seasons": [] + } + }, + { + "id": "646060", + "type": "feature", + "attributes": { + "title": "The Matrix Revolutions", + "original_title": "The Matrix Revolutions", + "year": "2003", + "subtitles_counts": { + "tr": 29, + "es": 26, + "en": 22, + "sr": 22, + "ro": 20, + "pt-BR": 16, + "bg": 15, + "cs": 14, + "pt-PT": 12, + "sl": 11, + "pl": 10, + "fi": 9, + "fr": 8, + "ar": 7, + "et": 7, + "nl": 6, + "he": 6, + "el": 5, + "hu": 5, + "da": 4, + "mk": 4, + "hr": 3, + "id": 3, + "sk": 3, + "sq": 1, + "zh-CN": 1, + "de": 1, + "is": 1, + "it": 1, + "lt": 1, + "no": 1, + "ru": 1, + "sv": 1 + }, + "subtitles_count": 818, + "seasons_count": 0, + "parent_title": "", + "season_number": 0, + "imdb_id": 242653, + "tmdb_id": 605, + "feature_id": "646060", + "title_aka": [ + "Матрицата: Революции", + " Matrix Revolutions", + " The Matrix: Revolutions", + " The Matrix Revolutions", + " Matrix revolutions", + " انقلاب‌های ماتریکس", + " מטריקס רבולושנס", + " Mátrix - Forradalmak", + " 매트릭스 3: 레볼루션", + " Matrix - Revoluții", + " Матрица: Революция", + " ปฎิวัติมนุษย์เหนือโลก", + " Матриця: Революція", + " 黑客帝国3:矩阵革命" + ], + "feature_type": "Movie", + "url": "https://www.opensubtitles.com/en/movies/2003-the-matrix-revolutions", + "img_url": "https://m.media-amazon.com/images/M/MV5BNzNlZTZjMDctZjYwNi00NzljLWIwN2QtZWZmYmJiYzQ0MTk2XkEyXkFqcGdeQXVyNTAyODkwOQ@@._V1_UX182_CR0,0,182,268_AL_.jpg", + "seasons": [] + } + } + ] + } + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ] + } + }, + "/subtitles": { + "get": { + "tags": [ + "Subtitles" + ], + "summary": "Search for subtitles", + "description": "Find subtitle for a video file. All parameters can be combined following various logics: searching by a specific external id (imdb, tmdb), a file moviehash, or a simple text query.\n\n\n> Something wrong? Read about [common mistakes and best practices](docs/2-Best-Practices.md). \n\n> Getting no results? Follow HTTP redirects! ```curl --location``` and use verbose mode\n\n> Use ```imdb_id for``` movie or episode. Use ```parent_imdb_id``` for TV Shows\n\n\n\nImplement the logic that best fits your needs, keeping in mind the following guidelines:\n\n- If you can obtain the moviehash from the file, please send it along.\n- If you possess the ID, whether it's IMDB or TMDB, send it instead of a query, as an ID provides more precision.\n- When searching for TV show episodes, it is recommended to send the parent ID, along with the episode and season number for optimal results. If you have the unique ID of an episode, only send this ID, excluding the episode or season number.\n- Include the filename as a query parameter along with the moviehash for improved results. If your filenames are generally irrelevant, such as dynamically generated filenames from a streaming service, there's no need to include them.\n- Consider treating parameters as filters rather than additional criteria. If you have a specific ID and send a query with conflicting data, like a wrong year, it could result in fewer matches.\n- Explore querying the /features endpoint to gather the exact list of available episodes.\n- Keep in mind that this is a collaborative project where subtitles are submitted by users, filtered by admins, and movie/show results are processed through various APIs. Occasionally, errors may occur, and we depend on user feedback to address and rectify them.\n\n\n> Avoid http redirection by sending request parameters sorted and without default values, and send all queries in lowercase. Remove leading zeroes in ID parameters (IMDB ID, TMDB ID...)\n\n### Moviehash \nIf a ```moviehash``` is sent with a request, a ```moviehash_match``` boolean field will be added to the response.\n\nThe matching subtitles will always come first in the response.\n\n\n### Ordering\n\n\n> If possible, don't order results, because sorting on server is \"expensive, time consuming operation\" and also you have much higher chance to get cached result when not using this function.\n\nYou can order the results using the ```order_by``` parameter. Ordering is possible on the following fields:\n```language```, ```download_count```, ```new_download_count```, ```hearing_impaired```, ```hd```, ```fps```, ```votes```, ```points```, ```ratings```, ```from_trusted```, ```foreign_parts_only```, ```ai_translated```, ```machine_translated```, ```upload_date```, ```release```, ```comments```\n\nChange the order direction with *order_direction* (asc/desc)\n\n### Final notes\n```ai_translated``` (default include in search results) subtitles should be much better quality than ```machine_translated``` subtitles (excluded in search results).", + "operationId": "subtitles", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "ID of the movie or episode", + "schema": { + "type": "integer" + } + }, + { + "name": "imdb_id", + "in": "query", + "description": "IMDB ID of the movie or episode", + "schema": { + "type": "integer" + } + }, + { + "name": "tmdb_id", + "in": "query", + "description": "TMDB ID of the movie or episode", + "schema": { + "type": "integer" + } + }, + { + "name": "type", + "in": "query", + "description": "movie, episode or all, (default: all) ", + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "file name or text search", + "schema": { + "type": "string" + } + }, + { + "name": "languages", + "in": "query", + "description": "Language code(s), comma separated, sorted in alphabetical order (en,fr)", + "schema": { + "type": "string" + } + }, + { + "name": "moviehash", + "in": "query", + "description": "Moviehash of the moviefile", + "schema": { + "maxLength": 16, + "minLength": 16, + "pattern": "^[a-f0-9]{16}$", + "type": "string" + } + }, + { + "name": "uploader_id", + "in": "query", + "description": "To be used alone - for user uploads listing", + "schema": { + "type": "integer" + } + }, + { + "name": "hearing_impaired", + "in": "query", + "description": "include, exclude, only. (default: include)", + "schema": { + "type": "string" + } + }, + { + "name": "foreign_parts_only", + "in": "query", + "description": "exclude, include, only (default: include)", + "schema": { + "type": "string" + } + }, + { + "name": "trusted_sources", + "in": "query", + "description": "include, only (default: include)", + "schema": { + "type": "string" + } + }, + { + "name": "machine_translated", + "in": "query", + "description": "exclude, include (default: exclude)", + "schema": { + "type": "string" + } + }, + { + "name": "ai_translated", + "in": "query", + "description": "exclude, include (default: include)", + "schema": { + "type": "string" + } + }, + { + "name": "order_by", + "in": "query", + "description": "Order of the returned results, accept any of above fields", + "schema": { + "type": "string" + } + }, + { + "name": "order_direction", + "in": "query", + "description": "Order direction of the returned results (asc,desc)", + "schema": { + "type": "string" + } + }, + { + "name": "parent_feature_id", + "in": "query", + "description": "For Tvshows", + "schema": { + "type": "integer" + } + }, + { + "name": "parent_imdb_id", + "in": "query", + "description": "For Tvshows", + "schema": { + "type": "integer" + } + }, + { + "name": "parent_tmdb_id", + "in": "query", + "description": "For Tvshows", + "schema": { + "type": "integer" + } + }, + { + "name": "season_number", + "in": "query", + "description": "For Tvshows\n", + "schema": { + "type": "integer" + } + }, + { + "name": "episode_number", + "in": "query", + "description": "For Tvshows", + "schema": { + "type": "integer" + } + }, + { + "name": "year", + "in": "query", + "description": "Filter by movie/episode year", + "schema": { + "type": "integer" + } + }, + { + "name": "moviehash_match", + "in": "query", + "description": "include, only (default: include)", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "description": "Results page to display", + "schema": { + "type": "integer" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "responses": { + "200": { + "description": "Find subtitles for a video file ", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "", + "properties": { + "total_pages": { + "type": "number" + }, + "total_count": { + "type": "number" + }, + "page": { + "type": "number" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Subtitle" + } + } + }, + "required": [ + "total_pages", + "total_count", + "page", + "data" + ] + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Api-Key": [] + } + ] + } + }, + "/download": { + "post": { + "tags": [ + "Download" + ], + "summary": "Download", + "description": "Request a download url for a subtitle. Subtitle file in temporary URL will be always in UTF-8 encoding.\n\n\n\n> VERY IMPORTANT: In HTTP request must be both headers: ```Api-Key``` and ```Authorization``` stoplight.io doesn't allow to use in shown example both headers\n\n\n> The download count is calculated on this action, not the file download itself\n\n> IN and OUT FPS must be indicated for subtitle conversions, we want to make sure you know what you are doing, and therefore collected the current FPS from the subtitle search result, or calculated it somehow.\n\n\n\n> The download URL is temporary, and cannot be used more than 3 hours, so do not cache it, but you can download the file more than once if needed.", + "operationId": "download", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "x-examples": { + "example-1": { + "file_id": "", + "sub_format": "", + "file_name": "", + "in_fps": "", + "out_fps": "", + "timeshift": "", + "force_download": "" + } + }, + "required": [ + "file_id" + ], + "properties": { + "file_id": { + "type": "integer", + "description": "file_id from /subtitles search results", + "format": "int32", + "example": 123 + }, + "sub_format": { + "type": "string", + "description": "from /infos/formats" + }, + "file_name": { + "type": "string", + "description": "desired file name" + }, + "in_fps": { + "type": "number", + "description": "used for conversions, in_fps and out_fps must then be indicated" + }, + "out_fps": { + "type": "number", + "description": "used for conversions, in_fps and out_fps must then be indicated" + }, + "timeshift": { + "type": "number", + "description": "delay to add or remove to the subtitle, + or - value, in seconds, i.e. 2.5s or -1s " + }, + "force_download": { + "type": "boolean", + "description": "(1/0) set subtitle file headers to \"application/force-download\"" + } + } + }, + "examples": { + "example-1": { + "value": { + "file_id": 123 + } + } + } + } + }, + "required": false, + "description": "" + }, + "responses": { + "200": { + "description": "Request a download URL for a subtitle. \n", + "content": { + "application/json": { + "schema": { + "description": "", + "type": "object", + "properties": { + "link": { + "type": "string", + "minLength": 1 + }, + "file_name": { + "type": "string", + "minLength": 1 + }, + "requests": { + "type": "number" + }, + "remaining": { + "type": "number" + }, + "message": { + "type": "string", + "minLength": 1 + }, + "reset_time": { + "type": "string", + "minLength": 1 + }, + "reset_time_utc": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "link", + "file_name", + "requests", + "remaining", + "message", + "reset_time", + "reset_time_utc" + ], + "x-examples": { + "example-1": { + "link": "https://www.opensubtitles.com/download/A184A5EA6302F2CA7FD9D49BCEA49A1F36662BBEFB8C9B0ECDC9BB6CAF4BF09A5AA8D7B95C7FBD01615021D1973BAC18D431A8E6A1F627E4617341E8508A6968532088A68B6DDDA996C0116E2CE6F778ED9096A9CAB942B42B59C4EA93F1A7D61FCD6CBBC29C720EBD40CE674A55375862F00981E5D2F315A0982766A2004E0ED0AD9ADABEB506A638F1B829DBC2BE15979F22DA123523967F4D4069BC32098F1086F09AAA776CC365ED744633FD5FA7160B65A2C83539DF30134F5BE6272E46019AF9FD2423AFE12E1DC8642CDB56B8FEB9A4C1F30BF68EF431A3D4ABD3A7E44559E3E572210E5A5A33EC282D3445C537C5DA9DA598300A9900FA1B3B92983FD1504FDDFB34F89E409BF03EC662FC5734F25843C277A64B7C603156926FC6C74AA1D14AABEA6E20/subfile/castle.rock.s01e03.webrip.x264-tbs.ettv.-eng.ro.srt", + "file_name": "castle.rock.s01e03.webrip.x264-tbs.ettv.-eng.ro.srt", + "requests": 3, + "remaining": 97, + "message": "Your quota will be renewed in 07 hours and 40 minutes (2022-04-08 13:03:15 UTC) ", + "reset_time": "07 hours and 40 minutes", + "reset_time_utc": "2022-04-08T13:03:15.000Z" + } + } + }, + "examples": { + "example-1": { + "value": { + "link": "https://www.opensubtitles.com/download/A184A5EA6302F2CA7FD9D49BCEA49A1F36662BBEFB8C9B0ECDC9BB6CAF4BF09A5AA8D7B95C7FBD01615021D1973BAC18D431A8E6A1F627E4617341E8508A6968532088A68B6DDDA996C0116E2CE6F778ED9096A9CAB942B42B59C4EA93F1A7D61FCD6CBBC29C720EBD40CE674A55375862F00981E5D2F315A0982766A2004E0ED0AD9ADABEB506A638F1B829DBC2BE15979F22DA123523967F4D4069BC32098F1086F09AAA776CC365ED744633FD5FA7160B65A2C83539DF30134F5BE6272E46019AF9FD2423AFE12E1DC8642CDB56B8FEB9A4C1F30BF68EF431A3D4ABD3A7E44559E3E572210E5A5A33EC282D3445C537C5DA9DA598300A9900FA1B3B92983FD1504FDDFB34F89E409BF03EC662FC5734F25843C277A64B7C603156926FC6C74AA1D14AABEA6E20/subfile/castle.rock.s01e03.webrip.x264-tbs.ettv.-eng.ro.srt", + "file_name": "castle.rock.s01e03.webrip.x264-tbs.ettv.-eng.ro.srt", + "requests": 3, + "remaining": 97, + "message": "Your quota will be renewed in 07 hours and 30 minutes (2022-04-08 13:03:16 UTC) ", + "reset_time": "07 hours and 30 minutes", + "reset_time_utc": "2022-04-08T13:03:16.000Z" + } + } + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "Bearer": [] + }, + { + "Api-Key": [] + } + ], + "x-codegen-request-body-name": "body" + }, + "parameters": [] + }, + "/utilities/guessit": { + "get": { + "tags": [ + "Utilities" + ], + "summary": "Guessit", + "description": "Extracts as much information as possible from a video filename.\n\nIt has a very powerful matcher that allows to guess properties from a video using its filename only. This matcher works with both movies and tv shows episodes.\n\nThis is a simple implementation of the python guessit library.\nhttps://guessit-io.github.io/guessit/\n\nFind examples of the returned data.\nhttps://guessit-io.github.io/guessit/properties/", + "operationId": "guessit", + "parameters": [ + { + "name": "filename", + "in": "query", + "description": "File name", + "schema": { + "type": "string" + } + }, + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "User-Agent", + "description": "<<{{APP_NAME}} v{{APP_VERSION}}>>" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "required": [ + "audio_channels", + "audio_codec", + "language", + "other", + "release_group", + "screen_size", + "source", + "streaming_service", + "subtitle_language", + "title", + "type", + "video_codec", + "year" + ], + "type": "object", + "properties": { + "title": { + "minLength": 1, + "type": "string" + }, + "year": { + "type": "number" + }, + "language": { + "minLength": 1, + "type": "string" + }, + "subtitle_language": { + "minLength": 1, + "type": "string" + }, + "screen_size": { + "minLength": 1, + "type": "string" + }, + "streaming_service": { + "minLength": 1, + "type": "string" + }, + "source": { + "minLength": 1, + "type": "string" + }, + "other": { + "minLength": 1, + "type": "string" + }, + "audio_codec": { + "minLength": 1, + "type": "string" + }, + "audio_channels": { + "minLength": 1, + "type": "string" + }, + "video_codec": { + "minLength": 1, + "type": "string" + }, + "release_group": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + } + }, + "description": "" + } + } + } + } + }, + "security": [ + { + "Api-Key": [] + } + ] + } + } + }, + "components": { + "schemas": { + "Subtitle": { + "type": "object", + "x-tags": [ + "Models" + ], + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "type": "object", + "required": [ + "subtitle_id", + "language", + "download_count", + "new_download_count", + "from_trusted", + "foreign_parts_only", + "ai_translated", + "machine_translated", + "upload_date", + "feature_details", + "url", + "files" + ], + "properties": { + "subtitle_id": { + "minLength": 1, + "type": "string" + }, + "language": { + "minLength": 1, + "type": "string" + }, + "download_count": { + "type": "number" + }, + "new_download_count": { + "type": "number" + }, + "hearing_impaired": { + "type": "boolean" + }, + "hd": { + "type": "boolean" + }, + "fps": { + "type": "number" + }, + "votes": { + "type": "number" + }, + "points": { + "type": "number" + }, + "ratings": { + "type": "number" + }, + "from_trusted": { + "type": "boolean" + }, + "foreign_parts_only": { + "type": "boolean" + }, + "ai_translated": { + "type": "boolean" + }, + "machine_translated": { + "type": "boolean" + }, + "upload_date": { + "minLength": 1, + "type": "string" + }, + "release": { + "minLength": 1, + "type": "string" + }, + "comments": { + "type": "string" + }, + "legacy_subtitle_id": { + "type": "number" + }, + "uploader": { + "type": "object", + "properties": { + "uploader_id": { + "type": "number" + }, + "name": { + "type": "string" + }, + "rank": { + "type": "string" + } + } + }, + "feature_details": { + "type": "object", + "required": [ + "feature_id", + "feature_type", + "title", + "movie_name", + "imdb_id" + ], + "properties": { + "feature_id": { + "type": "number" + }, + "feature_type": { + "minLength": 1, + "type": "string" + }, + "year": { + "type": "number" + }, + "title": { + "minLength": 1, + "type": "string" + }, + "movie_name": { + "minLength": 1, + "type": "string" + }, + "imdb_id": { + "type": "number" + }, + "tmdb_id": { + "type": "number" + } + } + }, + "url": { + "minLength": 1, + "type": "string" + }, + "related_links": { + "type": "array", + "items": { + "type": "object" + } + }, + "files": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "type": "object", + "properties": { + "file_id": { + "type": "number" + }, + "cd_number": { + "type": "number" + }, + "file_name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "file_id", + "file_name" + ] + } + } + } + } + }, + "required": [ + "id", + "type", + "attributes" + ] + }, + "Feature-Tvshow": { + "required": [ + "attributes", + "id", + "type" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "required": [ + "feature_id", + "imdb_id", + "img_url", + "original_title", + "seasons", + "subtitles_count", + "subtitles_counts", + "title", + "title_aka", + "tmdb_id", + "url", + "year" + ], + "type": "object", + "properties": { + "title": { + "minLength": 1, + "type": "string" + }, + "original_title": { + "minLength": 1, + "type": "string" + }, + "year": { + "minLength": 1, + "type": "string" + }, + "imdb_id": { + "type": "number" + }, + "tmdb_id": { + "type": "number" + }, + "title_aka": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "feature_id": { + "minLength": 1, + "type": "string" + }, + "url": { + "minLength": 1, + "type": "string" + }, + "img_url": { + "minLength": 1, + "type": "string" + }, + "subtitles_counts": { + "required": [ + "ar", + "bg", + "bs", + "ca", + "cs", + "da", + "de", + "el", + "en", + "es", + "et", + "fa", + "fi", + "fr", + "he", + "hr", + "hu", + "id", + "it", + "ja", + "ko", + "mk", + "nl", + "no", + "pl", + "pt-BR", + "pt-PT", + "ro", + "ru", + "sk", + "sl", + "sr", + "sv", + "th", + "tr", + "vi", + "zh-CN", + "zh-TW" + ], + "type": "object", + "properties": { + "pl": { + "type": "number" + }, + "en": { + "type": "number" + }, + "pt-BR": { + "type": "number" + }, + "ro": { + "type": "number" + }, + "nl": { + "type": "number" + }, + "pt-PT": { + "type": "number" + }, + "es": { + "type": "number" + }, + "he": { + "type": "number" + }, + "hu": { + "type": "number" + }, + "el": { + "type": "number" + }, + "fr": { + "type": "number" + }, + "tr": { + "type": "number" + }, + "cs": { + "type": "number" + }, + "fi": { + "type": "number" + }, + "ar": { + "type": "number" + }, + "hr": { + "type": "number" + }, + "sl": { + "type": "number" + }, + "bg": { + "type": "number" + }, + "sr": { + "type": "number" + }, + "sv": { + "type": "number" + }, + "de": { + "type": "number" + }, + "et": { + "type": "number" + }, + "da": { + "type": "number" + }, + "bs": { + "type": "number" + }, + "it": { + "type": "number" + }, + "mk": { + "type": "number" + }, + "ru": { + "type": "number" + }, + "no": { + "type": "number" + }, + "th": { + "type": "number" + }, + "vi": { + "type": "number" + }, + "ja": { + "type": "number" + }, + "fa": { + "type": "number" + }, + "zh-CN": { + "type": "number" + }, + "ca": { + "type": "number" + }, + "id": { + "type": "number" + }, + "sk": { + "type": "number" + }, + "ko": { + "type": "number" + }, + "zh-TW": { + "type": "number" + } + } + }, + "subtitles_count": { + "type": "number" + }, + "seasons": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "season_number" + ], + "type": "object", + "properties": { + "season_number": { + "type": "number" + }, + "episodes": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "episode_number", + "feature_id", + "feature_imdb_id", + "title" + ], + "type": "object", + "properties": { + "episode_number": { + "type": "number" + }, + "title": { + "minLength": 1, + "type": "string" + }, + "feature_id": { + "type": "number" + }, + "feature_imdb_id": { + "type": "number" + } + } + } + } + } + } + } + } + } + }, + "description": "", + "x-tags": [ + "Models" + ] + }, + "Feature-Episode": { + "required": [ + "attributes", + "id", + "type" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "required": [ + "episode_number", + "feature_id", + "imdb_id", + "img_url", + "parent_title", + "season_number", + "subtitles_count", + "subtitles_counts", + "title", + "title_aka", + "tmdb_id", + "url", + "year" + ], + "type": "object", + "properties": { + "title": { + "minLength": 1, + "type": "string" + }, + "original_title": { + "type": "object" + }, + "year": { + "minLength": 1, + "type": "string" + }, + "parent_imdb_id": { + "type": "object" + }, + "parent_title": { + "minLength": 1, + "type": "string" + }, + "season_number": { + "type": "number" + }, + "episode_number": { + "type": "number" + }, + "imdb_id": { + "type": "number" + }, + "tmdb_id": { + "type": "number" + }, + "title_aka": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "feature_id": { + "minLength": 1, + "type": "string" + }, + "url": { + "minLength": 1, + "type": "string" + }, + "img_url": { + "minLength": 1, + "type": "string" + }, + "subtitles_counts": { + "required": [ + "ar", + "bg", + "bs", + "cs", + "da", + "de", + "el", + "en", + "es", + "et", + "fi", + "fr", + "he", + "hr", + "hu", + "mk", + "nl", + "no", + "pl", + "pt-BR", + "pt-PT", + "ro", + "ru", + "sl", + "sr", + "sv", + "th", + "tr" + ], + "type": "object", + "properties": { + "pl": { + "type": "number" + }, + "en": { + "type": "number" + }, + "pt-BR": { + "type": "number" + }, + "es": { + "type": "number" + }, + "ro": { + "type": "number" + }, + "nl": { + "type": "number" + }, + "tr": { + "type": "number" + }, + "he": { + "type": "number" + }, + "pt-PT": { + "type": "number" + }, + "cs": { + "type": "number" + }, + "fi": { + "type": "number" + }, + "hu": { + "type": "number" + }, + "ar": { + "type": "number" + }, + "bg": { + "type": "number" + }, + "fr": { + "type": "number" + }, + "sl": { + "type": "number" + }, + "el": { + "type": "number" + }, + "hr": { + "type": "number" + }, + "sr": { + "type": "number" + }, + "et": { + "type": "number" + }, + "sv": { + "type": "number" + }, + "th": { + "type": "number" + }, + "bs": { + "type": "number" + }, + "da": { + "type": "number" + }, + "de": { + "type": "number" + }, + "mk": { + "type": "number" + }, + "no": { + "type": "number" + }, + "ru": { + "type": "number" + } + } + }, + "subtitles_count": { + "type": "number" + } + } + } + }, + "description": "", + "x-tags": [ + "Models" + ] + }, + "Feature-Movie": { + "required": [ + "attributes", + "id", + "type" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "required": [ + "feature_id", + "feature_type", + "imdb_id", + "img_url", + "original_title", + "parent_title", + "season_number", + "seasons_count", + "subtitles_count", + "subtitles_counts", + "title", + "title_aka", + "tmdb_id", + "url", + "year" + ], + "type": "object", + "properties": { + "title": { + "minLength": 1, + "type": "string" + }, + "original_title": { + "minLength": 1, + "type": "string" + }, + "year": { + "minLength": 1, + "type": "string" + }, + "subtitles_counts": { + "required": [ + "ar", + "bg", + "bn", + "cs", + "da", + "de", + "el", + "en", + "es", + "et", + "eu", + "fa", + "fi", + "fr", + "he", + "hr", + "hu", + "id", + "it", + "ja", + "ka", + "ko", + "lt", + "mk", + "ml", + "ms", + "nl", + "no", + "pl", + "pt-BR", + "pt-PT", + "ro", + "ru", + "sk", + "sl", + "sr", + "sv", + "ta", + "tr", + "uk", + "vi", + "ze", + "zh-CN", + "zh-TW" + ], + "type": "object", + "properties": { + "en": { + "type": "number" + }, + "pt-PT": { + "type": "number" + }, + "fi": { + "type": "number" + }, + "pt-BR": { + "type": "number" + }, + "es": { + "type": "number" + }, + "ar": { + "type": "number" + }, + "pl": { + "type": "number" + }, + "sr": { + "type": "number" + }, + "id": { + "type": "number" + }, + "ro": { + "type": "number" + }, + "zh-CN": { + "type": "number" + }, + "nl": { + "type": "number" + }, + "el": { + "type": "number" + }, + "hu": { + "type": "number" + }, + "fr": { + "type": "number" + }, + "sl": { + "type": "number" + }, + "tr": { + "type": "number" + }, + "et": { + "type": "number" + }, + "bg": { + "type": "number" + }, + "cs": { + "type": "number" + }, + "de": { + "type": "number" + }, + "he": { + "type": "number" + }, + "it": { + "type": "number" + }, + "vi": { + "type": "number" + }, + "hr": { + "type": "number" + }, + "ko": { + "type": "number" + }, + "no": { + "type": "number" + }, + "sv": { + "type": "number" + }, + "ta": { + "type": "number" + }, + "eu": { + "type": "number" + }, + "da": { + "type": "number" + }, + "fa": { + "type": "number" + }, + "sk": { + "type": "number" + }, + "uk": { + "type": "number" + }, + "zh-TW": { + "type": "number" + }, + "bn": { + "type": "number" + }, + "ka": { + "type": "number" + }, + "ja": { + "type": "number" + }, + "lt": { + "type": "number" + }, + "mk": { + "type": "number" + }, + "ml": { + "type": "number" + }, + "ms": { + "type": "number" + }, + "ru": { + "type": "number" + }, + "ze": { + "type": "number" + } + } + }, + "subtitles_count": { + "type": "number" + }, + "seasons_count": { + "type": "number" + }, + "parent_title": { + "type": "string" + }, + "season_number": { + "type": "number" + }, + "episode_number": { + "type": "object" + }, + "imdb_id": { + "type": "number" + }, + "tmdb_id": { + "type": "number" + }, + "parent_imdb_id": { + "type": "object" + }, + "feature_id": { + "minLength": 1, + "type": "string" + }, + "title_aka": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "feature_type": { + "minLength": 1, + "type": "string" + }, + "url": { + "minLength": 1, + "type": "string" + }, + "img_url": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "", + "x-tags": [ + "Models" + ] + } + }, + "securitySchemes": { + "Api-Key": { + "type": "apiKey", + "description": "Application API key obtained in the user profile of app developer on opensubtitles.com to authorise the **application**", + "name": "Api-Key", + "in": "header" + }, + "Bearer": { + "type": "http", + "description": "User token created in the login endpoint to authorise opensubtitles.com **user**", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + }, + "security": [ + { + "Api-Key": [] + }, + { + "Bearer": [] + } + ] +} \ No newline at end of file diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java index 8b9ddbd3..eedbee80 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java @@ -1,10 +1,8 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; -import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; @@ -31,10 +29,14 @@ void testKeywordMatchFilter() { Release release = createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); Subtitle subtitle1 = createSubtitle("Criminal.Minds.S10E12.HDTV.XviD-AFG.srt", "AFG", false, ""); Subtitle subtitle2 = createSubtitle("criminal.minds.1012.hdtv-lol.srt", "lol", false, ""); - Subtitle subtitle3 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); - Subtitle subtitle4 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); - Subtitle subtitle5 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, "720p HDTV X264"); - Subtitle subtitle6 = createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); + Subtitle subtitle3 = + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); + Subtitle subtitle4 = + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + Subtitle subtitle5 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, + "720p HDTV X264"); + Subtitle subtitle6 = + createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); // only keyword assertThatFilter(new SubtitleFiltering(createSettings(true, false, false))) @@ -54,9 +56,12 @@ void testExactMatchFilter() { Release release = createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); Subtitle subtitle1 = createSubtitle("Criminal.Minds.S10E12.HDTV.XviD-AFG.srt", "AFG", false, ""); Subtitle subtitle2 = createSubtitle("criminal.minds.1012.hdtv-lol.srt", "lol", false, ""); - Subtitle subtitle3 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); - Subtitle subtitle4 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); - Subtitle subtitle5 = createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); + Subtitle subtitle3 = + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); + Subtitle subtitle4 = + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + Subtitle subtitle5 = + createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); // only exact match assertThatFilter(new SubtitleFiltering(createSettings(false, true, false))) @@ -76,10 +81,14 @@ void testExactMatchAndKeywordMatchFilter() { Release release = createRelease("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); Subtitle subtitle1 = createSubtitle("Criminal.Minds.S10E12.HDTV.XviD-AFG.srt", "AFG", false, ""); Subtitle subtitle2 = createSubtitle("criminal.minds.1012.hdtv-lol.srt", "lol", false, ""); - Subtitle subtitle3 = createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); - Subtitle subtitle4 = createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); - Subtitle subtitle5 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); - Subtitle subtitle6 = createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); + Subtitle subtitle3 = + createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); + Subtitle subtitle4 = + createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + Subtitle subtitle5 = + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + Subtitle subtitle6 = + createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); // only exact match assertThatFilter(new SubtitleFiltering(createSettings(true, true, false))) @@ -97,9 +106,9 @@ void testExactMatchAndKeywordMatchFilter() { private Settings createSettings(boolean keyword, boolean exact, boolean excludehearing) { Settings settings = mock(Settings.class); - when(settings.isOptionSubtitleExactMatch()).thenReturn(exact); - when(settings.isOptionSubtitleKeywordMatch()).thenReturn(keyword); - when(settings.isOptionSubtitleExcludeHearingImpaired()).thenReturn(excludehearing); + when(settings.optionSubtitleExactMatch).thenReturn(exact); + when(settings.optionSubtitleKeywordMatch).thenReturn(keyword); + when(settings.optionSubtitleExcludeHearingImpaired).thenReturn(excludehearing); return settings; } @@ -107,9 +116,9 @@ private Settings createSettings(boolean keyword, boolean exact, boolean excludeh private Release createRelease(String filename, String releasegroup) { Release release = mock(Release.class); - when(release.getFileName()).thenReturn(filename); - when(release.getExtension()).thenReturn("mkv"); - when(release.getReleaseGroup()).thenReturn(releasegroup); + when(release.fileName).thenReturn(filename); + when(release.extension).thenReturn("mkv"); + when(release.releaseGroup).thenReturn(releasegroup); return release; } @@ -117,10 +126,10 @@ private Release createRelease(String filename, String releasegroup) { private Subtitle createSubtitle(String filename, String releaseGroup, boolean excludeHearing, String quality) { Subtitle subtitle = mock(Subtitle.class); - when(subtitle.getFileName()).thenReturn(filename); - when(subtitle.getReleaseGroup()).thenReturn(releaseGroup); - when(subtitle.getQuality()).thenReturn(quality); - when(subtitle.isHearingImpaired()).thenReturn(excludeHearing); + when(subtitle.fileName).thenReturn(filename); + when(subtitle.releaseGroup).thenReturn(releaseGroup); + when(subtitle.quality).thenReturn(quality); + when(subtitle.hearingImpaired).thenReturn(excludeHearing); return subtitle; } @@ -143,7 +152,8 @@ private interface TestSetupMatchesIntf { void matchesSubtitles(Subtitle... subtitles); } - private static class TestSetupFiltering implements TestSetupSubtitlesIntf, TestSetupReleaseIntf, TestSetupMatchesIntf { + private static class TestSetupFiltering + implements TestSetupSubtitlesIntf, TestSetupReleaseIntf, TestSetupMatchesIntf { private SubtitleFiltering filter; private List subtitles; private Release release; @@ -154,7 +164,7 @@ public TestSetupFiltering assertThatFilter(SubtitleFiltering filter) { } public TestSetupFiltering appliedOnSubtitles(Subtitle... subtitles) { - this.subtitles = Arrays.stream(subtitles).toList(); + this.subtitles = subtitles.stream().toList(); return this; } @@ -164,10 +174,11 @@ public TestSetupFiltering forRelease(Release release) { } public void matchesSubtitles(Subtitle... subtitles) { - List filteredSubtitles = this.subtitles.stream().filter(subtitle -> filter.useSubtitle(subtitle, release)).toList(); + List filteredSubtitles = + this.subtitles.stream().filter(subtitle -> filter.useSubtitle(subtitle, release)).toList(); assertThat(filteredSubtitles) .withFailMessage("Expected the filtered subtitles to contain exactly %s, but found %s".formatted( - Arrays.stream(subtitles).map(Subtitle::getFileName).toList(), + subtitles.stream().map(Subtitle::getFileName).toList(), filteredSubtitles.stream().map(Subtitle::getFileName).toList())) .containsExactlyInAnyOrder(subtitles); } diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java index 311ebc7f..38bdb3f0 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java @@ -12,7 +12,7 @@ class ScoreCalculatorTest { @Test - void test_it_calculates_the_score_for_subtitle() throws Exception { + void test_it_calculates_the_score_for_subtitle() { SortWeight weights = createWeights("DVDRip XviD", "MEDiEVAL"); ScoreCalculator calculator = new ScoreCalculator(weights); @@ -44,17 +44,17 @@ void test_it_calculates_the_score_for_subtitle() throws Exception { private Subtitle createSubtitle(String filename, String quality, String team) { Subtitle subtitle = mock(Subtitle.class); - when(subtitle.getFileName()).thenReturn(filename); - when(subtitle.getQuality()).thenReturn(quality); - when(subtitle.getReleaseGroup()).thenReturn(team); + when(subtitle.fileName).thenReturn(filename); + when(subtitle.quality).thenReturn(quality); + when(subtitle.releaseGroup).thenReturn(team); return subtitle; } private SortWeight createWeights(String quality, String group) { // Arrested.Development.S01E01.DVDRip.XviD-MEDiEVAL Release release = mock(Release.class); - when(release.getQuality()).thenReturn(quality); - when(release.getReleaseGroup()).thenReturn(group); + when(release.quality).thenReturn(quality); + when(release.releaseGroup).thenReturn(group); HashMap definedWeights = new HashMap<>(); definedWeights.put("hdtv", 2); diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java index 16651f04..34a634ce 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java @@ -15,8 +15,8 @@ class SortWeightTest { void test_it_generates_weights_for_release() throws Exception { // Arrested.Development.S01E01.DVDRip.XviD-MEDiEVAL Release release = mock(Release.class); - when(release.getQuality()).thenReturn("DVDRip XviD"); - when(release.getReleaseGroup()).thenReturn("MEDiEVAL"); + when(release.quality).thenReturn("DVDRip XviD"); + when(release.releaseGroup).thenReturn("MEDiEVAL"); HashMap definedWeights = new HashMap<>(); definedWeights.put("dvdrip", 2); diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java index 8cdf59d4..70d23619 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java @@ -21,7 +21,7 @@ public void setUp() throws Exception { @Test void test_it_replaces_the_keyword_group_to_a_releasename() throws Exception { Release release = mock(Release.class); - when(release.getReleaseGroup()).thenReturn("Acme"); + when(release.releaseGroup).thenReturn("Acme"); HashMap definedWeights = new HashMap<>(); definedWeights.put("%GROUP%", 5); @@ -38,7 +38,7 @@ void test_it_replaces_the_keyword_group_to_a_releasename() throws Exception { @Test void testEmptyWeights() throws Exception { Release release = mock(Release.class); - when(release.getReleaseGroup()).thenReturn("Acme"); + when(release.releaseGroup).thenReturn("Acme"); HashMap definedWeights = new HashMap<>(); diff --git a/SubLibrary/pom.xml b/SubLibrary/pom.xml index cadd27b4..414f9904 100644 --- a/SubLibrary/pom.xml +++ b/SubLibrary/pom.xml @@ -61,10 +61,14 @@ org.jsoup - jsoup - - - org.hsqldb + jsoup + + + org.jspecify + jspecify + + + org.hsqldb hsqldb @@ -79,6 +83,14 @@ com.uwetrottmann.thetvdb-java thetvdb-java + + systems.manifold + manifold-ext-rt + + + systems.manifold + manifold-props-rt + org.projectlombok lombok @@ -124,38 +136,40 @@ org.apache.maven.plugins maven-compiler-plugin + + + -Xplugin:Manifold + + + + systems.manifold + manifold-ext + ${manifold.version} + + + systems.manifold + manifold-props + ${manifold.version} + + + systems.manifold + manifold-strings + ${manifold.version} + + + - org.openapitools - openapi-generator-maven-plugin - - - - generate - - - java - ${project.basedir}/src/main/resources/opensubtitles/open-api.json - ${project.build.directory}/generated-source/openapi - org.opensubtitles.model - org.opensubtitles.api - org.opensubtitles.invoker - org.opensubtitles - - - Authentication,Download,Subtitles - false - - integer=int,int=int - false - true - is - false - - - - - + org.apache.maven.plugins + maven-jar-plugin + + + + + java,class + + + diff --git a/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java b/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java new file mode 100644 index 00000000..09d754a5 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java @@ -0,0 +1,18 @@ +package extensions.java.io.InputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class InputStreamExt { + + public static String asString(@This InputStream inputStream, Charset charset) throws IOException { + return new String(inputStream.readAllBytes(), charset); + } +} diff --git a/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java b/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java new file mode 100644 index 00000000..78e7026d --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java @@ -0,0 +1,33 @@ +package extensions.java.lang.String; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.apache.commons.lang3.StringUtils; + +@UtilityClass +@Extension +public class StringExt { + + public static String removeIllegalFilenameChars(@This String s) { + return s.replace("/", "").replace("\0", ""); + } + + public static String removeIllegalWindowsChars(@This String text) { + return StringUtils.removeEnd(text.replaceAll("[\\\\/:*?\"<>|]", ""), ".").trim(); + } + + public static String urlEncode(@This String text) { + return URLEncoder.encode(text, StandardCharsets.UTF_8); + } + + public static InputStream toInputStream(@This String text, Charset charset) { + return new ByteArrayInputStream(text.getBytes(charset)); + } +} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/FileUtils.java b/SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java similarity index 86% rename from SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/FileUtils.java rename to SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java index ddbed2ed..425a0435 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/FileUtils.java +++ b/SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java @@ -1,4 +1,4 @@ -package org.lodder.subtools.sublibrary.util; +package extensions.java.nio.file.Path; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -15,28 +15,32 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.commons.lang3.StringUtils; - -import lombok.experimental.ExtensionMethod; +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; import name.falgout.jeffrey.throwing.ThrowingConsumer; import name.falgout.jeffrey.throwing.ThrowingFunction; +import org.apache.commons.lang3.StringUtils; +import org.lodder.subtools.sublibrary.util.CopyDirVisitor; +import org.lodder.subtools.sublibrary.util.DeleteDirVisitor; -@ExtensionMethod({ StreamExtension.class }) -public class FileUtils { +@UtilityClass +@Extension +public class PathExt { - public static String getExtension(Path path) { + public static String getExtension(@This Path path) { return StringUtils.substringAfterLast(path.getFileName().toString(), "."); } - public static boolean hasExtension(Path path, String extension) { + public static boolean hasExtension(@This Path path, String extension) { return extension.equalsIgnoreCase(getExtension(path)); } - public static String changeExtension(Path path, String newExtension) { + public static String changeExtension(@This Path path, String newExtension) { return StringUtils.substringBeforeLast(path.getFileName().toString(), ".") + "." + newExtension; } - public static String withoutExtension(Path path) { + public static String withoutExtension(@This Path path) { return changeExtension(path, ""); } @@ -44,7 +48,7 @@ public static String withoutExtension(String path) { return StringUtils.substringBeforeLast(path, "."); } - public static String toAbsolutePathAsString(Path path) { + public static String toAbsolutePathAsString(@This Path path) { return path.toAbsolutePath().toString(); } @@ -74,7 +78,7 @@ public static String toAbsolutePathAsString(Path path) { * @throws IOException * if an I/O error occurs while moving the path */ - public static Path moveToDir(Path source, Path destinationDir, StandardCopyOption... copyOptions) throws IOException { + public static Path moveToDir(@This Path source, Path destinationDir, StandardCopyOption... copyOptions) throws IOException { return moveToDirAndRename(source, destinationDir, source.getFileName().toString(), copyOptions); } @@ -107,7 +111,7 @@ public static Path moveToDir(Path source, Path destinationDir, StandardCopyOptio * @throws IOException * if an I/O error occurs while moving the path */ - public static Path moveToDirAndRename(Path source, Path destinationDir, String newFileName, StandardCopyOption... copyOptions) + public static Path moveToDirAndRename(@This Path source, Path destinationDir, String newFileName, StandardCopyOption... copyOptions) throws IOException { Files.createDirectories(destinationDir); if (Files.isRegularFile(source)) { @@ -155,7 +159,7 @@ private static Path moveNonEmptyDirectoryRecursively(Path source, Path target, S * if an I/O error occurs while deleting the path */ // TODO change name? (nameclash) - public static void delete(Path path) throws IOException { + public static void deletePath(@This Path path) throws IOException { Files.walkFileTree(path, new DeleteDirVisitor()); } @@ -185,7 +189,7 @@ public static void delete(Path path) throws IOException { * @throws IOException * if an I/O error occurs while deleting the path */ - public static Path copyToDir(Path source, Path destinationDir, StandardCopyOption... copyOptions) throws IOException { + public static Path copyToDir(@This Path source, Path destinationDir, StandardCopyOption... copyOptions) throws IOException { return copyToDirAndRename(source, destinationDir, source.getFileName().toString(), copyOptions); } @@ -218,7 +222,7 @@ public static Path copyToDir(Path source, Path destinationDir, StandardCopyOptio * @throws IOException * if an I/O error occurs while deleting the path */ - public static Path copyToDirAndRename(Path source, Path destinationDir, String newFileName, StandardCopyOption... copyOptions) + public static Path copyToDirAndRename(@This Path source, Path destinationDir, String newFileName, StandardCopyOption... copyOptions) throws IOException { if (Files.isRegularFile(source)) { Files.createDirectories(destinationDir); @@ -233,36 +237,36 @@ public static Path copyToDirAndRename(Path source, Path destinationDir, String n } } - public static String getFileNameAsString(Path path) { + public static String getFileNameAsString(@This Path path) { return path.getFileName().toString(); } - public static boolean fileNameContains(Path path, String text) { + public static boolean fileNameContains(@This Path path, String text) { return path.getFileName().toString().contains(text); } - public static boolean fileNameContainsIgnoreCase(Path path, String text) { + public static boolean fileNameContainsIgnoreCase(@This Path path, String text) { return StringUtils.containsIgnoreCase(path.getFileName().toString(), text); } - public static boolean isEmptyDir(Path path) throws IOException { + public static boolean isEmptyDir(@This Path path) throws IOException { requireDir(path); return applySubfiles(path, children -> children.findAny().isEmpty()); } - public static T applySubfiles(Path path, ThrowingFunction, T, X> function) throws IOException, X { + public static T applySubfiles(@This Path path, ThrowingFunction, T, X> function) throws IOException, X { try (Stream pathStream = Files.list(path)) { return function.apply(pathStream); } } - public static void foreachSubfile(Path path, ThrowingConsumer, X> consumer) throws IOException, X { + public static void foreachSubfile(@This Path path, ThrowingConsumer, X> consumer) throws IOException, X { try (Stream pathStream = Files.list(path)) { consumer.accept(pathStream); } } - public static void requireDir(Path path) { + public static void requireDir(@This Path path) { if (!Files.isDirectory(path)) { throw new IllegalArgumentException("[%s] is not a directory".formatted(path)); } diff --git a/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java b/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java new file mode 100644 index 00000000..2893a56b --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java @@ -0,0 +1,47 @@ +package extensions.java.util.Optional; + +import java.util.Optional; +import java.util.OptionalInt; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingConsumer; +import name.falgout.jeffrey.throwing.ThrowingFunction; +import name.falgout.jeffrey.throwing.ThrowingToIntFunction; + +@UtilityClass +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@Extension +public class OptionalExt { + + /** + * If the value is present, apply the {@link ThrowingFunction} and return the value. Otherwise return {@code null} + * + * @param optional input object of the extension method + * @param function the {@link ThrowingFunction} to apply to the value if present + * @param The type of value of the input {@code Optional} + * @param type of the return value + * @param type of the exception that can be thrown + * @return the result of the {@link ThrowingFunction} if the value is present, otherwise {@code null} + * @throws X exception type of the throwing Function + */ + public static Optional mapThrowing(@This Optional optional, + ThrowingFunction function) throws X { + return optional.isPresent() ? Optional.ofNullable(function.apply(optional.get())) : Optional.empty(); + } + + public static OptionalInt mapToInt(@This Optional optional, + ThrowingToIntFunction mapper) throws X { + return optional.isPresent() ? OptionalInt.of(mapper.applyAsInt(optional.get())) : OptionalInt.empty(); + } + + public static @Self Optional useIfPresent(@This Optional optional, + ThrowingConsumer consumer) throws X { + if (optional.isPresent()) { + consumer.accept(optional.get()); + } + return optional; + } +} diff --git a/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java b/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java new file mode 100644 index 00000000..a44edfcf --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java @@ -0,0 +1,50 @@ +package extensions.java.util.OptionalInt; + + +import java.util.Optional; +import java.util.OptionalInt; + +import com.pivovarit.function.ThrowingIntFunction; +import com.pivovarit.function.ThrowingSupplier; +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingIntUnaryOperator; + + +@Extension +@UtilityClass +public class OptionalIntExt { + + /** + * If the value is present, apply the {@link ThrowingIntUnaryOperator} and return the value wrapped in an + * + * @param optional input object of the extension method + * @param function the function to apply to the value if present + * @param type of the exception that can be thrown + * @return the result of the function wrapped in an @{link OptionalInt} if the value is present, otherwise an empty + * {@code OptionalInt} + * @throws X exception type of the throwing Function + * @{link OptionalInt}. Otherwise, return an empty {@code OptionalInt} + */ + public static OptionalInt map(@This OptionalInt optional, + ThrowingIntUnaryOperator function) throws X { + return optional.isPresent() ? OptionalInt.of(function.applyAsInt(optional.getAsInt())) : OptionalInt + .empty(); + } + + public static OptionalInt orElseMap(@This OptionalInt optionalInt, + ThrowingSupplier intSupplier) throws X { + return optionalInt.isPresent() ? optionalInt : intSupplier.get(); + } + + public static Optional mapToObj(@This OptionalInt optionalInt, + ThrowingIntFunction mapper) throws X { + return optionalInt.isPresent() ? Optional.ofNullable(mapper.apply(optionalInt.getAsInt())) : Optional + .empty(); + } + + public static Integer orElseNull(@This OptionalInt optionalInt) { + return optionalInt.isPresent() ? optionalInt.getAsInt() : null; + } +} diff --git a/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java b/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java new file mode 100644 index 00000000..613e9fea --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java @@ -0,0 +1,50 @@ +package extensions.java.util.OptionalLong; + +import java.util.Optional; +import java.util.OptionalLong; + +import com.pivovarit.function.ThrowingFunction; +import com.pivovarit.function.ThrowingUnaryOperator; +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingLongUnaryOperator; + +@Extension +@UtilityClass +public class OptionalLongExt { + + /** + * If the value is present, apply the {@link ThrowingLongUnaryOperator} and return the value wrapped in an @{link + * OptionalLong}. Otherwise, return an empty {@code OptionalLong} + * + * @param optional input object of the extension method + * @param function the function to apply to the value if present + * @param type of the exception that can be thrown + * @return the result of the function wrapped in an @{link OptionalLong} if the value is present, otherwise an empty + * {@code OptionalLong} + * @throws X exception type of the throwing Function + */ + public static OptionalLong map(@This OptionalLong optional, + ThrowingLongUnaryOperator function) throws X { + return optional.isPresent() ? OptionalLong.of(function.applyAsLong(optional.getAsLong())) : + OptionalLong.empty(); + } + + /** + * If the value is present, apply the {@link ThrowingUnaryOperator} and return the value wrapped in an @{link + * Optional}. Otherwise, return an empty {@code Optional} + * + * @param optional input object of the extension method + * @param function the function to apply to the value if present + * @param type of the result value wrapped in the @{link Optional} + * @param type of the exception that can be thrown + * @return the result of the function wrapped in an @{link Optional} if the value is present, otherwise an empty + * {@code Optional} + * @throws X exception type of the throwing Function + */ + public static Optional mapToObj(@This OptionalLong optional, + ThrowingFunction function) throws X { + return optional.isPresent() ? Optional.ofNullable(function.apply(optional.getAsLong())) : Optional.empty(); + } +} diff --git a/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java b/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java new file mode 100644 index 00000000..34ca28c6 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java @@ -0,0 +1,17 @@ +package extensions.java.util.stream.Stream; + +import java.util.stream.Stream; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.stream.ThrowingStream; + +@UtilityClass +@Extension +public class StreamExt { + + public static ThrowingStream asThrowingStream(@This Stream stream, Class exceptionType) { + return ThrowingStream.of(stream, exceptionType); + } +} diff --git a/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java b/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java new file mode 100644 index 00000000..ac5d1a33 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java @@ -0,0 +1,35 @@ +package extensions.manifold.rt.api.Array; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.function.Consumer; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class ArrayExt { + + /** + * Performs an action for each element of the array. + * + * @param array the Array + * @param action an action to perform on the elements + */ + public static void forEach(@This Object array, Consumer action) { + primitiveCheck(array); + Arrays.stream((Object[]) array, 0, Array.getLength(array)).forEach(action); + } + + + private static void primitiveCheck(Object array) { + Class componentType = array.getClass().getComponentType(); + if (componentType.isPrimitive()) { + throw new IllegalArgumentException("$array has not a primitive component type: " + + array.getClass().getComponentType().getSimpleName()); + } + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java b/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java new file mode 100644 index 00000000..30af1c01 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java @@ -0,0 +1,24 @@ +package extensions.org.json.JSONArray; + +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.json.JSONArray; +import org.json.JSONObject; + +@Extension +@UtilityClass +public class JSONArrayExt { + + public static Stream stream(@This JSONArray jsonArray) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + (Iterator) (Iterator) jsonArray.iterator(), Spliterator.ORDERED), false); + // return IntStream.range(0, jsonArray.length()).mapToObj(jsonArray::getJSONObject); + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java new file mode 100644 index 00000000..0e60f408 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java @@ -0,0 +1,242 @@ +package extensions.org.jsoup.nodes.Element; + +import extensions.org.jsoup.nodes.Node.NodeExt; +import extensions.org.jsoup.select.Elements.UnmodifiableElements; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Jailbreak; +import manifold.ext.rt.api.This; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jsoup.helper.Validate; +import org.jsoup.nodes.Element; +import org.jsoup.select.Collector; +import org.jsoup.select.Elements; +import org.jsoup.select.Evaluator; +import org.jsoup.select.QueryParser; +import org.lodder.subtools.sublibrary.exception.WebpageException; + +@Extension +public class ElementExt extends NodeExt { + + // --------------- \\ + // Get all Element \\ + // --------------- \\ + + public static Elements selectAllByClass(@Nullable @This Element element, String className) { + return element == null ? UnmodifiableElements.EMPTY : element.getElementsByClass(requireNotEmpty(className)); + } + + public static Elements selectAllByTag(@Nullable @This Element element, String tagName) { + return element == null ? UnmodifiableElements.EMPTY : element.getElementsByTag(requireNotEmpty(tagName)); + } + + public static Elements selectAllByAttribute(@Nullable @This Element element, String attribute) { + return element == null ? UnmodifiableElements.EMPTY : + element.getElementsByAttribute(requireNotEmpty(attribute)); + } + + public static Elements selectAllByCss(@Nullable @This Element element, String cssQuery) { + return element == null ? UnmodifiableElements.EMPTY : element.getElements(requireNotEmpty(cssQuery)); + } + + public static Elements selectAll(@Nullable @This Element element, Evaluator evaluator) { + return element == null ? UnmodifiableElements.EMPTY : Collector.collect(evaluator, element); + } + + // ----------------- \\ + // Get First Element \\ + // ----------------- \\ + + public static @Nullable Element selectFirstByClass(@Nullable @This Element element, String className) { + return element == null ? null : element.selectFirst(new Evaluator.Class(requireNotEmpty(className))); + } + + public static @Nullable Element selectFirstByTag(@Nullable @This Element element, String tagName) { + return element == null ? null : element.selectFirst(new Evaluator.Tag(requireNotEmpty(tagName))); + } + + public static @Nullable Element selectFirstByAttribute(@Nullable @This Element element, String attribute) { + return element == null ? null : element.selectFirst(new Evaluator.Attribute(requireNotEmpty(attribute))); + } + + public static @Nullable Element selectFirstByCss(@Nullable @This Element element, String cssQuery) { + return element == null ? null : element.selectFirst(QueryParser.parse(requireNotEmpty(cssQuery))); + } + + public static @Nullable Element selectFirstById(@Nullable @This Element element, String id) { + return element == null ? null : element.selectFirst(new Evaluator.Id(requireNotEmpty(id))); + } + + public static @Nullable Element selectFirstBy(@Nullable @This Element element, Evaluator evaluator) { + return element == null ? null : Collector.findFirst(evaluator, element); + } + + // ---------------- \\ + // Get n-th Element \\ + // ---------------- \\ + + public static @Nullable Element selectNthByClass(@Nullable @This Element element, String className, int index) { + return element == null ? null : selectNth(element, new Evaluator.Class(requireNotEmpty(className)), index); + } + + public static @Nullable Element selectNthByTag(@Nullable @This Element element, String tagName, int index) { + return element == null ? null : selectNth(element, new Evaluator.Tag(requireNotEmpty(tagName)), index); + } + + public static @Nullable Element selectNthByAttribute(@Nullable @This Element element, String attribute, + int index) { + return element == null ? null : + selectNth(element, new Evaluator.Attribute(requireNotEmpty(attribute)), index); + } + + public static @Nullable Element selectNthByCss(@Nullable @This Element element, String cssQuery, int index) { + return element == null ? null : selectNth(element, QueryParser.parse(requireNotEmpty(cssQuery)), index); + } + + public static @Nullable Element selectNth(@Nullable @This Element element, @Jailbreak Evaluator eval, int idx) { + eval.reset(); + return new NthElementFinder(eval).find(element, element, idx); + } + + // -------------------------- \\ + // Get First Element or Throw \\ + // -------------------------- \\ + + public static Element selectFirstByClassOrThrow(@Nullable @This Element element, String className) + throws WebpageException { + Element n = selectFirstByClass(element, className); + if (n == null) { + throw new WebpageException("Could not find element with class '%s'".formatted(className)); + } + return n; + } + + public static Element selectFirstByTagOrThrow(@Nullable @This Element element, String tagName) + throws WebpageException { + Element n = selectFirstByTag(element, tagName); + if (n == null) { + throw new WebpageException("Could not find element with tag '%s'".formatted(tagName)); + } + return n; + } + + public static Element selectFirstByAttributeOrThrow(@Nullable @This Element element, String attribute) + throws WebpageException { + Element n = selectFirstByAttribute(element, attribute); + if (n == null) { + throw new WebpageException("Could not find element with attribute '%s'".formatted(attribute)); + } + return n; + } + + public static Element selectFirstByCssOrThrow(@Nullable @This Element element, String cssQuery) + throws WebpageException { + Element n = selectFirstByCss(element, cssQuery); + if (n == null) { + throw new WebpageException("Could not find element with css selector '%s'".formatted(cssQuery)); + } + return n; + } + + public static Element selectFirstByIdOrThrow(@Nullable @This Element element, String id) + throws WebpageException { + Element n = selectFirstById(element, id); + if (n == null) { + throw new WebpageException("Could not find element with id '%s'".formatted(id)); + } + return n; + } + + public static Element selectOrThrow(@Nullable @This Element element, Evaluator evaluator) + throws WebpageException { + Element n = selectFirstBy(element, evaluator); + if (n == null) { + throw new WebpageException("Could not find element using selector '%s'".formatted(evaluator)); + } + return n; + } + + // ------------------------- \\ + // Get n-th Element or Throw \\ + // ------------------------- \\ + + public static Element selectNthByClassOrThrow(@Nullable @This Element element, String className, int index) + throws WebpageException { + Element elem = selectNthByClass(element, className, index); + if (elem == null) { + throw new WebpageException("Could not find %sth element with class '%s'".formatted(index, className)); + } + return elem; + } + + public static Element selectNthByTagOrThrow(@Nullable @This Element element, String tagName, int index) + throws WebpageException { + Element elem = selectNthByTag(element, tagName, index); + if (elem == null) { + throw new WebpageException("Could not find %sth element with tag '%s'".formatted(index, tagName)); + } + return elem; + } + + public static Element selectNthByAttributeOrThrow(@Nullable @This Element element, String attribute, int index) + throws WebpageException { + Element elem = selectNthByAttribute(element, attribute, index); + if (elem == null) { + throw new WebpageException("Could not find %sth element with attribute '%s'".formatted(index, attribute)); + } + return elem; + } + + public static Element selectNthByCssOrThrow(@Nullable @This Element element, String cssQuery, int index) + throws WebpageException { + Element elem = selectNthByCss(element, cssQuery, index); + if (elem == null) { + throw new WebpageException("Could not find %sth element with css selector '%s'".formatted(index, cssQuery)); + } + return elem; + } + + public static Element selectNthOrThrow(@Nullable @This Element element, Evaluator eval, int index) + throws WebpageException { + Element elem = selectNth(element, eval, index); + if (elem == null) { + throw new WebpageException( + "Could not find %sth element using selector '%s'".formatted(index, eval.toString())); + } + return elem; + } + + // ------------ \\ + // Get Elements \\ + // ------------ \\ + + public static Elements getElements(@Nullable @This Element element, String cssQuery) { + return element == null ? new Elements() : element.select(cssQuery); + } + + // --------------- \\ + // Element methods \\ + // --------------- \\ + + + public static String getText(@Nullable @This Element element) { + return element == null ? "" : element.text(); + } + + public static @Nullable Element getParent(@Nullable @This Element element) { + return element == null ? null : element.parent(); + } + + // public static T map(@This @Nullable Element element, Function function) { + // return element == null ? null : function.apply(element); + // } + + + // --------------- \\ + // Utility methods \\ + // --------------- \\ + + private static String requireNotEmpty(String value) { + Validate.notEmpty(value); + return value; + } +} diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java new file mode 100644 index 00000000..e690f00a --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java @@ -0,0 +1,38 @@ +package extensions.org.jsoup.nodes.Element; + +import static org.jsoup.select.NodeFilter.FilterResult.*; + +import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.jsoup.select.Evaluator; +import org.jsoup.select.NodeFilter; +import org.jsoup.select.NodeTraversor; + +@RequiredArgsConstructor +class NthElementFinder implements NodeFilter { + private final Evaluator eval; + private Element evalRoot; + private Element match; + private int index; + private int currentIdx; + + @Nullable + Element find(Element root, Element start, int index) { + this.index = index; + this.evalRoot = root; + this.match = null; + NodeTraversor.filter(this, start); + return match; + } + + @Override + public FilterResult head(Node node, int depth) { + if (node instanceof Element el && eval.matches(evalRoot, el) && currentIdx++ == index) { + match = el; + return STOP; + } + return CONTINUE; + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Node/NodeExt.java b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Node/NodeExt.java new file mode 100644 index 00000000..2b107869 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Node/NodeExt.java @@ -0,0 +1,119 @@ +package extensions.org.jsoup.nodes.Node; + +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingFunction; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jsoup.nodes.Node; + +@Extension +public class NodeExt { + + // ------------ \\ + // Node methods \\ + // ------------ \\ + + public static @Nullable Node getParent(@Nullable @This Node node) { + return node == null ? null : node.parent(); + } + + public static String getAttr(@Nullable @This Node node, String attr) { + return node == null ? "" : node.attr(attr); + } + + public static T useAttrOrElse(@Nullable @This Node node, String attr, Function mapper, + Supplier supplier) { + return node != null && node.hasAttr(attr) ? mapper.apply(node.attr(attr)) : supplier.get(); + } + + public static @Nullable String getAttrNull(@Nullable @This Node node, String attr) { + return node == null ? null : node.attr(attr); + } + + public static Optional getAttrOptional(@Nullable @This Node node, String attr) { + return node == null ? Optional.empty() : Optional.of(node.attr(attr)); + } + + public static String getAttrOrThrow(@Nullable @This Node node, String attr, + Supplier exceptionSupplier) throws X { + if (node == null) { + throw exceptionSupplier.get(); + } + return node.attr(attr); + } + + // ------------- \\ + // Other methods \\ + // ------------- \\ + + public static @Nullable Node filter(@Nullable @This Node node, Predicate predicate) { + return node != null && predicate.test(node) ? node : null; + } + + public static boolean matches(@Nullable @This Node node, Predicate predicate) { + return node != null && predicate.test(node); + } + + public static <@Nullable T> @Nullable T map(@Nullable @This Node node, Function mapper) { + return node != null ? mapper.apply(node) : null; + } + + public static Optional mapOptional(@Nullable @This Node node, Function mapper) { + return node != null ? Optional.ofNullable(mapper.apply(node)) : Optional.empty(); + } + + public static T mapOrElse(@Nullable @This Node node, Function mapper, T obj) { + return node != null ? mapper.apply(node) : obj; + } + + public static T mapOrElseGet(@Nullable @This Node node, Function mapper, + Supplier elseSupplier) { + return node != null ? mapper.apply(node) : elseSupplier.get(); + } + + public static <@Nullable T, X extends Exception> @Nullable T mapThrowing(@Nullable @This Node node, + ThrowingFunction mapper) + throws X { + return node != null ? mapper.apply(node) : null; + } + + public static Node orElseGet(@Nullable @This Node node, Supplier elementSupplier) { + return node != null ? node : elementSupplier.get(); + } + + public static Node orElseThrow(@Nullable @This Node node, + Supplier exceptionSupplier) throws X { + if (node == null) { + throw exceptionSupplier.get(); + } + return node; + } + + public static void ifPresent(@Nullable @This Node node, Consumer consumer) { + if (node != null) { + consumer.accept(node); + } + } + + public static boolean isFound(@Nullable @This Node node) { + return node != null; + } + + public static boolean isNotNull(@Nullable @This Node node) { + return node != null; + } + + public static boolean isNotFound(@Nullable @This Node node) { + return node == null; + } + + public static boolean isNull(@Nullable @This Node node) { + return node == null; + } +} diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/ElementsExt.java b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/ElementsExt.java new file mode 100644 index 00000000..fda5d617 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/ElementsExt.java @@ -0,0 +1,35 @@ +package extensions.org.jsoup.select.Elements; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.jspecify.annotations.Nullable; + +@Extension +@UtilityClass +public class ElementsExt { + + + public static String getText(@This @Nullable Elements elements) { + return elements == null ? "" : elements.text(); + } + + public static String getAttr(@This @Nullable Elements elements, String attribute) { + return elements == null ? "" : + elements.stream() + .filter(elem -> elem.hasAttr(attribute)) + .map(elem -> elem.attr(attribute)) + .findFirst() + .orElse(""); + } + + public static @Nullable Element getFirstElement(@This @Nullable Elements elements) { + return elements == null ? null : elements.first(); + } + + public static int getSize(@This Elements elements) { + return elements == null ? 0 : elements.size(); + } +} diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java new file mode 100644 index 00000000..7b86b424 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java @@ -0,0 +1,123 @@ +package extensions.org.jsoup.select.Elements; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +public class UnmodifiableElements extends Elements { + + public static UnmodifiableElements EMPTY = new UnmodifiableElements(); + + public UnmodifiableElements() { + } + + public UnmodifiableElements(int initialCapacity) { + super(initialCapacity); + } + + public UnmodifiableElements(Collection elements) { + super(elements); + } + + public UnmodifiableElements(List elements) { + super(elements); + } + + public UnmodifiableElements(Element... elements) { + super(Arrays.asList(elements)); + } + + public Elements removeAttr(String attributeKey) { + return this; + } + + public Elements addClass(String className) { + return this; + } + + public Elements removeClass(String className) { + return this; + } + + public Elements toggleClass(String className) { + return this; + } + + + public Elements prepend(String html) { + return this; + } + + public Elements append(String html) { + return this; + } + + public Elements before(String html) { + return this; + } + + public Elements after(String html) { + return this; + } + + public Elements wrap(String html) { + return this; + } + + public Elements unwrap() { + return this; + } + + public Elements empty() { + return this; + } + + public Elements remove() { + return this; + } + + // filters + + @Override + public Element set(int index, Element element) { + throw new IllegalStateException(); + } + + @Override + public Element remove(int index) { + throw new IllegalStateException(); + } + + @Override + public boolean remove(Object o) { + return false; + } + + @Override + public void clear() { + } + + @Override + public boolean removeAll(Collection c) { + return false; + } + + @Override + public boolean retainAll(Collection c) { + return false; + } + + @Override + public boolean removeIf(Predicate filter) { + return false; + } + + @Override + public void replaceAll(UnaryOperator operator) { + } +} diff --git a/SubLibrary/src/main/java/extensions/org/w3c/dom/Document/DocumentExt.java b/SubLibrary/src/main/java/extensions/org/w3c/dom/Document/DocumentExt.java new file mode 100644 index 00000000..0f1b0f69 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/w3c/dom/Document/DocumentExt.java @@ -0,0 +1,29 @@ +package extensions.org.w3c.dom.Document; + +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jsoup.helper.Validate; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +@Extension +public class DocumentExt { + + // ----------------- \\ + // Get First Element \\ + // ----------------- \\ + + public static @Nullable NodeList selectAllByTag(@Nullable @This Document document, String tagName) { + return document == null ? null : document.getElementsByTagName(requireNotEmpty(tagName)); + } + + // --------------- \\ + // Utility methods \\ + // --------------- \\ + + private static String requireNotEmpty(String value) { + Validate.notEmpty(value); + return value; + } +} diff --git a/SubLibrary/src/main/java/extensions/org/w3c/dom/Node/NodeExt.java b/SubLibrary/src/main/java/extensions/org/w3c/dom/Node/NodeExt.java new file mode 100644 index 00000000..811f6a1b --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/w3c/dom/Node/NodeExt.java @@ -0,0 +1,28 @@ +package extensions.org.w3c.dom.Node; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.jspecify.annotations.Nullable; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +@UtilityClass +@Extension +public class NodeExt { + + public static @Nullable String getAttribute(@This @Nullable Node node, String attribute) { + if (node == null) { + return null; + } + NamedNodeMap attributes = node.getAttributes(); + if (attributes == null) { + return null; + } + Node attributeNode = attributes.getNamedItem(attribute); + if (attributeNode == null) { + return null; + } + return attributeNode.getNodeValue(); + } +} diff --git a/SubLibrary/src/main/java/extensions/org/w3c/dom/NodeList/NodeListExt.java b/SubLibrary/src/main/java/extensions/org/w3c/dom/NodeList/NodeListExt.java new file mode 100644 index 00000000..1eb95c01 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/w3c/dom/NodeList/NodeListExt.java @@ -0,0 +1,20 @@ +package extensions.org.w3c.dom.NodeList; + +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.jspecify.annotations.Nullable; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +@UtilityClass +@Extension +public class NodeListExt { + + public static Stream stream(@This @Nullable NodeList nodeList) { + return nodeList == null ? null : IntStream.range(0, nodeList.getLength()).mapToObj(nodeList::item); + } +} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java b/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java index 8ae5416d..c94c82fb 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java @@ -5,40 +5,38 @@ import java.util.MissingResourceException; import java.util.ResourceBundle; +import manifold.ext.props.rt.api.var; import org.lodder.subtools.sublibrary.Language; public class Messages { private static final String BUNDLE_NAME = "messages"; private static final Language DEFAULT_LANGUAGE = Language.ENGLISH; - private static Language LANGUAGE; - private static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, Locale.forLanguageTag(DEFAULT_LANGUAGE.getLangCode())); + private static ResourceBundle resourceBundle = + ResourceBundle.getBundle(BUNDLE_NAME, Locale.forLanguageTag(DEFAULT_LANGUAGE.langCode)); + static @var Language language; private Messages() { } - public static String getString(String key) { + public static String getText(String key) { try { - return RESOURCE_BUNDLE.getString(key); + return resourceBundle.getString(key); } catch (MissingResourceException e) { - return '!' + key + '!'; + return "!$key!"; } } - public static String getString(String key, Object... replacements) { + public static String getText(String key, Object... replacements) { try { - return RESOURCE_BUNDLE.getString(key).formatted(replacements); + return resourceBundle.getString(key).formatted(replacements); } catch (MissingResourceException e) { - return '!' + key + '!'; + return "!$key!"; } } public static void setLanguage(Language language) { - LANGUAGE = language; - RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, Locale.forLanguageTag(language.getLangCode())); - } - - public static Language getLanguage() { - return LANGUAGE; + Messages.language = language; + resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME, Locale.forLanguageTag(language.langCode)); } public static List getAvailableLanguages() { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java index bb3c331a..aa65dbb1 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java @@ -3,9 +3,12 @@ import java.io.IOException; import java.io.InputStream; +import lombok.Getter; + public final class ConfigProperties { - private static ConfigProperties configProps = null; + @Getter(lazy = true) + private static final ConfigProperties instance = new ConfigProperties(); private final java.util.Properties prop = new java.util.Properties(); private ConfigProperties() { @@ -16,13 +19,6 @@ private ConfigProperties() { } } - public synchronized static ConfigProperties getInstance() { - if (configProps == null) { - configProps = new ConfigProperties(); - } - return configProps; - } - public String getProperty(String key) { return prop.getProperty(key); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java index 7c781ccc..5cea36b7 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java @@ -7,17 +7,16 @@ import java.nio.file.Path; import java.util.Optional; -import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; -import org.lodder.subtools.sublibrary.util.lazy.LazyThrowingSupplier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.optimaize.langdetect.LanguageDetector; import com.optimaize.langdetect.LanguageDetectorBuilder; import com.optimaize.langdetect.ngram.NgramExtractors; import com.optimaize.langdetect.profiles.LanguageProfileReader; import com.optimaize.langdetect.text.CommonTextObjectFactories; import com.optimaize.langdetect.text.TextObjectFactory; +import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; +import org.lodder.subtools.sublibrary.util.lazy.LazyThrowingSupplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DetectLanguage { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java index 76d9c690..0c0af954 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java @@ -1,15 +1,13 @@ package org.lodder.subtools.sublibrary; -import java.util.Arrays; import java.util.Optional; import java.util.Set; import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public enum Language { ALBANIAN("App.Language.Albanian", "sq", Set.of("alb", "sq", "Albanian")), @@ -74,20 +72,23 @@ public enum Language { VIETNAMESE("App.Language.Vietnamese", "vi", Set.of("vie", "Vietnamese")), WELSH("App.Language.Welsh", "cy", Set.of("wel", "cym", "Welsh")); - private final String msgCode; - private final String langCode; - private final Set langCodesOther; + @val String msgCode; + @val String langCode; + @val Set langCodesOther; public static Language fromValue(String value) { - return Arrays.stream(Language.values()).filter(lang -> lang.name().equalsIgnoreCase(value)).findAny().orElseThrow(); + return Language.values().stream() + .filter(lang -> lang.name().equalsIgnoreCase(value)) + .findAny() + .orElseThrow(); } public static Optional fromValueOptional(String value) { - return Arrays.stream(Language.values()).filter(lang -> lang.name().equalsIgnoreCase(value)).findAny(); + return Language.values().stream().filter(lang -> lang.name().equalsIgnoreCase(value)).findAny(); } public static Optional fromIdOptional(String languageId) { - return Arrays.stream(Language.values()).filter(lang -> lang.getLangCode().equalsIgnoreCase(languageId)).findAny(); + return Language.values().stream().filter(lang -> lang.langCode.equalsIgnoreCase(languageId)).findAny(); } public static Language fromId(String languageId) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java index 65681530..c57d6cc5 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -1,5 +1,7 @@ package org.lodder.subtools.sublibrary; +import static java.util.concurrent.TimeUnit.*; + import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; @@ -16,16 +18,16 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; +import java.util.function.LongUnaryOperator; import java.util.function.Predicate; import com.pivovarit.function.ThrowingSupplier; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; -import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.val; import name.falgout.jeffrey.throwing.Nothing; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -37,21 +39,17 @@ import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.cache.DiskCache; import org.lodder.subtools.sublibrary.cache.InMemoryCache; -import org.lodder.subtools.sublibrary.util.IOUtils; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.http.HttpClient; import org.lodder.subtools.sublibrary.util.http.HttpClientException; import org.lodder.subtools.sublibrary.xml.XMLHelper; import org.w3c.dom.Document; -@Setter -@RequiredArgsConstructor -@ExtensionMethod({ OptionalExtension.class }) +@AllArgsConstructor public class Manager { - private final HttpClient httpClient; - private final InMemoryCache inMemoryCache; - private final DiskCache diskCache; + @val HttpClient httpClient; + @val InMemoryCache inMemoryCache; + @val DiskCache diskCache; public boolean store(String downloadLink, Path file) throws ManagerException { try { @@ -99,7 +97,8 @@ public interface PostBuilderPostIntf { @Accessors(chain = true, fluent = true) @RequiredArgsConstructor public static class PostBuilder - implements PostBuilderUrlIntf, PostBuilderUserAgentIntf, PostBuilderDataMapIntf, PostBuilderDataIntf, PostBuilderPostIntf { + implements PostBuilderUrlIntf, PostBuilderUserAgentIntf, PostBuilderDataMapIntf, PostBuilderDataIntf, + PostBuilderPostIntf { private final HttpClient httpClient; private String url; private String userAgent; @@ -169,13 +168,14 @@ public interface PageContentBuilderGetIntf { InputStream getAsInputStream() throws ManagerException; - Optional getAsDocument() throws ParserConfigurationException, ManagerException; + Document getAsDocument() throws ParserConfigurationException, ManagerException, IOException; - Optional getAsDocument(Predicate emptyResultPredicate) throws ParserConfigurationException, ManagerException; + Document getAsDocument(Predicate emptyResultPredicate) + throws ParserConfigurationException, ManagerException, IOException; org.jsoup.nodes.Document getAsJsoupDocument() throws ManagerException; - Optional getAsJsoupDocument(Predicate emptyResultPredicate) throws ManagerException; + org.jsoup.nodes.Document getAsJsoupDocument(Predicate emptyResultPredicate) throws ManagerException; JSONObject getAsJsonObject() throws ManagerException; @@ -185,8 +185,9 @@ public interface PageContentBuilderGetIntf { @Setter @Accessors(chain = true, fluent = true) @RequiredArgsConstructor - public static class PageContentBuilder implements PageContentBuilderGetIntf, PageContentBuilderCacheTypeIntf, - PageContentBuilderUserAgentIntf, PageContentBuilderUrlIntf, PageContentBuilderRetryIntf, PageContentBuilderRetryConditionIntf, + public static class PageContentBuilder + implements PageContentBuilderGetIntf, PageContentBuilderCacheTypeIntf, PageContentBuilderUserAgentIntf, + PageContentBuilderUrlIntf, PageContentBuilderRetryIntf, PageContentBuilderRetryConditionIntf, PageContentBuilderRetryWaitIntf { private final HttpClient httpClient; private final InMemoryCache inMemoryCache; @@ -220,19 +221,20 @@ public String get() throws ManagerException { @Override public InputStream getAsInputStream() throws ManagerException { - return IOUtils.toInputStream(get(), StandardCharsets.UTF_8); + return get().toInputStream(StandardCharsets.UTF_8); } @Override - public Optional getAsDocument() throws ParserConfigurationException, ManagerException { + public Document getAsDocument() throws ParserConfigurationException, ManagerException, IOException { return XMLHelper.getDocument(get()); } @Override - public Optional getAsDocument(Predicate emptyResultPredicate) throws ParserConfigurationException, ManagerException { + public Document getAsDocument(Predicate emptyResultPredicate) + throws ParserConfigurationException, ManagerException, IOException { String html = get(); - return StringUtils.isBlank(html) || (emptyResultPredicate != null && emptyResultPredicate.test(html)) ? Optional.empty() - : XMLHelper.getDocument(html); + return StringUtils.isBlank(html) || (emptyResultPredicate != null && emptyResultPredicate.test(html)) ? + null : XMLHelper.getDocument(html); } @Override @@ -241,10 +243,11 @@ public org.jsoup.nodes.Document getAsJsoupDocument() throws ManagerException { } @Override - public Optional getAsJsoupDocument(Predicate emptyResultPredicate) throws ManagerException { + public org.jsoup.nodes.Document getAsJsoupDocument(Predicate emptyResultPredicate) + throws ManagerException { String html = get(); - return StringUtils.isBlank(html) || (emptyResultPredicate != null && emptyResultPredicate.test(html)) ? Optional.empty() - : Optional.of(Jsoup.parse(html)); + return StringUtils.isBlank(html) || (emptyResultPredicate != null && emptyResultPredicate.test(html)) ? + null : Jsoup.parse(html); } @Override @@ -277,8 +280,9 @@ private String getContentWithoutCache(String urlString, String userAgent) throws } return getContentWithoutCache(urlString, userAgent); } - throw new ManagerException("Error occurred with httpclient response: %s %s".formatted(e.getResponseCode(), e.getResponseMessage()), - e); + throw new ManagerException( + "Error occurred with httpclient response: %s %s".formatted(e.getResponseCode(), + e.getResponseMessage()), e); } catch (IOException e) { if (retries-- > 0 && retryPredicate.test(e)) { return getContentWithoutCache(urlString, userAgent); @@ -318,7 +322,8 @@ public interface ClearExpiredCacheBuilderClearIntf { @RequiredArgsConstructor @SuppressWarnings({ "rawtypes", "unchecked" }) public static class ClearExpiredCacheBuilder - implements ClearExpiredCacheBuilderCacheTypeIntf, ClearExpiredCacheBuilderKeyFilterIntf, ClearExpiredCacheBuilderClearIntf { + implements ClearExpiredCacheBuilderCacheTypeIntf, ClearExpiredCacheBuilderKeyFilterIntf, + ClearExpiredCacheBuilderClearIntf { private final InMemoryCache inMemoryCache; private final DiskCache diskCache; private CacheType cacheType; @@ -376,8 +381,8 @@ public interface ValueBuilderIsPresentIntf extends ValuesBuilderCacheTypeIntf public interface ValuesBuilderCacheTypeIntf extends ValueBuilderRetryIntf { ValueBuilderGetOptionalIntf returnType(Class returnType); - , S extends T> ValueBuilderGetCollectionIntf returnType(Class collectionReturnType, - Class returnType); + , S extends T> ValueBuilderGetCollectionIntf returnType( + Class collectionReturnType, Class returnType); void remove(); } @@ -391,7 +396,8 @@ public interface ValueBuilderRetryIntf extends ValueBuilderValueSupplierIntf< ValueBuilderGetOptionalIntStoreTempValueIntf optionalIntValue(OptionalInt optionalIntValue); - , S extends T> ValueBuilderGetCollectionIntf collectionValue(C collectionValue); + , S extends T> ValueBuilderGetCollectionIntf collectionValue( + C collectionValue); } public interface ValueBuilderRetryConditionIntf { @@ -404,19 +410,21 @@ public interface ValueBuilderRetryWaitIntf { public interface ValueBuilderValueSupplierIntf { - ValueBuilderGetValueStoreTempValueIntf valueSupplier(ThrowingSupplier valueSupplier); + ValueBuilderGetValueStoreTempValueIntf valueSupplier( + ThrowingSupplier valueSupplier); - , S extends T, X extends Exception> ValueBuilderGetCollectionIntf - collectionSupplier(Class collectionValueType, ThrowingSupplier valueSupplier); + , S extends T, X extends Exception> ValueBuilderGetCollectionIntf collectionSupplier( + Class collectionValueType, ThrowingSupplier valueSupplier); - ValueBuilderGetOptionalStoreTempValueIntf - optionalSupplier(ThrowingSupplier, X> valueSupplier); + ValueBuilderGetOptionalStoreTempValueIntf optionalSupplier( + ThrowingSupplier, X> valueSupplier); - ValueBuilderGetOptionalIntStoreTempValueIntf - optionalIntSupplier(ThrowingSupplier optionalIntSupplier); + ValueBuilderGetOptionalIntStoreTempValueIntf optionalIntSupplier( + ThrowingSupplier optionalIntSupplier); } - public interface ValueBuilderGetValueStoreTempValueIntf extends ValueBuilderGetValueIntf { + public interface ValueBuilderGetValueStoreTempValueIntf + extends ValueBuilderGetValueIntf { ValueBuilderGetValueStoreTempValueTtlIntf storeTempNullValue(); } @@ -424,7 +432,7 @@ public interface ValueBuilderGetValueStoreTempValueTtlIntf { ValueBuilderGetValueIntf timeToLive(long seconds); - ValueBuilderGetValueIntf timeToLiveFunction(Function timeToLiveFunction); + ValueBuilderGetValueIntf timeToLiveFunction(LongUnaryOperator timeToLiveFunction); } public interface ValueBuilderGetValueIntf extends ValueBuilderStoreIntf { @@ -440,7 +448,7 @@ public interface ValueBuilderGetOptionalStoreTempValueTtlIntf { ValueBuilderGetOptionalIntf timeToLive(long seconds); - ValueBuilderGetOptionalIntf timeToLiveFunction(Function timeToLiveFunction); + ValueBuilderGetOptionalIntf timeToLiveFunction(LongUnaryOperator timeToLiveFunction); } public interface ValueBuilderGetOptionalIntf extends ValueBuilderStoreIntf { @@ -449,14 +457,16 @@ public interface ValueBuilderGetOptionalIntf extends Val Optional getOptional() throws X; } - public interface ValueBuilderGetOptionalIntStoreTempValueIntf extends ValueBuilderGetOptionalIntIntf { + public interface ValueBuilderGetOptionalIntStoreTempValueIntf + extends ValueBuilderGetOptionalIntIntf { ValueBuilderGetOptionalIntStoreTempValueTtlIntf storeTempNullValue(); } - public interface ValueBuilderGetOptionalIntStoreTempValueTtlIntf extends ValueBuilderGetOptionalIntIntf { + public interface ValueBuilderGetOptionalIntStoreTempValueTtlIntf + extends ValueBuilderGetOptionalIntIntf { ValueBuilderGetOptionalIntIntf timeToLive(long seconds); - ValueBuilderGetOptionalIntIntf timeToLiveFunction(Function timeToLiveFunction); + ValueBuilderGetOptionalIntIntf timeToLiveFunction(LongUnaryOperator timeToLiveFunction); } public interface ValueBuilderGetOptionalIntIntf extends ValueBuilderStoreIntf { @@ -472,7 +482,7 @@ public interface ValueBuilderGetOptionalIntIntf extends Val // extends ValueBuilderGetCollectionIntf { // ValueBuilderGetCollectionIntf timeToLive(long seconds); // - // ValueBuilderGetCollectionIntf timeToLiveFunction(Function timeToLiveFunction); + // ValueBuilderGetCollectionIntf timeToLiveFunction(LongUnaryOperator timeToLiveFunction); // } public interface ValueBuilderGetCollectionIntf, T, X extends Exception> @@ -491,13 +501,14 @@ public interface ValueBuilderStoreIntf { @RequiredArgsConstructor @SuppressWarnings({ "rawtypes", "unchecked" }) public static class ValueBuilder, T, X extends Exception> - implements ValueBuilderGetOptionalIntf, ValueBuilderCacheTypeIntf, - ValueBuilderValueSupplierIntf, ValueBuilderKeyIntf, ValueBuilderGetCollectionIntf, ValueBuilderGetOptionalIntIntf, + implements ValueBuilderGetOptionalIntf, ValueBuilderCacheTypeIntf, ValueBuilderValueSupplierIntf, + ValueBuilderKeyIntf, ValueBuilderGetCollectionIntf, ValueBuilderGetOptionalIntIntf, ValueBuilderRetryIntf, ValueBuilderRetryConditionIntf, ValueBuilderRetryWaitIntf, - ValueBuilderIsPresentIntf, ValuesBuilderCacheTypeIntf, - ValueBuilderStoreIntf, ValueBuilderGetOptionalIntStoreTempValueIntf, ValueBuilderGetOptionalStoreTempValueIntf, + ValueBuilderIsPresentIntf, ValuesBuilderCacheTypeIntf, ValueBuilderStoreIntf, + ValueBuilderGetOptionalIntStoreTempValueIntf, ValueBuilderGetOptionalStoreTempValueIntf, ValueBuilderGetOptionalIntStoreTempValueTtlIntf, ValueBuilderGetOptionalStoreTempValueTtlIntf, - ValueBuilderGetValueStoreTempValueIntf, ValueBuilderGetValueStoreTempValueTtlIntf, ValueBuilderGetValueIntf { + ValueBuilderGetValueStoreTempValueIntf, ValueBuilderGetValueStoreTempValueTtlIntf, + ValueBuilderGetValueIntf { private final InMemoryCache inMemoryCache; private final DiskCache diskCache; private String key; @@ -516,15 +527,13 @@ public static class ValueBuilder, T, X extends Exception private Predicate retryPredicate; private int retryWait; private Predicate keyFilter; - @Setter(value = AccessLevel.NONE) - private Long timeToLive; - @Setter(value = AccessLevel.NONE) - private boolean storeTempNullValue; - private Function timeToLiveFunction; + @Setter(value = AccessLevel.NONE) private Long timeToLive; + @Setter(value = AccessLevel.NONE) private boolean storeTempNullValue; + private LongUnaryOperator timeToLiveFunction; // // @Override - // public ValueBuilder timeToLiveFunction(Function timeToLiveFunction) { + // public ValueBuilder timeToLiveFunction(LongUnaryOperator timeToLiveFunction) { // this.timeToLiveFunction = timeToLiveFunction; // return this; // } @@ -562,34 +571,36 @@ public ValueBuilder returnType(Class returnType) } @Override - public , S extends T> ValueBuilderGetCollectionIntf - returnType(Class collectionReturnType, Class returnType) { + public , S extends T> ValueBuilderGetCollectionIntf returnType( + Class collectionReturnType, Class returnType) { this.returnType = (Class) returnType; return (ValueBuilder) this; } @Override - public ValueBuilder valueSupplier(ThrowingSupplier valueSupplier) { + public ValueBuilder valueSupplier( + ThrowingSupplier valueSupplier) { this.valueSupplier = (ThrowingSupplier) valueSupplier; return (ValueBuilder) this; } @Override - public ValueBuilder - optionalSupplier(ThrowingSupplier, E> valueSupplier) { + public ValueBuilder optionalSupplier( + ThrowingSupplier, E> valueSupplier) { this.optionalSupplier = (ThrowingSupplier) valueSupplier; return (ValueBuilder) this; } @Override - public ValueBuilder optionalIntSupplier(ThrowingSupplier optionalIntSupplier) { + public ValueBuilder optionalIntSupplier( + ThrowingSupplier optionalIntSupplier) { this.optionalIntSupplier = (ThrowingSupplier) optionalIntSupplier; return (ValueBuilder) this; } @Override - public , S extends T, E extends Exception> ValueBuilder - collectionSupplier(Class collectionValueType, ThrowingSupplier collectionSupplier) { + public , S extends T, E extends Exception> ValueBuilder collectionSupplier( + Class collectionValueType, ThrowingSupplier collectionSupplier) { this.collectionSupplier = (ThrowingSupplier) collectionSupplier; return (ValueBuilder) this; } @@ -649,10 +660,9 @@ private T getOrPutValue(Cache cache) throws X { if (value != null) { cache.put(key, value); } else { - if (cache instanceof DiskCache dCache) { - dCache.putWithoutPersist(key, null); - } else { - cache.put(key, null); + switch (cache) { + case DiskCache dCache -> dCache.putWithoutPersist(key, null); + case InMemoryCache mCache -> mCache.put(key, null); } } return value; @@ -714,10 +724,9 @@ private Optional getOrPutOptional(Cache cache) throws X { } else { Optional value = executeSupplier(optionalSupplier); value.ifPresentOrElse(v -> cache.put(key, v), () -> { - if (cache instanceof DiskCache dCache) { - dCache.putWithoutPersist(key, null); - } else { - cache.put(key, null); + switch (cache) { + case DiskCache dCache -> dCache.putWithoutPersist(key, null); + case InMemoryCache mCache -> mCache.put(key, null); } }); return value; @@ -730,27 +739,35 @@ private Optional getOrPutOptional(Cache cache) throws X { @Override public OptionalInt getOptionalInt() throws X { - return switch (cacheType) { - case NONE -> optionalIntSupplier.get(); - case MEMORY -> getOrPutOptionalInt(inMemoryCache); - case DISK -> getOrPutOptionalInt(diskCache); - }; + try { + return switch (cacheType) { + case NONE -> optionalIntSupplier.get(); + case MEMORY -> getOrPutOptionalInt(inMemoryCache); + case DISK -> getOrPutOptionalInt(diskCache); + }; + } catch (Exception e) { + // TODO why is this needed? + try { + throw (X) e; + } catch (ClassCastException e2) { + throw new RuntimeException(e); + } + } } - private OptionalInt getOrPutOptionalInt(Cache cache) throws X { + private OptionalInt getOrPutOptionalInt(Cache cache) throws X { boolean containsKey = cache.contains(key); if (!containsKey && storeTempNullValue) { timeToLive(calculateTtl()).store(); - return cache.get(key).mapToOptionalInt(); + return cache.get(key).mapToInt(t -> t); } else if (containsKey && !isExpiredTemporary()) { - return cache.get(key).mapToOptionalInt(); + return cache.get(key).mapToInt(t -> t); } else { OptionalInt value = executeSupplier(optionalIntSupplier); value.ifPresentOrElse(v -> cache.put(key, v), () -> { - if (cache instanceof DiskCache dCache) { - dCache.putWithoutPersist(key, null); - } else { - cache.put(key, null); + switch (cache) { + case DiskCache dCache -> dCache.putWithoutPersist(key, null); + case InMemoryCache mCache -> mCache.put(key, null); } }); return value; @@ -815,7 +832,7 @@ public OptionalLong getTemporaryTimeToLive() { } private OptionalLong getTemporaryTimeToLive(Cache cache) { - return cache.getTemporaryTimeToLive(key).map(v -> TimeUnit.SECONDS.convert(v, TimeUnit.MILLISECONDS)); + return cache.getTemporaryTimeToLive(key).map(v -> SECONDS.convert(v, MILLISECONDS)); } // ##### \\ @@ -839,20 +856,20 @@ private void store(boolean storeAsTempValue) throws X { } else if (optionalSupplier != null) { value = executeSupplier(optionalSupplier).orElse(null); } else if (optionalIntSupplier != null) { - value = OptionalExtension.mapToObj(executeSupplier(optionalIntSupplier), i -> i).orElse(null); + value = executeSupplier(optionalIntSupplier).orElseNull(); } else if (collectionSupplier != null) { value = executeSupplier(collectionSupplier); } else if (optionalValue != null) { value = optionalValue.orElse(null); } else if (optionalIntValue != null) { - value = optionalIntValue.mapToObj(i -> i).orElse(null); + value = optionalIntValue.orElseNull(); } else if (collectionValue != null) { value = collectionValue; } else { value = this.value; } if (storeAsTempValue || (storeTempNullValue && value == null)) { - long ttl = timeToLive != null ? timeToLive : TimeUnit.SECONDS.convert(1, TimeUnit.DAYS); + long ttl = timeToLive != null ? timeToLive : SECONDS.convert(1, DAYS); switch (cacheType) { case MEMORY -> inMemoryCache.put(key, value, ttl); case DISK -> diskCache.put(key, value, ttl); @@ -904,13 +921,18 @@ private V executeSupplier(ThrowingSupplier supplier) throws X { } return executeSupplier(supplier); } - throw new RuntimeException("Exception while getting value (%s)".formatted(e.getMessage()), e); + try { + throw (X) e; + } catch (ClassCastException e2) { + throw new RuntimeException("Exception while getting value (%s)".formatted(e.getMessage()), e); + } } } private long calculateTtl() { - return getTemporaryTimeToLive().mapToObj(v -> timeToLiveFunction != null ? timeToLiveFunction.apply(v) : v * 2) - .orElseGet(() -> timeToLive != null ? timeToLive : TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)); + return getTemporaryTimeToLive().mapToObj( + v -> timeToLiveFunction != null ? timeToLiveFunction.applyAsLong(v) : v * 2) + .orElseGet(() -> timeToLive != null ? timeToLive : MILLISECONDS.convert(1, DAYS)); } } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/OsCheck.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/OsCheck.java index 9dcbaa81..067f62fa 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/OsCheck.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/OsCheck.java @@ -2,6 +2,8 @@ import java.util.Locale; +import manifold.ext.props.rt.api.val; + /** * helper class to check the operating system this Java VM runs in *

@@ -16,30 +18,22 @@ public final class OsCheck { * types of Operating Systems */ public enum OSType { - Windows, MacOS, Linux, Other + WINDOWS, MAC, LINUX, OTHER } - // cached result of OS detection - private static OSType detectedOS; + @val static OSType operatingSystemType = calculatedOsType(); - /** - * detect the operating system from the os.name System property and cache the result - * - * @return the operating system detected - */ - public static OSType getOperatingSystemType() { - if (detectedOS == null) { - String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); - if (OS.contains("mac") || OS.contains("darwin")) { - detectedOS = OSType.MacOS; - } else if (OS.contains("win")) { - detectedOS = OSType.Windows; - } else if (OS.contains("nux")) { - detectedOS = OSType.Linux; - } else { - detectedOS = OSType.Other; - } + private static OSType calculatedOsType() { + // detect the operating system from the os.name System property + String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); + if (os.contains("mac") || os.contains("darwin")) { + return OSType.MAC; + } else if (os.contains("win")) { + return OSType.WINDOWS; + } else if (os.contains("nux")) { + return OSType.LINUX; + } else { + return OSType.OTHER; } - return detectedOS; } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/Cache.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/Cache.java index b1b15cd1..aeb66b64 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/Cache.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/Cache.java @@ -1,5 +1,7 @@ package org.lodder.subtools.sublibrary.cache; +import static manifold.ext.props.rt.api.PropOption.*; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,21 +11,15 @@ import java.util.function.Predicate; import java.util.stream.Stream; -import org.apache.commons.lang3.tuple.Pair; - import com.pivovarit.function.ThrowingSupplier; +import manifold.ext.props.rt.api.val; +import org.apache.commons.lang3.tuple.Pair; -import lombok.AccessLevel; -import lombok.Getter; - -@Getter(value = AccessLevel.PROTECTED) -public abstract class Cache { +public abstract sealed class Cache permits DiskCache, InMemoryCache { - private final Map> cacheMap; - private final Integer maxItems; + @val(Protected) Map> cacheMap; protected Cache(Integer maxItems) { - this.maxItems = maxItems; this.cacheMap = maxItems != null ? new LRUMap<>(maxItems) : new HashMap<>(); } @@ -54,42 +50,31 @@ public Optional get(K key) { return Optional.empty(); } else { obj.updateLastAccessed(); - return Optional.ofNullable(obj.getValue()); + return Optional.ofNullable(obj.value); } } } public boolean isTemporaryObject(K key) { synchronized (cacheMap) { - CacheObject obj = cacheMap.get(key); - if (obj == null) { - return false; - } else { - return obj instanceof TemporaryCacheObject; - } + return cacheMap.get(key) instanceof TemporaryCacheObject; } } public boolean isTemporaryExpired(K key) { synchronized (cacheMap) { CacheObject obj = cacheMap.get(key); - if (obj == null) { - return false; - } else { - return obj instanceof TemporaryCacheObject tempCacheObject && tempCacheObject.isExpired(); - } + return obj instanceof TemporaryCacheObject tempCacheObject && tempCacheObject.isExpired(); } } public OptionalLong getTemporaryTimeToLive(K key) { synchronized (cacheMap) { - CacheObject obj = cacheMap.get(key); - if (obj == null) { - return OptionalLong.empty(); - } else { - return obj instanceof TemporaryCacheObject tempCacheObject ? OptionalLong.of(tempCacheObject.getTimeToLive()) - : OptionalLong.empty(); - } + return switch (cacheMap.get(key)) { + case TemporaryCacheObject tempCacheObject -> OptionalLong.of(tempCacheObject.timeToLive); + case ExpiringCacheObject _ -> OptionalLong.empty(); + case null -> OptionalLong.empty(); + }; } } @@ -113,7 +98,7 @@ public V getOrPut(K key, ThrowingSupplier supplier) return null; } else { obj.updateLastAccessed(); - return obj.getValue(); + return obj.value; } } @@ -136,7 +121,7 @@ public List> getEntries() { public List> getEntries(Predicate keyFilter) { synchronized (cacheMap) { - return getEntryStream(keyFilter).map(entry -> Pair.of(entry.getKey(), entry.getValue().getValue())).toList(); + return getEntryStream(keyFilter).map(entry -> Pair.of(entry.getKey(), entry.getValue().value)).toList(); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/CacheObject.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/CacheObject.java index a3d639b4..c770e71b 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/CacheObject.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/CacheObject.java @@ -3,11 +3,13 @@ import java.util.Optional; import java.util.function.Function; -public interface CacheObject { +import manifold.ext.props.rt.api.val; - long getCreated(); +public sealed interface CacheObject permits ExpiringCacheObject, TemporaryCacheObject { - T getValue(); + @val long created; + @val T value; + @val long age; void updateLastAccessed(); @@ -20,12 +22,12 @@ static CacheObject fromString(String string, Function valueToO if (cacheObject.isPresent()) { return cacheObject.get(); } - Optional> temporaryCacheObject = TemporaryCacheObject.fromString(string, valueToObjectMapper); + Optional> temporaryCacheObject = + TemporaryCacheObject.fromString(string, valueToObjectMapper); if (temporaryCacheObject.isPresent()) { return temporaryCacheObject.get(); } throw new IllegalStateException("Could not parse value: " + string); } - long getAge(); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/DiskCache.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/DiskCache.java index 6a90635f..7d3f5547 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/DiskCache.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/DiskCache.java @@ -19,99 +19,101 @@ import java.util.Set; import java.util.function.Predicate; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.lodder.subtools.sublibrary.util.lazy.LazyBiFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; - -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ Files.class }) -public abstract class DiskCache extends Cache { +public abstract sealed class DiskCache extends Cache permits SerializableDiskCache, TypedDiskCache { private static final Logger LOGGER = LoggerFactory.getLogger(DiskCache.class); - public static final Object LOCK = new Object(); + private static final Object LOCK = new Object(); + private final Long timeToLiveSeconds; private final Set doublesToRemove = new HashSet<>(); private final Map> removedToAdd = new HashMap<>(); + protected abstract @val Class dbKeyType; + protected abstract @val Class dbValueType; + private final LazyBiFunction, String, Connection> connection = + new LazyBiFunction<>((cache, tableName) -> { + try { + synchronized (cache.cacheMap) { + Path path = Path.of(System.getProperty("user.home")).resolve(".MultiSubDownloader"); + if (!Files.exists(path)) { + try { + Files.createDirectory(path); + } catch (IOException e) { + throw new RuntimeException("Could not create folder $path", e); + } + } + Class.forName("org.hsqldb.jdbcDriver"); + Connection connection = DriverManager.getConnection( + "jdbc:hsqldb:file:$path/diskcache.hsqldb;hsqldb.write_delay=false;shutdown=true", + "user", "pass"); + + try (Statement stmt = connection.createStatement()) { + stmt.execute("create table IF NOT EXISTS $tableName (key %s, cacheobject %s);".formatted( + dbKeyType == String.class ? "VARCHAR(32768)" : "OBJECT", + dbValueType == String.class ? "VARCHAR(32768)" : "OBJECT")); + } - private final LazyBiFunction, String, Connection> connection = new LazyBiFunction<>((cache, tableName) -> { - try { - synchronized (cache.getCacheMap()) { - Path path = Path.of(System.getProperty("user.home")).resolve(".MultiSubDownloader"); - if (!path.exists()) { - try { - Files.createDirectory(path); - } catch (IOException e) { - throw new RuntimeException("Could not create folder " + path, e); - } - } - Class.forName("org.hsqldb.jdbcDriver"); - Connection connection = DriverManager.getConnection( - "jdbc:hsqldb:file:" + path + "/diskcache.hsqldb;hsqldb.write_delay=false;shutdown=true", "user", "pass"); - - try (Statement stmt = connection.createStatement()) { - stmt.execute("create table IF NOT EXISTS %s (key %s, cacheobject %s);".formatted(tableName, - getDbKeyType() == String.class ? "VARCHAR(32768)" : "OBJECT", - getDbValueType() == String.class ? "VARCHAR(32768)" : "OBJECT")); - } - - boolean errorWhileReadingCacheFile = false; - try ( - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT key, cacheobject FROM %s;".formatted(tableName))) { - // Map> tempCache = new HashMap<>(); - Multimap> tempCache = MultimapBuilder.hashKeys() - .treeSetValues(Comparator.comparingLong((CacheObject value) -> value.getAge()).reversed()).build(); - synchronized (cache.getCacheMap()) { - while (rs.next()) { + boolean errorWhileReadingCacheFile = false; + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT key, cacheobject FROM $tableName;")) { + Multimap> tempCache = MultimapBuilder.hashKeys() + .treeSetValues(Comparator.comparingLong((CacheObject value) -> value.getAge()) + .reversed()) + .build(); + synchronized (cache.cacheMap) { + while (rs.next()) { + try { + tempCache.put(cache.diskObjectToKey(rs.getObject("key")), + cache.diskCacheObjectToValue(rs.getObject("cacheobject"))); + } catch (SQLException e2) { + LOGGER.error("Unable to insert object in disk cache. (${e2.getMessage()})", e2); + errorWhileReadingCacheFile = true; + } + } + Map>> map = tempCache.asMap(); + map.entrySet().stream().filter(entry -> entry.getValue().size() > 1).forEach(entry -> { + doublesToRemove.add(entry.getKey()); + removedToAdd.put(entry.getKey(), entry.getValue().iterator().next()); + }); + map.entrySet() + .stream() + .sorted(Comparator.comparingLong( + entry -> entry.getValue().iterator().next().getAge())) + .forEach(entry -> put(entry.getKey(), entry.getValue().iterator().next())); + } + } catch (SQLException e) { + LOGGER.error("Unable while insert objects in disk cache! (${e.getMessage()})", e); + } + if (errorWhileReadingCacheFile) { + LOGGER.error("Deleting cache file to fix errors"); + connection.close(); try { - tempCache.put(cache.diskObjectToKey(rs.getObject("key")), cache.diskCacheObjectToValue(rs.getObject("cacheobject"))); - } catch (SQLException e2) { - LOGGER.error("Unable to insert object in disk cache. (%s)".formatted(e2.getMessage()), e2); - errorWhileReadingCacheFile = true; + path.deletePath(); + } catch (IOException e) { + LOGGER.error("Error while deleting the cache file, please delete it yourself: $path " + + "(${e.getMessage()})", e); } + connection = DriverManager.getConnection( + "jdbc:hsqldb:file:$path/diskcache.hsqldb;hsqldb.write_delay=false;shutdown=true", + "user", "pass"); } - Map>> map = tempCache.asMap(); - map.entrySet().stream().filter(entry -> entry.getValue().size() > 1).forEach(entry -> { - doublesToRemove.add(entry.getKey()); - removedToAdd.put(entry.getKey(), entry.getValue().iterator().next()); - }); - map.entrySet().stream().sorted(Comparator.comparingLong(entry -> entry.getValue().iterator().next().getAge())) - .forEach(entry -> put(entry.getKey(), entry.getValue().iterator().next())); + return connection; } + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to load jdbcdriver for diskcache"); } catch (SQLException e) { - LOGGER.error("Unable while insert objects in disk cache!" + e.getMessage(), e); + throw new RuntimeException(e); } - if (errorWhileReadingCacheFile) { - LOGGER.error("Deleting cache file to fix errors"); - connection.close(); - try { - FileUtils.delete(path); - } catch (IOException e) { - LOGGER.error("Error while deleting the cache file, please delete it yourself: %s (%s)".formatted(path, e.getMessage()), e); - } - connection = DriverManager.getConnection( - "jdbc:hsqldb:file:" + path + "/diskcache.hsqldb;hsqldb.write_delay=false;shutdown=true", "user", "pass"); - } - return connection; - } - } catch (ClassNotFoundException e) { - throw new RuntimeException("Unable to load jdbcdriver for diskcache"); - } catch (SQLException e) { - throw new RuntimeException(e); - } - }); + }); private final String tableName; - protected abstract Class getDbKeyType(); - - protected abstract Class getDbValueType(); - private Connection getConnection() { return connection.apply(this, tableName); } @@ -137,11 +139,12 @@ public void cleanup() { } public void cleanup(Predicate keyFilter) { - synchronized (getCacheMap()) { - Iterator>> itr = getCacheMap().entrySet().iterator(); + synchronized (cacheMap) { + Iterator>> itr = cacheMap.entrySet().iterator(); while (itr.hasNext()) { Entry> entry = itr.next(); - if ((keyFilter == null || keyFilter.test(entry.getKey())) && entry.getValue().isExpired(timeToLiveSeconds * 1000)) { + if ((keyFilter == null || keyFilter.test(entry.getKey())) && + entry.getValue().isExpired(timeToLiveSeconds * 1000)) { itr.remove(); removeFromDisk(entry.getKey()); } @@ -160,8 +163,7 @@ public void remove(K key) { private void removeFromDisk(K key) { synchronized (LOCK) { - try (PreparedStatement prep = getConnection().prepareStatement("delete from %s where key = ?".formatted(tableName))) { - // prep.clearParameters(); + try (PreparedStatement prep = getConnection().prepareStatement("delete from $tableName where key = ?")) { prep.setObject(1, keyToDiskObject(key)); prep.executeUpdate(); } catch (SQLException e) { @@ -196,11 +198,12 @@ public final void put(K key, V value, long timeToLive) { private void putFromMemoryCache(K key) { synchronized (LOCK) { - try (PreparedStatement prep = getConnection().prepareCall("INSERT INTO %s (key,cacheobject) VALUES (?,?)".formatted(tableName))) { + try (PreparedStatement prep = getConnection().prepareCall( + "INSERT INTO $tableName (key,cacheobject) VALUES (?,?)")) { prep.clearParameters(); prep.setObject(1, keyToDiskObject(key)); - synchronized (getCacheMap()) { - CacheObject cacheObject = getCacheMap().get(key); + synchronized (cacheMap) { + CacheObject cacheObject = cacheMap.get(key); prep.setObject(2, cacheObjectToDiskObject(cacheObject)); prep.execute(); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/ExpiringCacheObject.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/ExpiringCacheObject.java index 888e0254..2b471b42 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/ExpiringCacheObject.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/ExpiringCacheObject.java @@ -9,22 +9,22 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; import lombok.ToString; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; @ToString -@Setter -@Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) -class ExpiringCacheObject implements CacheObject, Serializable { +sealed class ExpiringCacheObject implements CacheObject, Serializable permits ExpiringSerializableCacheObject { @Serial private static final long serialVersionUID = 3852086993086134232L; private static final Pattern PATTERN = Pattern.compile("created:(.*?)|lastAccessed:(.*?)|value:(.*)"); - private final long created; - private long lastAccessed = System.currentTimeMillis(); - private T value; + + @override @val long created; + @var long lastAccessed = System.currentTimeMillis(); + @override @var T value; protected ExpiringCacheObject(T value) { this.created = System.currentTimeMillis(); @@ -43,7 +43,7 @@ public boolean isExpired(long ttl) { @Override public String toString(Function valueToStringMapper) { - return "created:%s|lastAccessed:%s|value:%s".formatted(created, lastAccessed, valueToStringMapper.apply(value)); + return "created:$created|lastAccessed:$lastAccessed|value:${valueToStringMapper.apply(value)}"; } public static Optional> fromString(String string, Function valueToObjectMapper) { @@ -59,6 +59,6 @@ public static Optional> fromString(String string, Function extends ExpiringCacheObject implements Serializable { +final class ExpiringSerializableCacheObject extends ExpiringCacheObject + implements Serializable { @Serial private static final long serialVersionUID = 8773462650510864103L; diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/InMemoryCache.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/InMemoryCache.java index 281d7b75..dfc51aa6 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/InMemoryCache.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/InMemoryCache.java @@ -1,19 +1,19 @@ package org.lodder.subtools.sublibrary.cache; +import static manifold.ext.props.rt.api.PropOption.*; + import java.io.Serializable; import java.util.function.Predicate; -import lombok.AccessLevel; -import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import manifold.ext.props.rt.api.val; -@Getter(value = AccessLevel.PROTECTED) -public class InMemoryCache extends Cache { +public final class InMemoryCache extends Cache { - private final Long timeToLive; + @val(Protected) Long timeToLive; - protected InMemoryCache(Long timeToLiveSeconds, Long timerIntervalSeconds, Integer maxItems) { + public InMemoryCache(Long timeToLiveSeconds, Long timerIntervalSeconds, Integer maxItems) { super(maxItems); if (maxItems != null && maxItems < 1) { throw new IllegalStateException("maxItems should be a positive number"); @@ -79,6 +79,7 @@ private void createCleanUpThread(long timerInterval) { try { Thread.sleep(timerInterval); } catch (InterruptedException ignored) { + //ignore } cleanup(); } @@ -93,9 +94,10 @@ public void cleanup() { } public void cleanup(Predicate keyFilter) { - synchronized (getCacheMap()) { - getCacheMap().entrySet() - .removeIf(entry -> (keyFilter == null || keyFilter.test(entry.getKey())) && entry.getValue().isExpired(timeToLive)); + synchronized (cacheMap) { + cacheMap.entrySet() + .removeIf(entry -> (keyFilter == null || keyFilter.test(entry.getKey())) && + entry.getValue().isExpired(timeToLive)); Thread.yield(); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/LRUMap.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/LRUMap.java index 7cca550b..eede6031 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/LRUMap.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/LRUMap.java @@ -16,4 +16,14 @@ public class LRUMap extends LinkedHashMap { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxItems; } + + @Override + public int hashCode() { + return maxItems + super.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof LRUMap map && this.maxItems == map.maxItems && super.equals(map); + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/SerializableDiskCache.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/SerializableDiskCache.java index f0964b1b..61d59587 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/SerializableDiskCache.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/SerializableDiskCache.java @@ -1,18 +1,18 @@ package org.lodder.subtools.sublibrary.cache; +import static manifold.ext.props.rt.api.PropOption.*; + import java.io.Serializable; -import lombok.AccessLevel; -import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; -public class SerializableDiskCache extends DiskCache { +public final class SerializableDiskCache extends DiskCache { - @Getter(value = AccessLevel.PROTECTED) - private final Class dbKeyType; - @Getter(value = AccessLevel.PROTECTED) - private final Class dbValueType; + @override @val(Protected) Class dbKeyType; + @override @val(Protected) Class dbValueType; @SuppressWarnings("rawtypes") public static DiskCacheBuilderKeyTypeIntf cacheBuilder() { @@ -46,7 +46,8 @@ public interface DiskCacheBuilderPasswordIntf - implements DiskCacheBuilderOtherIntf, DiskCacheBuilderPasswordIntf, DiskCacheBuilderValueTypeIntf, + implements DiskCacheBuilderOtherIntf, DiskCacheBuilderPasswordIntf, + DiskCacheBuilderValueTypeIntf, DiskCacheBuilderKeyTypeIntf { private Class keyType; private Class valueType; @@ -76,7 +77,8 @@ public SerializableDiskCache build() { } } - private SerializableDiskCache(Class keyType, Class valueType, Long timeToLive, Integer maxItems, String username, String password, + private SerializableDiskCache(Class keyType, Class valueType, Long timeToLive, Integer maxItems, + String username, String password, String cacheName) { super(timeToLive, maxItems, username, password, cacheName); this.dbKeyType = keyType; diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporaryCacheObject.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporaryCacheObject.java index d022efb6..e6eee11f 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporaryCacheObject.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporaryCacheObject.java @@ -9,22 +9,20 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; import lombok.ToString; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; @ToString -@Setter -@Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) -class TemporaryCacheObject implements CacheObject, Serializable { +sealed class TemporaryCacheObject implements CacheObject, Serializable permits TemporarySerializableCacheObject { @Serial private static final long serialVersionUID = -152474119228350222L; private static final Pattern PATTERN = Pattern.compile("created:(.*?)|expire:(.*?)|value:(.*)"); - private final long created; - private final long timeToLive; - private T value; + @override @val long created; + @val long timeToLive; + @override @val T value; protected TemporaryCacheObject(long timeToLive, T value) { this.created = System.currentTimeMillis(); @@ -52,7 +50,8 @@ public String toString(Function valueToStringMapper) { return "created:%s|expire:%s|value:%s".formatted(created, timeToLive, valueToStringMapper.apply(value)); } - public static Optional> fromString(String string, Function valueToObjectMapper) { + public static Optional> fromString(String string, + Function valueToObjectMapper) { Matcher matcher = PATTERN.matcher(string); if (matcher.matches()) { long created = Long.parseLong(matcher.group(1)); @@ -65,6 +64,6 @@ public static Optional> fromString(String string, Fu @Override public long getAge() { - return getCreated(); + return created; } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporarySerializableCacheObject.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporarySerializableCacheObject.java index 562c4b84..d2db881f 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporarySerializableCacheObject.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TemporarySerializableCacheObject.java @@ -3,14 +3,11 @@ import java.io.Serial; import java.io.Serializable; -import lombok.Getter; -import lombok.Setter; import lombok.ToString; @ToString -@Setter -@Getter -class TemporarySerializableCacheObject extends TemporaryCacheObject implements Serializable { +final class TemporarySerializableCacheObject extends TemporaryCacheObject + implements Serializable { @Serial private static final long serialVersionUID = 3426939140266268946L; diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TypedDiskCache.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TypedDiskCache.java index eac42aa5..db8e6ca2 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TypedDiskCache.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TypedDiskCache.java @@ -2,24 +2,24 @@ import java.util.function.Function; -import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; - +import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import net.jodah.typetools.TypeResolver; -public class TypedDiskCache extends DiskCache { +public final class TypedDiskCache extends DiskCache { private final Function toStringMapperKey; private final Function toObjectMapperKey; private final Function toStringMapperValue; private final Function toObjectMapperValue; - @SuppressWarnings("unchecked") - private final LazySupplier> keyType = - new LazySupplier<>(() -> (Class) TypeResolver.resolveRawArguments(TypedDiskCache.class, this.getClass())[0]); - @SuppressWarnings("unchecked") - private final LazySupplier> valueType = - new LazySupplier<>(() -> (Class) TypeResolver.resolveRawArguments(TypedDiskCache.class, this.getClass())[1]); + + @Getter(lazy = true) @SuppressWarnings("unchecked") + private final Class dbKeyType = + (Class) TypeResolver.resolveRawArguments(TypedDiskCache.class, this.getClass())[0]; + @Getter(lazy = true) @SuppressWarnings("unchecked") + private final Class dbValueType = + (Class) TypeResolver.resolveRawArguments(TypedDiskCache.class, this.getClass())[1]; @SuppressWarnings("rawtypes") public static DiskCacheBuilderToStringMapperKeyIntf cacheBuilder() { @@ -60,8 +60,10 @@ public interface DiskCacheBuilderPasswordIntf { @Setter @Accessors(chain = true, fluent = true) - public static class DiskCacheBuilder implements DiskCacheBuilderPasswordIntf, DiskCacheBuilderOtherIntf, - DiskCacheBuilderToObjectMapperValueIntf, DiskCacheBuilderToStringMapperValueIntf, DiskCacheBuilderToObjectMapperKeyIntf, + public static class DiskCacheBuilder + implements DiskCacheBuilderPasswordIntf, DiskCacheBuilderOtherIntf, + DiskCacheBuilderToObjectMapperValueIntf, DiskCacheBuilderToStringMapperValueIntf, + DiskCacheBuilderToObjectMapperKeyIntf, DiskCacheBuilderToStringMapperKeyIntf { private Long timeToLive; private Integer maxItems; @@ -89,13 +91,16 @@ public DiskCacheBuilder toStringMapperValue(Function toStri @Override public TypedDiskCache build() { - return new TypedDiskCache<>(timeToLive, maxItems, username, password, toStringMapperKey, toObjectMapperKey, toStringMapperValue, + return new TypedDiskCache<>(timeToLive, maxItems, username, password, toStringMapperKey, toObjectMapperKey, + toStringMapperValue, toObjectMapperValue, cacheName); } } - private TypedDiskCache(Long timeToLive, Integer maxItems, String username, String password, Function toStringMapperKey, - Function toObjectMapperKey, Function toStringMapperValue, Function toObjectMapperValue, + private TypedDiskCache(Long timeToLive, Integer maxItems, String username, String password, + Function toStringMapperKey, + Function toObjectMapperKey, Function toStringMapperValue, + Function toObjectMapperValue, String cacheName) { super(timeToLive, maxItems, username, password, cacheName); this.toStringMapperKey = toStringMapperKey; @@ -124,15 +129,4 @@ protected Object keyToDiskObject(K key) { protected Object cacheObjectToDiskObject(CacheObject value) { return value.toString(toStringMapperValue); } - - @Override - protected Class getDbKeyType() { - return keyType.get(); - } - - @Override - protected Class getDbValueType() { - return valueType.get(); - } - } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/ReleaseParser.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/ReleaseParser.java index aab6b332..44c95fb7 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/ReleaseParser.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/ReleaseParser.java @@ -8,11 +8,11 @@ import java.util.regex.Pattern; import java.util.stream.IntStream; +import extensions.java.nio.file.Path.PathExt; import org.lodder.subtools.sublibrary.exception.ReleaseParseException; import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.TvRelease; -import org.lodder.subtools.sublibrary.util.FileUtils; import org.lodder.subtools.sublibrary.util.NamedMatcher; import org.lodder.subtools.sublibrary.util.NamedPattern; import org.slf4j.Logger; @@ -20,13 +20,13 @@ public class ReleaseParser { - private NamedMatcher namedMatcher; private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseParser.class); + private NamedMatcher namedMatcher; public final Release parse(Path file) throws ReleaseParseException { String folderName = file.getParent() != null ? file.getParent().getFileName().toString() : ""; - for (String fileParseName : List.of(file.getFileName().toString(), folderName)) { + for (String fileParseName : List.of(file.fileName.toString(), folderName)) { for (NamedPattern np : VideoPatterns.COMPILED_PATTERNS) { namedMatcher = np.matcher(fileParseName); if (namedMatcher.find()) { @@ -35,16 +35,16 @@ public final Release parse(Path file) throws ReleaseParseException { } } } - throw new ReleaseParseException("Unknown format, can't be parsed: " + file.toAbsolutePath()); + throw new ReleaseParseException("Unknown format, can't be parsed: ${file.toAbsolutePath()}"); } private Release parsePatternResult(Path file, String fileParseName) throws ReleaseParseException { - List namedGroups = namedMatcher.namedPattern().groupNames(); + List namedGroups = namedMatcher.namedPattern().groupNames; String seriesName = ""; List episodeNumbers = new ArrayList<>(); int seasonNumber = 0; - Integer year = namedGroups.contains("year") ? Integer.parseInt(namedMatcher.group("year")): null; - String description =namedGroups.contains("description") ? namedMatcher.group("description").substring(1): ""; + Integer year = namedGroups.contains("year") ? Integer.parseInt(namedMatcher.group("year")) : null; + String description = namedGroups.contains("description") ? namedMatcher.group("description").substring(1) : ""; if (namedGroups.contains("moviename")) { String movieName; @@ -55,7 +55,8 @@ private Release parsePatternResult(Path file, String fileParseName) throws Relea } else if (namedGroups.contains("partnumber")) { number = namedMatcher.group("partnumber"); } - movieName = cleanUnwantedChars(namedMatcher.group("moviename") + " " + namedMatcher.group("part") + " " + number); + movieName = cleanUnwantedChars( + namedMatcher.group("moviename") + " " + namedMatcher.group("part") + " " + number); } else { movieName = cleanUnwantedChars(namedMatcher.group("moviename")); } @@ -64,7 +65,7 @@ private Release parsePatternResult(Path file, String fileParseName) throws Relea .file(file) .year(year) .description(description) - .releaseGroup(extractReleasegroup(file.getFileName().toString(), true)) + .releaseGroup(extractReleaseGroup(file.getFileName().toString(), true)) .quality(getQualityKeyword(fileParseName)) .build(); } @@ -134,8 +135,8 @@ private Release parsePatternResult(Path file, String fileParseName) throws Relea .season(seasonNumber) .episodes(episodeNumbers) .file(file) - .description(FileUtils.withoutExtension(description)) - .releaseGroup(extractReleasegroup(file.getFileName().toString(), true)) + .description(PathExt.withoutExtension(description)) + .releaseGroup(extractReleaseGroup(file.getFileName().toString(), true)) .special(isSpecialEpisode(seasonNumber, episodeNumbers)) .quality(getQualityKeyword(fileParseName)) .build(); @@ -185,12 +186,12 @@ public static List getQualityKeyWords(String name) { while (m.find()) { keywords.add(m.group(0)); } - LOGGER.trace("getQualityKeyWords: keyswords: {}", keywords); + LOGGER.trace("getQualityKeyWords: keywords: {}", keywords); return keywords; } - public static String extractReleasegroup(final String fileName, boolean hasExtension) { - LOGGER.trace("extractReleasegroup: name: {} , hasExtension: {}", fileName, hasExtension); + public static String extractReleaseGroup(final String fileName, boolean hasExtension) { + LOGGER.trace("extractReleaseGroup: name: {} , hasExtension: {}", fileName, hasExtension); Pattern releaseGroupPattern; if (hasExtension) { releaseGroupPattern = Pattern.compile("-([\\w]+).[\\w]+$"); @@ -203,7 +204,7 @@ public static String extractReleasegroup(final String fileName, boolean hasExten releaseGroup = matcher.group(1); } - LOGGER.trace("extractReleasegroup: release group: {}", releaseGroup); + LOGGER.trace("extractReleaseGroup: release group: {}", releaseGroup); return releaseGroup; } @@ -211,6 +212,6 @@ public static boolean isSpecialEpisode(final int season, final List epi if (season == 0) { return true; } - return episodeNumbers.size() == 1 && episodeNumbers.get(0) == 0; + return episodeNumbers.size() == 1 && episodeNumbers.first == 0; } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Roman.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Roman.java index b0f8e70a..70d84279 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Roman.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Roman.java @@ -1,9 +1,11 @@ package org.lodder.subtools.sublibrary.control; +import lombok.experimental.UtilityClass; + /** - * Source + * Source */ - +@UtilityClass public class Roman { private static int decodeSingle(char letter) { return switch (letter) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/VideoPatterns.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/VideoPatterns.java index 937c5480..99c03bb1 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/VideoPatterns.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/VideoPatterns.java @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.control; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,67 +8,48 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.lodder.subtools.sublibrary.util.NamedPattern; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.ExtensionMethod; +import lombok.AllArgsConstructor; import lombok.experimental.UtilityClass; +import manifold.ext.props.rt.api.val; +import org.lodder.subtools.sublibrary.util.NamedPattern; @UtilityClass -@ExtensionMethod({ Arrays.class }) public class VideoPatterns { public interface VideoPatternEnumIntf { } - @Getter - @RequiredArgsConstructor + @AllArgsConstructor public enum Quality implements VideoPatternEnumIntf { Q1080P("1080p"), Q1080I("1080i"), Q720P("720p"), Q480P("480p"); - final String value; - - public static Stream getValuesStream() { - return Quality.values().stream().map(Quality::getValue); - } + @val String value; } - @Getter public enum VideoEncoding implements VideoPatternEnumIntf { X264("x264", "h264"), X265("x265", "h265"); - final String[] values; + @val String[] values; VideoEncoding(String... values) { this.values = values; } - - public static Stream getValuesStream() { - return VideoEncoding.values().stream().map(VideoEncoding::getValues).flatMap(Arrays::stream); - } } - @Getter public enum AudioEncoding implements VideoPatternEnumIntf { DD5_1("dd5.1", "dd5-1"); - final String[] values; + @val String[] values; AudioEncoding(String... values) { this.values = values; } - - public static Stream getValuesStream() { - return AudioEncoding.values().stream().map(AudioEncoding::getValues).flatMap(Arrays::stream); - } } - @Getter public enum Source implements VideoPatternEnumIntf { HDTV(false, "hdtv"), DVDRIP(false, "dvdrip"), @@ -90,27 +70,23 @@ public enum Source implements VideoPatternEnumIntf { static final Map VALUE_MAP = new HashMap<>(); static { - Arrays.stream(Source.values()).forEach(source -> Arrays.stream(source.getValues()).forEach(value -> VALUE_MAP.put(value, source))); + Source.values().forEach(source -> source.values.forEach(value -> VALUE_MAP.put(value, source))); } - final boolean manyDifferentSources; - final String[] values; + @val boolean manyDifferentSources; + @val String[] values; Source(boolean manyDifferentSources, String... values) { this.manyDifferentSources = manyDifferentSources; this.values = values; } - public static Stream getValuesStream() { - return Source.values().stream().map(Source::getValues).flatMap(Arrays::stream); - } - public static Source fromValue(String value) { return VALUE_MAP.get(value); } public boolean isTypeForValue(String value) { - return Arrays.stream(getValues()).map(String::toLowerCase).anyMatch(value::equals); + return values.stream().map(String::toLowerCase).anyMatch(value::equals); } @Override @@ -119,8 +95,7 @@ public String toString() { } } - @Getter - @RequiredArgsConstructor + @AllArgsConstructor public enum VideoExtensions { MKV("mkv"), MP4("mp4"), @@ -129,7 +104,7 @@ public enum VideoExtensions { TS("ts"), M4V("m4v"); - final String value; + @val String value; } private static final Set QUALITY_KEYWORDS_SET = Set.of("hdtv", "dvdrip", "bluray", @@ -144,50 +119,84 @@ public enum VideoExtensions { private static final String[] PATTERNS = { // example: // Back.to.the.Future.Part.II.1989.720p.BluRay.X264-AMIABLE.mkv - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[I|V|X]+)[. ](?19\\d{2}|20\\d{2})(?['\\w\\s:&()!.,_-]+)", - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[I|V|X]+)[. ](?19\\d{2}|20\\d{2})(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[I|V|X]+)[. ]" + + "(?19\\d{2}|20\\d{2})" + + "(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[I|V|X]+)[. ]" + + "(?19\\d{2}|20\\d{2})" + + "(?['\\w\\s:&()!.,_-]+)", // The.Hunger.Games.Mockingjay.Part.1..2014.720p.BluRay.x264-SPARKS.mkv - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[\\d]{1})[. ](?19\\d{2}|20\\d{2})(?['\\w\\s:&()!.,_-]+)", - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[\\d]{1})[. ](?19\\d{2}|20\\d{2})(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[\\d]{1})[. ]" + + "(?19\\d{2}|20\\d{2})" + + "(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[\\d]{1})[. ]" + + "(?19\\d{2}|20\\d{2})" + + "(?['\\w\\s:&()!.,_-]+)", // serie - "(?['\\w\\s:&()!.,_-]+)[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1,2})(?[XxEe][\\d]{1,2})*[XxEe](?[\\d]{1,2})(?['\\w\\s:&()!.,_-]+)", - "(?['\\w\\s:&()!.,_-]+)[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1,3})(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)[Ss. _](?[\\d]{1,2})[XxEe]{1,2}" + + "(?[\\d]{1,2})" + + "(?[XxEe][\\d]{1,2})*[XxEe](?[\\d]{1,2})" + + "(?['\\w\\s:&()!.," + + "_-]+)", + "(?['\\w\\s:&()!.,_-]+)[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1," + + "3})(?['\\w\\s:&()" + + "!.,_-]+)", // sXeX - Serienaam - Titel ex: S04E02 - White Collar - Most Wanted.mkv - "[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1,2})(?[XxEe][\\d]{1,2})*[XxEe](?[\\d]{1,2})\\s?+-?\\s?+(?['\\w\\s:&()!.,_]+)\\s?+-?\\s?+(?['\\w\\s:&()!.,_]+)", - "[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1,2})\\s?+-?\\s?+(?['\\w\\s:&()!.,_]+)\\s?+-?\\s?+(?['\\w\\s:&()!.,_]+)", + "[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1,2})" + + "(?[XxEe][\\d]{1,2})*[XxEe]" + + "(?[\\d]{1,2})\\s?+-?\\s?+(?['\\w\\s:&()!.,_]+)\\s?+-?\\s?+" + + "(?['\\w\\s:&()!.,_]+)", + "[Ss. _](?[\\d]{1,2})[XxEe]{1,2}(?[\\d]{1,2})\\s?+-?\\s?+" + + "(?['\\w\\s:&()!.,_]+)\\s?+-?\\s?+" + + "(?['\\w\\s:&()!.,_]+)", // example: hawaii.five-0.2010.410.hdtv-lol.mp4 // example: // Spartacus.Gods.of.The.Arena.Pt.I.720p.HDTV.X264-DIMENSION.mkv - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[I|V|X]+)(?['\\w\\s:&()!.,_-]+)", - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[I|V|X]+)(?['\\w\\s:&()!.,_-]+)", - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[\\d]{1,2})(?['\\w\\s:&()!.,_-]+)", - "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[\\d]{1,2})(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[I|V|X]+)" + + "(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[I|V|X]+)" + + "(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)(?[\\d]{1,2})" + + "(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)(?Pt|Part|pt|part|Ep)[.](?[\\d]{1,2})" + + "(?['\\w\\s:&()!.,_-]+)", // example hawaii.five-0.2010.410.hdtv-lol.mp4 - "(?['\\w\\s:&()!.,_-]+)[. ](?19\\d{2}|20\\d{2})[. ](?[\\d]{3,4})[. ](?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)[. ](?19\\d{2}|20\\d{2})[. ](?[\\d]{3,4})[. ]" + + "(?['\\w\\s:&()!.," + + "_-]+)", // format movietitle.year - "(?['\\w\\s:&()!.,_-]+)[\\.|\\[|\\(| ]{1}(?19\\d{2}|20\\d{2})(?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)[\\.|\\[|\\(| ]{1}(?19\\d{2}|20\\d{2})(?['\\w\\s:&()" + + "!.,_-]+)", // format episode.0101.title // format episode.101.title // exclude format movietitle.720p - "(?['\\w\\s:&()!.,_-]+)[. ](?[\\d]{3,4})[. ](?['\\w\\s:&()!.,_-]+)", + "(?['\\w\\s:&()!.,_-]+)[. ](?[\\d]{3,4})[. ](?['\\w\\s:&()!.," + + "_-]+)", // format (2-11) Joey and the High School Friend - "[(](?[\\d]{1,2})[-](?[\\d]{1,2})[) ](?['\\w\\s:&()!.,_-]+)[ ]and(?['\\w\\s:&()!.,_-]+)", - "[(](?[\\d]{1,2})[-](?[\\d]{1,2})[) ](?['\\w\\s:&()!.,_-]+)[ ]And(?['\\w\\s:&()!.,_-]+)", + "[(](?[\\d]{1,2})[-](?[\\d]{1,2})[) ](?['\\w\\s:&()!.,_-]+)[ " + + "]and(?['\\w\\s:&()!" + + ".,_-]+)", + "[(](?[\\d]{1,2})[-](?[\\d]{1,2})[) ](?['\\w\\s:&()!.,_-]+)[ " + + "]And(?['\\w\\s:&()!" + + ".,_-]+)", // take the rest and treat as movie "(?['\\w\\s:&()!.,_-]+)[\\.|\\[|\\(| ]{1}[720P|1080P](?['\\w\\s:&()!.,_-]+)" }; public static final List COMPILED_PATTERNS = - Arrays.stream(PATTERNS).map(p -> NamedPattern.compile(p, Pattern.CASE_INSENSITIVE)).toList(); + PATTERNS.stream().map(p -> NamedPattern.compile(p, Pattern.CASE_INSENSITIVE)).toList(); public static final List QUALITY_KEYWORDS = List.of(); // Stream.concat(QUALITY_KEYWORDS_SET.stream(), - // new Generex(QUALITY_KEYWORDS_REGEX_SET.stream().collect(Collectors.joining("|"))).getAllMatchedStrings().stream()).toList(); + // new Generex(QUALITY_KEYWORDS_REGEX_SET.stream().collect(Collectors.joining("|"))).getAllMatchedStrings() + // .stream()).toList(); private static final String QUALITY_KEYWORDS_REGEX = - Stream.concat(QUALITY_KEYWORDS_SET.stream(), QUALITY_KEYWORDS_REGEX_SET.stream()).collect(Collectors.joining("|", "(", ")")); + Stream.concat(QUALITY_KEYWORDS_SET.stream(), QUALITY_KEYWORDS_REGEX_SET.stream()) + .collect(Collectors.joining("|", "(", ")")); - public static final Pattern QUALITY_KEYWORDS_REGEX_PATTERN = Pattern.compile(QUALITY_KEYWORDS_REGEX, Pattern.CASE_INSENSITIVE); + public static final Pattern QUALITY_KEYWORDS_REGEX_PATTERN = + Pattern.compile(QUALITY_KEYWORDS_REGEX, Pattern.CASE_INSENSITIVE); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/Html.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/Html.java index c9d9a064..c5eadda7 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/Html.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/Html.java @@ -3,15 +3,14 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.Manager.PageContentBuilderCacheTypeIntf; -@RequiredArgsConstructor +@AllArgsConstructor public class Html { - @Getter - private final Manager manager; + @val Manager manager; private final String userAgent; public Html(Manager manager) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ProviderSerieId.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ProviderSerieId.java index e38445e1..cf1d0168 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ProviderSerieId.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ProviderSerieId.java @@ -3,17 +3,16 @@ import java.io.Serial; import java.io.Serializable; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor +@AllArgsConstructor @EqualsAndHashCode public class ProviderSerieId implements Serializable { @Serial private static final long serialVersionUID = -120703658294502220L; - private final String name; - private final String id; + @val String name; + @val String id; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ReleaseDBIntf.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ReleaseDBIntf.java index c6abf762..572c64be 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ReleaseDBIntf.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/ReleaseDBIntf.java @@ -1,8 +1,10 @@ package org.lodder.subtools.sublibrary.data; +import manifold.ext.props.rt.api.val; + public interface ReleaseDBIntf { - String getName(); + @val String name; - int year(); + @val int year; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettings.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettings.java index f68c0696..ac9d9e09 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettings.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettings.java @@ -2,24 +2,23 @@ import java.util.List; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.control.VideoPatterns; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Getter +@AllArgsConstructor public class UserInteractionSettings implements UserInteractionSettingsIntf { - private final boolean optionsAlwaysConfirm; + @override @val boolean optionsAlwaysConfirm; - private final boolean optionsMinAutomaticSelection; + @override @val boolean optionsMinAutomaticSelection; - private final int optionsMinAutomaticSelectionValue; + @override @val int optionsMinAutomaticSelectionValue; - private final boolean optionsDefaultSelection; + @override @val boolean optionsDefaultSelection; - private final List optionsDefaultSelectionQualityList; + @override @val List optionsDefaultSelectionQualityList; - private final boolean optionsConfirmProviderMapping; + @override @val boolean optionsConfirmProviderMapping; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettingsIntf.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettingsIntf.java index b44a1cf3..c2d57d4c 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettingsIntf.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/UserInteractionSettingsIntf.java @@ -2,19 +2,20 @@ import java.util.List; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; public interface UserInteractionSettingsIntf { - boolean isOptionsAlwaysConfirm(); + @val boolean optionsAlwaysConfirm; - boolean isOptionsMinAutomaticSelection(); + @val boolean optionsMinAutomaticSelection; - int getOptionsMinAutomaticSelectionValue(); + @val int optionsMinAutomaticSelectionValue; - boolean isOptionsDefaultSelection(); + @val boolean optionsDefaultSelection; - List getOptionsDefaultSelectionQualityList(); + @val List optionsDefaultSelectionQualityList; - boolean isOptionsConfirmProviderMapping(); + @val boolean optionsConfirmProviderMapping; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbAdapter.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbAdapter.java index f00eac78..ffebf70a 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbAdapter.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbAdapter.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.OptionalInt; +import com.pivovarit.function.ThrowingBiFunction; import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.sublibrary.Manager; @@ -15,16 +16,10 @@ import org.lodder.subtools.sublibrary.data.imdb.model.ImdbDetails; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderInitException; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.pivovarit.function.ThrowingBiFunction; - -import lombok.experimental.ExtensionMethod; - -@ExtensionMethod({ OptionalExtension.class }) public class ImdbAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(ImdbAdapter.class); @@ -33,6 +28,7 @@ public class ImdbAdapter { private final UserInteractionHandler userInteractionHandler; private final LazySupplier imdbApi; private final LazySupplier imdbSearchIdApi; + private final String providerName = "IMDB"; private ImdbAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { this.manager = manager; @@ -41,31 +37,28 @@ private ImdbAdapter(Manager manager, UserInteractionHandler userInteractionHandl try { return new ImdbApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); this.imdbSearchIdApi = new LazySupplier<>(() -> { try { return new ImdbSearchIdApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } - public String getProviderName() { - return "IMDB"; - } - public Optional getMovieDetails(int imdbId) { return manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%s-MovieDetails:%s".formatted(getProviderName(), imdbId)) + .key("%s-MovieDetails:%s".formatted(providerName, imdbId)) .optionalSupplier(() -> { try { return imdbApi.get().getMovieDetails(imdbId); } catch (ImdbException e) { - LOGGER.error("API %s getMovieDetails for id [%s] (%s)".formatted(getProviderName(), imdbId, e.getMessage()), e); + LOGGER.error("API %s getMovieDetails for id [%s] (%s)".formatted(providerName, imdbId, + e.getMessage()), e); return Optional.empty(); } }).getOptional(); @@ -75,14 +68,14 @@ public OptionalInt getImdbId(String title, Integer year) { try { return manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%s-id-%s-%s".formatted(getProviderName(), title, year)) + .key("%s-id-%s-%s".formatted(providerName, title, year)) .optionalIntSupplier(() -> getImdbIdOnImdb(title, year) .orElseMap(() -> getImdbIdOnGoogle(title, year)) .orElseMap(() -> getImdbIdOnYahoo(title, year)) .orElseMap(() -> promptUserToEnterImdbId(title, year))) .storeTempNullValue().getOptionalInt(); } catch (Exception e) { - LOGGER.error("API %s getImdbId for title [%s] (%s)".formatted(getProviderName(), title, e.getMessage()), e); + LOGGER.error("API %s getImdbId for title [%s] (%s)".formatted(providerName, title, e.getMessage()), e); return OptionalInt.empty(); } } @@ -105,33 +98,36 @@ private OptionalInt getImdbIdCommon(String title, Integer year, try { providerSerieIds = providerSerieIdSupplier.apply(title, year); } catch (ImdbSearchIdException e) { - LOGGER.error("API %s getImdbId for title [%s] and year [%s] (%s)".formatted(getProviderName(), title, year, e.getMessage()), e); + LOGGER.error("API %s getImdbId for title [%s] and year [%s] (%s)".formatted(providerName, title, year, + e.getMessage()), e); return OptionalInt.empty(); } - if (!userInteractionHandler.getSettings().isOptionsConfirmProviderMapping() && providerSerieIds.size() == 1) { + if (!userInteractionHandler.settings.optionsConfirmProviderMapping && providerSerieIds.size() == 1) { // found single exact match - return OptionalInt.of(Integer.parseInt(providerSerieIds.iterator().next().getId())); + return OptionalInt.of(Integer.parseInt(providerSerieIds.iterator().next().id)); } String formattedTitle = title.replaceAll("[^A-Za-z]", ""); return userInteractionHandler .selectFromList( providerSerieIds.stream().sorted(Comparator - .comparing((ProviderSerieId providerSerieId) -> providerSerieId.getName().replaceAll("[^A-Za-z]", "") - .equalsIgnoreCase(formattedTitle), Comparator.reverseOrder()) - .thenComparing(ProviderSerieId::getName)) + .comparing((ProviderSerieId providerSerieId) -> providerSerieId.name.replaceAll( + "[^A-Za-z]", "") + .equalsIgnoreCase(formattedTitle), Comparator.reverseOrder()) + .thenComparing(ProviderSerieId::getName)) .toList(), - Messages.getString("Prompter.SelectImdbMatchForSerie").formatted(title), - getProviderName(), + Messages.getText("Prompter.SelectImdbMatchForSerie").formatted(title), + providerName, ProviderSerieId::getName) - .mapToInt(providerSerieId -> Integer.parseInt(providerSerieId.getId())); + .mapToInt(providerSerieId -> Integer.parseInt(providerSerieId.id)); } private OptionalInt promptUserToEnterImdbId(String title, int year) { - return userInteractionHandler.enter(getProviderName(), Messages.getString("Prompter.EnterImdbMatchForSerie").formatted(title), - Messages.getString("Prompter.ValueIsNotValid"), StringUtils::isNumeric).mapToInt(Integer::parseInt); + return userInteractionHandler.enter(providerName, + Messages.getText("Prompter.EnterImdbMatchForSerie").formatted(title), + Messages.getText("Prompter.ValueIsNotValid"), StringUtils::isNumeric).mapToInt(Integer::parseInt); } - public synchronized static ImdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { + public static synchronized ImdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { if (instance == null) { instance = new ImdbAdapter(manager, userInteractionHandler); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbApi.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbApi.java index 694b80c1..700dbf40 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbApi.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbApi.java @@ -2,13 +2,11 @@ import java.util.Optional; -import org.apache.commons.lang3.StringUtils; +import lombok.RequiredArgsConstructor; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.imdb.exception.ImdbException; import org.lodder.subtools.sublibrary.data.imdb.model.ImdbDetails; -import lombok.RequiredArgsConstructor; - @RequiredArgsConstructor public class ImdbApi { @@ -18,19 +16,20 @@ public class ImdbApi { public Optional getMovieDetails(int imdbId) throws ImdbException { return manager.valueBuilder() .memoryCache() - .key("%s-moviedetails-%s".formatted("IMDB", imdbId)) + .key("IMDB-moviedetails-$imdbId") .optionalSupplier(() -> { - final String url = "%s/title/tt%s/releaseinfo".formatted(DOMAIN, StringUtils.leftPad(String.valueOf(imdbId), 7, "0")); + final String url = "$DOMAIN/title/tt${%07d/releaseinfo".formatted(imdbId); try { org.jsoup.nodes.Element element = manager.getPageContentBuilder() .url(url) .getAsJsoupDocument() - .selectFirst(".article .subpage_title_block .subpage_title_block__right-column"); - String imdbName = element.selectFirst("a[itemprop='url']").text(); - int year = Integer.parseInt(element.selectFirst("span.nobr").text().replaceAll("[^0-9]", "")); + .selectFirstByCss(".article .subpage_title_block .subpage_title_block__right-column"); + String imdbName = element.selectFirstByCss("a[itemprop='url']").getText(); + int year = Integer.parseInt( + element.selectFirstByCss("span.nobr").getText().replaceAll("[^0-9]", "")); return Optional.of(new ImdbDetails(imdbName, year)); } catch (Exception e) { - throw new ImdbException("Error IMDBAPI", url, e); + throw new ImdbException("Error IMDB API", url, e); } }).getOptional(); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbSearchIdApi.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbSearchIdApi.java index 2290ee40..e0f2683d 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbSearchIdApi.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/ImdbSearchIdApi.java @@ -3,30 +3,29 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.data.imdb.exception.ImdbSearchIdException; +import util.Utils; record ImdbSearchIdApi(Manager manager) { - private static final Pattern IMDB_URL_ID_PATTERN = Pattern.compile("/title/tt([0-9]*)"); + private static final Pattern IMDB_URL_ID_PATTERN = Pattern.compile("/title/tt(\\d*)"); public Set getImdbIdOnImdb(String title, Integer year) throws ImdbSearchIdException { return manager.valueBuilder() .memoryCache() - .key("%s-imdbid-imdb-%s-%s".formatted("IMDB", title, year)) + .key("IMDB-imdbid-imdb-$title-$year") .collectionSupplier(ProviderSerieId.class, () -> { - StringBuilder sb = new StringBuilder("https://www.imdb.com/find?q="); sb.append(URLEncoder.encode(title, StandardCharsets.UTF_8)); if (year != null) { @@ -37,21 +36,24 @@ public Set getImdbIdOnImdb(String title, Integer year) throws I Elements searchResults = manager.getPageContentBuilder() .url(url) .getAsJsoupDocument() - .select("#main .findList .findResult .result_text"); - return getImdbIdCommon(searchResults, title, year, e -> e.selectFirst("a").text() + " " + e.text(), - e -> e.selectFirst("a").attr("href")); + .selectAllByCss("#main .findList .findResult .result_text"); + return getImdbIdCommon(searchResults, + e -> e.selectFirstByTag("a").getText() + " " + e.getText(), + e -> e.selectFirst("a").getAttr("href")); } catch (Exception e) { throw new ImdbSearchIdException("Error getImdbIdOnImdb", url, e); } - }).getCollection(); + }) + .getCollection(); } public Set getImdbIdOnYahoo(String title, Integer year) throws ImdbSearchIdException { return manager.valueBuilder() .memoryCache() - .key("%s-imdbid-yahoo-%s-%s".formatted("IMDB", title, year)) + .key("IMDB-imdbid-yahoo-$title-$year") .collectionSupplier(ProviderSerieId.class, () -> { - StringBuilder sb = new StringBuilder("http://search.yahoo.com/search;_ylt=A1f4cfvx9C1I1qQAACVjAQx.?p="); + StringBuilder sb = + new StringBuilder("http://search.yahoo.com/search;_ylt=A1f4cfvx9C1I1qQAACVjAQx.?p="); sb.append(URLEncoder.encode(title, StandardCharsets.UTF_8)); if (year != null) { sb.append("+%28").append(year).append("%29"); @@ -64,22 +66,25 @@ public Set getImdbIdOnYahoo(String title, Integer year) throws Elements searchResults = manager.getPageContentBuilder() .url(url) .getAsJsoupDocument() - .select("a[href~='https%3a%2f%2fwww.imdb.com%2ftitle%2ftt']"); - Function toStringMapper = - e -> Optional.ofNullable(e.selectFirst("h3")).map(e2 -> e2.text().replace(" - IMDb", "")).orElse(null); - Function toHrefMapper = e -> URLDecoder.decode(e.attr("href"), StandardCharsets.UTF_8); - return getImdbIdCommon(searchResults, title, year, toStringMapper, toHrefMapper); + .selectAllByCss("a[href~='https%3a%2f%2fwww.imdb.com%2ftitle%2ftt']"); + Function toStringMapper = e -> Optional.ofNullable(e.selectFirst("h3")) + .map(e2 -> e2.text().replace(" - IMDb", "")) + .orElse(null); + Function toHrefMapper = + e -> URLDecoder.decode(e.attr("href"), StandardCharsets.UTF_8); + return getImdbIdCommon(searchResults, toStringMapper, toHrefMapper); } catch (Exception e) { throw new ImdbSearchIdException("Error getImdbIdOnYahoo", url, e); } - }).getCollection(); + }) + .getCollection(); } public Set getImdbIdOnGoogle(String title, Integer year) throws ImdbSearchIdException { return manager.valueBuilder() .memoryCache() - .key("%s-imdbid-google-%s-%s".formatted("IMDB", title, year)) + .key("IMDB-imdbid-google-$title-$year") .collectionSupplier(ProviderSerieId.class, () -> { StringBuilder sb = new StringBuilder("http://www.google.com/search?q="); sb.append(URLEncoder.encode(title, StandardCharsets.UTF_8)); @@ -92,27 +97,31 @@ public Set getImdbIdOnGoogle(String title, Integer year) throws Elements searchResults = manager.getPageContentBuilder() .url(url) .getAsJsoupDocument() - .select("a[href*='https://www.imdb.com/title/tt']"); + .selectAllByCss("a[href*='https://www.imdb.com/title/tt']"); Function toStringMapper = - e -> Optional.ofNullable(e.selectFirst("span")).map(e2 -> e2.text().replace(" - IMDb", "")).orElse(null); - Function toHrefMapper = e -> e.attr("href"); - return getImdbIdCommon(searchResults, title, year, toStringMapper, toHrefMapper); + e -> e.selectFirstByTag("span").getText().replace(" - IMDb", ""); + Function toHrefMapper = e -> e.getAttr("href"); + return getImdbIdCommon(searchResults, toStringMapper, toHrefMapper); } catch (Exception e) { throw new ImdbSearchIdException("Error getImdbIdOnGoogle", url, e); } - }).getCollection(); + }) + .getCollection(); } - private Set getImdbIdCommon(Elements searchResults, String title, int year, Function toStringMapper, + private Set getImdbIdCommon(Elements searchResults, Function toStringMapper, Function toHrefMapper) { - return searchResults.stream().map(element -> { - String name = toStringMapper.apply(element); - if (name == null) { - return null; - } - String href = toHrefMapper.apply(element); - Matcher matcher = IMDB_URL_ID_PATTERN.matcher(href); - return matcher.find() ? new ProviderSerieId(name, matcher.group().replace("/title/tt", "")) : null; - }).filter(Objects::nonNull).collect(Collectors.toSet()); + return searchResults.stream().collect(Utils.setCollector( + (set, element) -> { + String name = toStringMapper.apply(element); + if (StringUtils.isBlank(name)) { + return; + } + String href = toHrefMapper.apply(element); + Matcher matcher = IMDB_URL_ID_PATTERN.matcher(href); + if (matcher.find()) { + set.add(new ProviderSerieId(name, matcher.group().replace("/title/tt", ""))); + } + })); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbException.java index 1b80bef5..40fbd8e7 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbException.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbException.java @@ -8,7 +8,7 @@ public class ImdbException extends Exception { private static final long serialVersionUID = 8887410537703318009L; public ImdbException(String s, String url, Exception e) { - super(s + " :" + url, e); + super("$s: $url", e); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbSearchIdException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbSearchIdException.java index a33c5d3d..584f54d4 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbSearchIdException.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/exception/ImdbSearchIdException.java @@ -8,7 +8,7 @@ public class ImdbSearchIdException extends Exception { private static final long serialVersionUID = 8887410537703318009L; public ImdbSearchIdException(String s, String url, Exception e) { - super(s + " :" + url, e); + super("$s: $url", e); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/model/ImdbDetails.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/model/ImdbDetails.java index 1ee65a86..4ec6ba9b 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/model/ImdbDetails.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/imdb/model/ImdbDetails.java @@ -2,12 +2,20 @@ import java.io.Serializable; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.ReleaseDBIntf; -public record ImdbDetails(String title, int year) implements ReleaseDBIntf, Serializable { +@AllArgsConstructor +public class ImdbDetails implements ReleaseDBIntf, Serializable { + + @val String title; + @val @override int year; @Override public String getName() { return title; } } + diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbAdapter.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbAdapter.java index b2c3bd06..0ba0b930 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbAdapter.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbAdapter.java @@ -2,6 +2,7 @@ import java.util.Optional; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.omdb.model.OmdbDetails; @@ -11,16 +12,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.AccessLevel; -import lombok.Getter; - -@Getter(value = AccessLevel.PROTECTED) public class OmdbAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(OmdbAdapter.class); private static OmdbAdapter instance; private final Manager manager; private final LazySupplier omdpApi; + @val String providerName = "OMDB"; private OmdbAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { this.manager = manager; @@ -28,15 +26,11 @@ private OmdbAdapter(Manager manager, UserInteractionHandler userInteractionHandl try { return new OmdbApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } - public String getProviderName() { - return "OMDB"; - } - private OmdbApi getApi() { return omdpApi.get(); } @@ -45,17 +39,17 @@ public Optional getMovieDetails(int imdbId) { try { return manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%S-movieDetails-%s".formatted(getProviderName(), imdbId)) + .key("$providerName-movieDetails-$imdbId") .optionalSupplier(() -> getApi().getMovieDetails(imdbId)) .storeTempNullValue() .getOptional(); } catch (Exception e) { - LOGGER.error("API %s getMovieDetails for id [%s] (%s)".formatted(getProviderName(), imdbId, e.getMessage()), e); + LOGGER.error("API $providerName getMovieDetails for id [$imdbId] (${e.getMessage()})", e); return Optional.empty(); } } - public synchronized static OmdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { + public static synchronized OmdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { if (instance == null) { instance = new OmdbAdapter(manager, userInteractionHandler); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbApi.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbApi.java index f44ad5ce..9c20c8a2 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbApi.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbApi.java @@ -2,40 +2,38 @@ import java.util.Optional; -import org.apache.commons.lang3.StringUtils; +import lombok.RequiredArgsConstructor; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.omdb.exception.OmdbException; import org.lodder.subtools.sublibrary.data.omdb.model.OmdbDetails; -import org.w3c.dom.Element; - -import lombok.RequiredArgsConstructor; +import org.w3c.dom.Node; @RequiredArgsConstructor class OmdbApi { + private static final String DOMAIN = "http://www.omdbapi.com"; private final Manager manager; public Optional getMovieDetails(int imdbId) throws OmdbException { return manager.valueBuilder() .memoryCache() - .key("%s-moviedetails-%s".formatted("OMDB", imdbId)) + .key("OMDB-moviedetails-$imdbId") .optionalSupplier(() -> { - final String url = "http://www.omdbapi.com/?i=tt" + StringUtils.leftPad(String.valueOf(imdbId), 7, "0") + "&plot=short&r=xml"; + final String url = "$DOMAIN/?i=tt$%07d&plot=short&r=xml".formatted(imdbId); try { return manager.getPageContentBuilder() .url(url) .getAsDocument() - .map(doc -> doc.getElementsByTagName("movie")) - .filter(nodeList -> nodeList.getLength() > 0) - .map(nodeList -> parseOMDBDetails((Element) nodeList.item(0))); + .selectAllByTag("movie").stream() + .map(this::parseOMDBDetails).findFirst(); } catch (Exception e) { - throw new OmdbException("Error OMDBAPI", url, e); + throw new OmdbException("Error OMDB API", url, e); } }).getOptional(); } - private OmdbDetails parseOMDBDetails(Element item) { - return new OmdbDetails(item.getAttribute("title"), Integer.parseInt(item.getAttribute("year"))); + private OmdbDetails parseOMDBDetails(Node node) { + return new OmdbDetails(node.getAttribute("title"), Integer.parseInt(node.getAttribute("year"))); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/exception/OmdbException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/exception/OmdbException.java index 780c20d9..8195ab96 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/exception/OmdbException.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/exception/OmdbException.java @@ -8,7 +8,7 @@ public class OmdbException extends Exception { private static final long serialVersionUID = 8887410537703318009L; public OmdbException(String s, String url, Exception e) { - super(s + " :" + url, e); + super("$s: $url", e); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/model/OmdbDetails.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/model/OmdbDetails.java index 90d97fcd..e5f6a7f9 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/model/OmdbDetails.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/model/OmdbDetails.java @@ -3,26 +3,21 @@ import java.io.Serial; import java.io.Serializable; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.ReleaseDBIntf; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor +@AllArgsConstructor public class OmdbDetails implements ReleaseDBIntf, Serializable { @Serial private static final long serialVersionUID = 7701770682134890544L; - private final String title; - private final int year; + + @val String title; + @val @override int year; @Override public String getName() { return title; } - - @Override - public int year() { - return year; - } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbAdapter.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbAdapter.java index dd717180..6130adac 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbAdapter.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbAdapter.java @@ -1,5 +1,6 @@ package org.lodder.subtools.sublibrary.data.tvdb; +import javax.swing.*; import java.io.Serializable; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -9,8 +10,6 @@ import java.util.OptionalInt; import java.util.concurrent.TimeUnit; -import javax.swing.JOptionPane; - import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; @@ -23,17 +22,10 @@ import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -import org.lodder.subtools.sublibrary.util.OptionalExtension; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@Getter(value = AccessLevel.PROTECTED) -@ExtensionMethod({ OptionalExtension.class }) public class TheTvdbAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(TheTvdbAdapter.class); @@ -41,6 +33,7 @@ public class TheTvdbAdapter { private final Manager manager; private final UserInteractionHandler userInteractionHandler; private final LazySupplier jtvapi; + private final String providerName = "TVDB"; private TheTvdbAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { this.manager = manager; @@ -49,15 +42,11 @@ private TheTvdbAdapter(Manager manager, UserInteractionHandler userInteractionHa try { return new TheTvdbApi(manager, "A1720D2DDFDCE82D"); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(providerName, e); } }); } - public String getProviderName() { - return "TVDB"; - } - private TheTvdbApi getApi() { return jtvapi.get(); } @@ -66,7 +55,7 @@ public Optional getSerie(String serieName) { String encodedSerieName = URLEncoder.encode(serieName.toLowerCase().replace(" ", "-"), StandardCharsets.UTF_8); ValueBuilderIsPresentIntf valueBuilder = manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%s-tvdbSerie-%s".formatted(getProviderName(), encodedSerieName)); + .key("$providerName-tvdbSerie-$encodedSerieName"); if (valueBuilder.isPresent() && (!valueBuilder.isTemporaryObject() || !valueBuilder.isExpiredTemporary())) { return valueBuilder.returnType(TheTvdbSerie.class).getOptional(); } @@ -80,20 +69,24 @@ public Optional getSerie(String serieName) { } if (serieIds.isEmpty()) { tvdbSerie = Optional.empty(); - } else if (!userInteractionHandler.getSettings().isOptionsConfirmProviderMapping() && serieIds.size() == 1) { - tvdbSerie = Optional.of(serieIds.get(0)); + } else if (!userInteractionHandler.settings.optionsConfirmProviderMapping && serieIds.size() == 1) { + tvdbSerie = Optional.of(serieIds.first); } else { String formattedSerieName = serieName.replaceAll("[^A-Za-z]", ""); Comparator comparator = Comparator - .comparing((TheTvdbSerie s) -> formattedSerieName.equalsIgnoreCase(s.getSerieName().replaceAll("[^A-Za-z]", "")), + .comparing((TheTvdbSerie s) -> formattedSerieName.equalsIgnoreCase( + s.serieName.replaceAll("[^A-Za-z]", "")), Comparator.reverseOrder()) .thenComparing(TheTvdbSerie::getFirstAired, Comparator.reverseOrder()); try { tvdbSerie = userInteractionHandler .selectFromList(serieIds.stream().sorted(comparator).toList(), - Messages.getString("Prompter.SelectTvdbMatchForSerie").formatted(serieName), - getProviderName(), s -> "%s (%s)".formatted(s.getSerieName(), s.getFirstAired())) - .orElseMap(() -> askUserToEnterTvdbId(serieName).mapToOptionalObj(id -> getApi().getSerie(id, null))); + Messages.getText("Prompter.SelectTvdbMatchForSerie").formatted(serieName), + providerName, s -> "${s.serieName} (${s.firstAired})"); + if (tvdbSerie.isEmpty()) { + tvdbSerie = askUserToEnterTvdbId(serieName) + .mapToObj(tvdbId -> api.getSerie(tvdbId, null).orElse(null)); + } } catch (TheTvdbException e) { tvdbSerie = Optional.empty(); } @@ -101,15 +94,15 @@ public Optional getSerie(String serieName) { if (tvdbSerie.isEmpty()) { valueBuilder.optionalValue(tvdbSerie) .storeTempNullValue() - .timeToLive(OptionalExtension.map(valueBuilder.getTemporaryTimeToLive(), v -> v * 2) + .timeToLive(valueBuilder.getTemporaryTimeToLive().map(v -> v * 2) .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) .storeAsTempValue(); } else { valueBuilder.optionalValue(tvdbSerie).store(); manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%s-serieId-%s".formatted(getProviderName(), encodedSerieName)) - .optionalValue(tvdbSerie.mapToObj(tvdbS -> new SerieMapping(serieName, tvdbS.getId(), tvdbS.getSerieName()))) + .key("$providerName-serieId-$encodedSerieName") + .optionalValue(tvdbSerie.map(tvdbS -> new SerieMapping(serieName, tvdbS.id, tvdbS.serieName))) .storeTempNullValue() .store(); } @@ -119,20 +112,22 @@ public Optional getSerie(String serieName) { public Optional getEpisode(int tvdbId, int season, int episode) { return manager.valueBuilder() .cacheType(CacheType.DISK) - .key("%s-episode-%s-%s-%s".formatted(getProviderName(), tvdbId, season, episode)) + .key("%s-episode-%s-%s-%s".formatted(providerName, tvdbId, season, episode)) .optionalSupplier(() -> { try { return getApi().getEpisode(tvdbId, season, episode, Language.ENGLISH); } catch (TheTvdbException e) { - LOGGER.error("API %s getEpisode for serie id [%s] %s (%s)".formatted(getProviderName(), tvdbId, - TvRelease.formatSeasonEpisode(season, episode), e.getMessage()), e); + LOGGER.error( + "API $providerName getEpisode for serie id [$tvdbId] %s (${e.getMessage()})".formatted( + TvRelease.formatSeasonEpisode(season, episode)), e); return Optional.empty(); } }).storeTempNullValue().getOptional(); } - public synchronized static TheTvdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { + public static synchronized TheTvdbAdapter getInstance(Manager manager, + UserInteractionHandler userInteractionHandler) { if (instance == null) { instance = new TheTvdbAdapter(manager, userInteractionHandler); } @@ -140,15 +135,15 @@ public synchronized static TheTvdbAdapter getInstance(Manager manager, UserInter } private OptionalInt askUserToEnterTvdbId(String showName) { - LOGGER.error("Unknown serie name in tvdb: " + showName); - String tvdbidString = JOptionPane.showInputDialog(null, "Enter tvdb id for serie " + showName); + LOGGER.error("Unknown serie name in tvdb: $showName"); + String tvdbidString = JOptionPane.showInputDialog(null, "Enter tvdb id for serie $showName"); if (tvdbidString == null) { return OptionalInt.empty(); } try { return OptionalInt.of(Integer.parseInt(tvdbidString)); } catch (NumberFormatException e) { - LOGGER.error("Invalid tvdb id: " + tvdbidString); + LOGGER.error("Invalid tvdb id: $tvdbidString"); return askUserToEnterTvdbId(showName); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbApi.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbApi.java index 5b5898ff..c255285f 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbApi.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbApi.java @@ -6,29 +6,21 @@ import java.util.List; import java.util.Optional; -import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.data.tvdb.exception.TheTvdbException; -import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbEpisode; -import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbSerie; -import org.lodder.subtools.sublibrary.util.OptionalExtension; - import com.uwetrottmann.thetvdb.TheTvdb; import com.uwetrottmann.thetvdb.entities.Episode; import com.uwetrottmann.thetvdb.entities.EpisodesResponse; import com.uwetrottmann.thetvdb.entities.Series; import com.uwetrottmann.thetvdb.entities.SeriesResponse; import com.uwetrottmann.thetvdb.entities.SeriesResultsResponse; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; +import org.lodder.subtools.sublibrary.Language; +import org.lodder.subtools.sublibrary.Manager; +import org.lodder.subtools.sublibrary.data.tvdb.exception.TheTvdbException; +import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbEpisode; +import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbSerie; import retrofit2.Response; -@ExtensionMethod({ OptionalExtension.class }) public class TheTvdbApi { - @Getter(value = AccessLevel.PRIVATE) private final Manager manager; private final TheTvdb theTvdb; @@ -40,15 +32,20 @@ public TheTvdbApi(Manager manager, String apikey) { public List getSeries(String serieName, Language language) throws TheTvdbException { return manager.valueBuilder() .memoryCache() - .key("%s-series-%s-%s".formatted("TVDB", serieName, language)) + .key("TVDB-series-$serieName-$language") .collectionSupplier(TheTvdbSerie.class, () -> { - String encodedSerieName = URLEncoder.encode(serieName.toLowerCase().replace(" ", "-"), StandardCharsets.UTF_8); + String encodedSerieName = + URLEncoder.encode(serieName.toLowerCase().replace(" ", "-"), StandardCharsets.UTF_8); try { Response response = - theTvdb.search().series(encodedSerieName, null, null, null, language == null ? null : language.getLangCode()) + theTvdb.search() + .series(encodedSerieName, null, null, null, + language == null ? null : language.langCode) .execute(); if (response.isSuccessful()) { - return response.body().data.stream().map(series -> seriesToTVDBSerie(series, language)).toList(); + return response.body().data.stream() + .map(series -> seriesToTVDBSerie(series, language)) + .toList(); } return List.of(); } catch (IOException e) { @@ -61,7 +58,7 @@ public Optional getSerie(int tvdbId, Language language) throws The try { Response response = theTvdb.series() - .series(tvdbId, language == null ? null : language.getLangCode()) + .series(tvdbId, language == null ? null : language.langCode) .execute(); if (response.isSuccessful()) { return Optional.of(seriesToTVDBSerie(response.body().data, language)); @@ -72,20 +69,25 @@ public Optional getSerie(int tvdbId, Language language) throws The } } - public Optional getEpisode(int tvdbId, int season, int episode, Language language) throws TheTvdbException { + public Optional getEpisode(int tvdbId, int season, int episode, Language language) + throws TheTvdbException { return manager.valueBuilder() .memoryCache() - .key("%s-episode-%s-%s-%s-%s".formatted("TVDB", tvdbId, season, episode, language)) + .key("TVDB-episode-$tvdbId-$season-$episode-$language") .optionalSupplier(() -> { try { Response response = - theTvdb.series().episodesQuery(tvdbId, null, season, episode, null, null, null, null, null, - language == null ? null : language.getLangCode()).execute(); + theTvdb.series() + .episodesQuery(tvdbId, null, season, episode, null, null, null, null, null, + language == null ? null : language.langCode) + .execute(); if (response.isSuccessful()) { if (response.body().data == null) { return Optional.empty(); } - return response.body().data.stream().map(serie -> episodeToTVDBEpisode(serie, language)).findFirst(); + return response.body().data.stream() + .map(serie -> episodeToTVDBEpisode(serie, language)) + .findFirst(); } throw new TheTvdbException(response.errorBody().string()); } catch (IOException e) { @@ -95,44 +97,44 @@ public Optional getEpisode(int tvdbId, int season, int episode, } private TheTvdbSerie seriesToTVDBSerie(Series serie, Language lang) { - TheTvdbSerie TheTVDBSerie = new TheTvdbSerie(); - - TheTVDBSerie.setId(serie.id); - TheTVDBSerie.setAirsDayOfWeek(serie.airsDayOfWeek); - TheTVDBSerie.setAirsTime(serie.airsTime); - TheTVDBSerie.setContentRating(serie.rating); - TheTVDBSerie.setFirstAired(serie.firstAired); - TheTVDBSerie.setGenres(serie.genre); - TheTVDBSerie.setImdbId(serie.imdbId); - TheTVDBSerie.setLanguage(lang); - TheTVDBSerie.setNetwork(serie.network); - // TheTVDBSerie.setOverview(serie.overview); - TheTVDBSerie.setRating(serie.rating); - TheTVDBSerie.setRuntime(serie.runtime); - // TheTVDBSerie.setSerieId(toString(serie.id)); - TheTVDBSerie.setSerieName(serie.seriesName); - TheTVDBSerie.setStatus(serie.status); - - return TheTVDBSerie; + TheTvdbSerie theTVDBSerie = new TheTvdbSerie(); + + theTVDBSerie.id = serie.id; + theTVDBSerie.airsDayOfWeek = serie.airsDayOfWeek; + theTVDBSerie.airsTime = serie.airsTime; + theTVDBSerie.contentRating = serie.rating; + theTVDBSerie.firstAired = serie.firstAired; + theTVDBSerie.genres = serie.genre; + theTVDBSerie.imdbId = serie.imdbId; + theTVDBSerie.language = lang; + theTVDBSerie.network = serie.network; + // theTVDBSerie.overview = serie.overview; + theTVDBSerie.rating = serie.rating; + theTVDBSerie.runtime = serie.runtime; + // theTVDBSerie.serieId = toString(serie.id); + theTVDBSerie.serieName = serie.seriesName; + theTVDBSerie.status = serie.status; + + return theTVDBSerie; } private TheTvdbEpisode episodeToTVDBEpisode(Episode episode, Language lang) { TheTvdbEpisode tvdbEpisode = new TheTvdbEpisode(); - tvdbEpisode.setId(toString(episode.id)); - tvdbEpisode.setDvdEpisodeNumber(toString(episode.dvdEpisodeNumber)); - tvdbEpisode.setDvdSeason(toString(episode.dvdSeason)); - tvdbEpisode.setEpisodeName(episode.episodeName); - tvdbEpisode.setEpisodeNumber(episode.airedEpisodeNumber); - tvdbEpisode.setFirstAired(episode.firstAired); - tvdbEpisode.setLanguage(lang); + tvdbEpisode.id = toString(episode.id); + tvdbEpisode.dvdEpisodeNumber = toString(episode.dvdEpisodeNumber); + tvdbEpisode.dvdSeason = toString(episode.dvdSeason); + tvdbEpisode.episodeName = episode.episodeName; + tvdbEpisode.episodeNumber = episode.airedEpisodeNumber; + tvdbEpisode.firstAired = episode.firstAired; + tvdbEpisode.language = lang; // tvdbEpisode.setOverview(episode.language.overview); - tvdbEpisode.setSeasonNumber(episode.airedSeason); - tvdbEpisode.setAbsoluteNumber(toString(episode.absoluteNumber)); - tvdbEpisode.setLastUpdated(toString(episode.lastUpdated)); - tvdbEpisode.setSeasonId(toString(episode.airedSeasonID)); - tvdbEpisode.setAirsAfterSeason(0); - tvdbEpisode.setAirsBeforeEpisode(0); + tvdbEpisode.seasonNumber = episode.airedSeason; + tvdbEpisode.absoluteNumber = toString(episode.absoluteNumber); + tvdbEpisode.lastUpdated = toString(episode.lastUpdated); + tvdbEpisode.seasonId = toString(episode.airedSeasonID); + tvdbEpisode.airsAfterSeason = 0; + tvdbEpisode.airsBeforeEpisode = 0; return tvdbEpisode; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbMirrors.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbMirrors.java index c71c7358..02341355 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbMirrors.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/TheTvdbMirrors.java @@ -1,23 +1,21 @@ package org.lodder.subtools.sublibrary.data.tvdb; import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Random; -import lombok.experimental.ExtensionMethod; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.ManagerException; import org.lodder.subtools.sublibrary.xml.XMLHelper; -import org.lodder.subtools.sublibrary.xml.XmlExtension; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * @author lodder - * Source + * Source */ -@ExtensionMethod({ XmlExtension.class }) public class TheTvdbMirrors { public static final String TYPE_XML = "XML"; @@ -34,34 +32,31 @@ public class TheTvdbMirrors { private final List bannerList = new ArrayList<>(); private final List zipList = new ArrayList<>(); - public TheTvdbMirrors(String apikey, Manager manager) throws ManagerException, ParserConfigurationException { + public TheTvdbMirrors(String apikey, Manager manager) throws ManagerException, ParserConfigurationException, + IOException { synchronized (this) { manager.getPageContentBuilder() .url("http://www.thetvdb.com/api/" + apikey + "/mirrors.xml") - .getAsDocument().ifPresent(doc -> { - doc.getElementsByTagName("Mirror").stream() - .filter(nMirror -> nMirror.getNodeType() == Node.ELEMENT_NODE) - .map(Element.class::cast) - .forEach(eMirror -> { - String url = XMLHelper.getStringTagValue("mirrorpath", eMirror); - int typeMask = XMLHelper.getIntTagValue("typemask", eMirror); - addMirror(typeMask, url); - }); + .getAsDocument() + .selectAllByTag("Mirror").stream() + .filter(nMirror -> nMirror.getNodeType() == Node.ELEMENT_NODE) + .map(Element.class::cast) + .forEach(eMirror -> { + String url = XMLHelper.getStringTagValue("mirrorpath", eMirror); + int typeMask = XMLHelper.getIntTagValue("typemask", eMirror); + addMirror(typeMask, url); }); } } public String getMirror(String type) { - if (TYPE_XML.equals(type) && !xmlList.isEmpty()) { - return xmlList.get(RNDM.nextInt(xmlList.size())); - } else if (TYPE_BANNER.equals(type) && !bannerList.isEmpty()) { - return bannerList.get(RNDM.nextInt(bannerList.size())); - } else if (TYPE_ZIP.equals(type) && !zipList.isEmpty()) { - return zipList.get(RNDM.nextInt(zipList.size())); - } else { - return null; - } + return switch (type) { + case TYPE_XML -> xmlList.isEmpty() ? null : xmlList.get(RNDM.nextInt(xmlList.size())); + case TYPE_BANNER -> bannerList.isEmpty() ? null : bannerList.get(RNDM.nextInt(bannerList.size())); + case TYPE_ZIP -> zipList.isEmpty() ? null : zipList.get(RNDM.nextInt(zipList.size())); + default -> null; + }; } private void addMirror(int typeMask, String url) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbEpisode.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbEpisode.java index 9deeaa23..bf9a6cd9 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbEpisode.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbEpisode.java @@ -5,44 +5,40 @@ import java.util.ArrayList; import java.util.List; -import org.lodder.subtools.sublibrary.Language; - -import lombok.Getter; -import lombok.Setter; import lombok.ToString; +import manifold.ext.props.rt.api.var; +import org.lodder.subtools.sublibrary.Language; @ToString -@Getter -@Setter public class TheTvdbEpisode implements Serializable { @Serial private static final long serialVersionUID = 913790243120597542L; - private String id; - private String combinedEpisodeNumber; - private String combinedSeason; - private String dvdChapter; - private String dvdDiscId; - private String dvdEpisodeNumber; - private String dvdSeason; - private List directors = new ArrayList<>(); - private String epImgFlag; - private String episodeName; - private int episodeNumber; - private String firstAired; - private List guestStars = new ArrayList<>(); - private String imdbId; - private Language language; - // private String overview; - private String productionCode; - private String rating; - private int seasonNumber; - private List writers = new ArrayList<>(); - private String absoluteNumber; - private int airsAfterSeason; - private int airsBeforeSeason; - private int airsBeforeEpisode; - private String filename; - private String lastUpdated; - private String seriesId; - private String seasonId; + @var String id; + @var String combinedEpisodeNumber; + @var String combinedSeason; + @var String dvdChapter; + @var String dvdDiscId; + @var String dvdEpisodeNumber; + @var String dvdSeason; + @var List directors = new ArrayList<>(); + @var String epImgFlag; + @var String episodeName; + @var int episodeNumber; + @var String firstAired; + @var List guestStars = new ArrayList<>(); + @var String imdbId; + @var Language language; + // @var String overview; + @var String productionCode; + @var String rating; + @var int seasonNumber; + @var List writers = new ArrayList<>(); + @var String absoluteNumber; + @var int airsAfterSeason; + @var int airsBeforeSeason; + @var int airsBeforeEpisode; + @var String filename; + @var String lastUpdated; + @var String seriesId; + @var String seasonId; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbSerie.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbSerie.java index bd7c6f2e..cc7dc468 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbSerie.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/tvdb/model/TheTvdbSerie.java @@ -5,38 +5,33 @@ import java.util.ArrayList; import java.util.List; -import org.lodder.subtools.sublibrary.Language; - -import lombok.Getter; -import lombok.Setter; import lombok.ToString; +import manifold.ext.props.rt.api.var; +import org.lodder.subtools.sublibrary.Language; @ToString -@Getter -@Setter public class TheTvdbSerie implements Serializable { @Serial private static final long serialVersionUID = -4036836377513152443L; - private int id; - // private String serieId; - private Language language; - private String serieName; - private String banner; - // private String overview; - private String firstAired; - private String imdbId; - private String zap2ItId; - private List actors = new ArrayList<>(); - private String airsDayOfWeek; - private String airsTime; - private String contentRating; - private List genres = new ArrayList<>(); - private String network; - private String rating; - private String runtime; - private String status; - private String fanArt; - private String lastUpdated; - private String poster; - + @var int id; + //@var String serieId; + @var Language language; + @var String serieName; + @var String banner; + //@var String overview; + @var String firstAired; + @var String imdbId; + @var String zap2ItId; + @var List actors = new ArrayList<>(); + @var String airsDayOfWeek; + @var String airsTime; + @var String contentRating; + @var List genres = new ArrayList<>(); + @var String network; + @var String rating; + @var String runtime; + @var String status; + @var String fanArt; + @var String lastUpdated; + @var String poster; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/ControlFactoryException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/ControlFactoryException.java index bec0fe60..bbb764c8 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/ControlFactoryException.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/ControlFactoryException.java @@ -4,11 +4,10 @@ public class ControlFactoryException extends Exception { - public ControlFactoryException(String string) { - super(string); - } - @Serial private static final long serialVersionUID = -7387961966699689531L; + public ControlFactoryException(String string) { + super(string); + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/SubtitlesProviderInitException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/SubtitlesProviderInitException.java index f056298a..d0bfb28f 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/SubtitlesProviderInitException.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/SubtitlesProviderInitException.java @@ -2,14 +2,14 @@ import java.io.Serial; -import lombok.Getter; +import manifold.ext.props.rt.api.val; public class SubtitlesProviderInitException extends RuntimeException { @Serial private static final long serialVersionUID = -2959483164333075297L; - @Getter - private final String providerName; + + @val String providerName; public SubtitlesProviderInitException(String providerName, Throwable e) { super(e); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/WebpageException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/WebpageException.java new file mode 100644 index 00000000..a7b9adbd --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/WebpageException.java @@ -0,0 +1,12 @@ +package org.lodder.subtools.sublibrary.exception; + +import java.io.IOException; +import java.io.Serial; + +import lombok.experimental.StandardException; + +@StandardException +public class WebpageException extends IOException { + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/InputPane.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/InputPane.java index 0c8cc37d..a1bdee72 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/InputPane.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/InputPane.java @@ -1,27 +1,21 @@ package org.lodder.subtools.sublibrary.gui; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Serial; -import java.util.Optional; -import java.util.function.Predicate; - -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JTextField; - -import org.lodder.subtools.multisubdownloader.Messages; - +import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Serial; +import java.util.Optional; +import java.util.function.Predicate; import lombok.Setter; import lombok.experimental.Accessors; +import org.lodder.subtools.multisubdownloader.Messages; public class InputPane { @@ -64,12 +58,13 @@ public interface InputPaneBuilderPromptIntf { @Setter @Accessors(fluent = true) public static class InputPaneBuilder extends JDialog implements ActionListener, PropertyChangeListener, - InputPaneBuilderPromptIntf, InputPaneBuilderErrorMessageIntf, InputPaneBuilderValidatorIntf, InputPaneBuilderMessageIntf, + InputPaneBuilderPromptIntf, InputPaneBuilderErrorMessageIntf, InputPaneBuilderValidatorIntf, + InputPaneBuilderMessageIntf, InputPaneBuilderTitleIntf { @Serial private static final long serialVersionUID = 1L; - private final static String OK = Messages.getString("App.OK"); - private final static String CANCEL = Messages.getString("App.Cancel"); + private static final String OK = Messages.getText("App.OK"); + private static final String CANCEL = Messages.getText("App.Cancel"); private String title; private String message; private String errorMessage; @@ -131,7 +126,6 @@ public void componentShown(ComponentEvent ce) { pack(); setVisible(true); - optionPane.getValue(); return typedText; } @@ -142,7 +136,7 @@ public void propertyChange(PropertyChangeEvent e) { if (isVisible() && e.getSource() == optionPane && (JOptionPane.VALUE_PROPERTY.equals(prop) - || JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) { + || JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) { Object value = optionPane.getValue(); if (value == JOptionPane.UNINITIALIZED_VALUE) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/OptionsPane.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/OptionsPane.java index 446ec116..aa0854d4 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/OptionsPane.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/OptionsPane.java @@ -1,15 +1,12 @@ package org.lodder.subtools.sublibrary.gui; -import java.util.Arrays; +import javax.swing.*; +import java.awt.*; import java.util.Collection; import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; -import javax.swing.JOptionPane; - -import java.awt.Component; - import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; @@ -81,7 +78,8 @@ public interface OptionsPaneBuilderPromptIntf { @Setter @Accessors(fluent = true) @RequiredArgsConstructor - private static class OptionsPaneBuilder implements OptionsPaneBuilderPromptIntf, OptionsPaneBuilderMessageTypeIntf, + private static class OptionsPaneBuilder + implements OptionsPaneBuilderPromptIntf, OptionsPaneBuilderMessageTypeIntf, OptionsPaneBuilderMessageIntf, OptionsPaneBuilderTitleIntf, OptionsPaneBuilderToStringMapperIntf { private final T[] optionsArray; private final Collection optionsList; @@ -110,13 +108,17 @@ public Optional prompt() { } else { options = optionsArray; } - return Optional.ofNullable((T) JOptionPane.showInputDialog(parent, message, title, messageType, null, options, "0")); + return Optional.ofNullable( + (T) JOptionPane.showInputDialog(parent, message, title, messageType, null, options, "0")); } else { - Stream optionsStream = optionsList != null ? optionsList.stream() : Arrays.stream(optionsArray); + Stream optionsStream = optionsList != null ? optionsList.stream() : optionsArray.stream(); ElementWrapper[] options = - optionsStream.map(option -> new ElementWrapper<>(option, toStringMapper)).toArray(ElementWrapper[]::new); + optionsStream.map(option -> new ElementWrapper<>(option, toStringMapper)) + .toArray(ElementWrapper[]::new); return Optional - .ofNullable((ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType, null, options, "0")) + .ofNullable( + (ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType, + null, options, "0")) .map(ElementWrapper::element); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/MovieRelease.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/MovieRelease.java index 5444682b..34da9711 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/MovieRelease.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/MovieRelease.java @@ -3,18 +3,14 @@ import java.nio.file.Path; import java.util.OptionalInt; -import org.apache.commons.lang3.StringUtils; - -import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import manifold.ext.props.rt.api.var; -@Getter -@Setter -public class MovieRelease extends Release { +public final class MovieRelease extends Release { - private String name; - private Integer year; + @var String name; + @var Integer year; private int imdbId; private int tvdbId; @@ -64,24 +60,32 @@ private MovieRelease(Path file, String description, String releaseGroup, String } public String getImdbIdAsString() { - return "tt" + StringUtils.leftPad(String.valueOf(imdbId), 7, "0"); + return "tt%07d".formatted(imdbId); } public OptionalInt getTvdbId() { return tvdbId == 0 ? OptionalInt.empty() : OptionalInt.of(tvdbId); } + public void setTvdbId(int tvdbId) { + this.tvdbId = tvdbId; + } + public OptionalInt getImdbId() { return imdbId == 0 ? OptionalInt.empty() : OptionalInt.of(imdbId); } + public void setImdbId(int imdbId) { + this.imdbId = imdbId; + } + @Override public String toString() { - return this.getClass().getSimpleName() + ": " + this.getName() + " " + this.getQuality() + " " + this.getReleaseGroup(); + return "${getClass().getSimpleName()}: $name ${quality} ${releaseGroup}"; } @Override public String getReleaseDescription() { - return getName(); + return name; } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Release.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Release.java index 1c5a6002..8cd9d43e 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Release.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Release.java @@ -6,61 +6,60 @@ import java.util.List; import java.util.Set; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; -import lombok.Getter; +public abstract sealed class Release permits MovieRelease, TvRelease { -@Getter -public abstract class Release extends Video { - - private final Set matchingSubs = new HashSet<>(); - private final Path path; - private final String quality; - private final String description; - private final String releaseGroup; + private final Set matchingSubsSet = new HashSet<>(); + @val VideoType videoType; + @val Path filePath; + @val String quality; + @val String description; + @val String releaseGroup; public void addMatchingSub(Subtitle sub) { - matchingSubs.add(sub); + matchingSubsSet.add(sub); } public List getMatchingSubs() { - return new ArrayList<>(matchingSubs); + return new ArrayList<>(matchingSubsSet); } public int getMatchingSubCount() { - return matchingSubs.size(); + return matchingSubsSet.size(); } - protected Release(VideoType videoFileType, Path path, String description, String releaseGroup, String quality) { - super(videoFileType); - this.path = path; + protected Release(VideoType videoType, Path filePath, String description, String releaseGroup, String quality) { + this.videoType = videoType; + this.filePath = filePath; this.description = description; this.releaseGroup = releaseGroup; this.quality = quality; } public String getFileName() { - return path != null ? path.getFileName().toString() : null; + return filePath != null ? filePath.getFileName().toString() : null; } public Path getPath() { - return path != null ? path.getParent() : null; + return filePath != null ? filePath.getParent() : null; } public String getExtension() { - return StringUtils.substringAfterLast(getFileName(), "."); + return StringUtils.substringAfterLast(fileName, "."); } public boolean hasExtension(String extension) { - return StringUtils.endsWith(getFileName(), "." + extension); + return StringUtils.endsWith(fileName, "." + extension); } @Override public String toString() { - return this.getClass().getSimpleName() + ": " + this.getFileName() + " " + this.getQuality(); + return "${getClass().getSimpleName()}: $fileName $quality"; } public String getReleaseDescription() { - return getFileName(); + return fileName; } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SeasonEpisode.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SeasonEpisode.java index d56481fd..f93db1cd 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SeasonEpisode.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SeasonEpisode.java @@ -6,17 +6,15 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import lombok.Getter; +import manifold.ext.props.rt.api.val; public class SeasonEpisode { private static final Pattern SEASON_EPISODE_PATTERN_1 = Pattern.compile("S(\\d{1,2})E(\\d{1,2})"); private static final Pattern SEASON_EPISODE_PATTERN_2 = Pattern.compile("[. ](\\d{1,2})x(\\d{1,2})"); private static final Pattern SEASON_EPISODES_PATTERN_1 = Pattern.compile("S(\\d{1,2})E(\\d{1,2})E(\\d{1,2})"); - @Getter - private final int season; - @Getter - private final List episodes; + @val int season; + @val List episodes; public SeasonEpisode(int season, int episode) { this.season = season; diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Subtitle.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Subtitle.java index 7717233a..c4099768 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Subtitle.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Subtitle.java @@ -2,39 +2,34 @@ import java.nio.file.Path; -import org.apache.commons.lang3.builder.EqualsExclude; -import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; - import com.pivovarit.function.ThrowingSupplier; - import lombok.AccessLevel; import lombok.EqualsAndHashCode; -import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; +import org.apache.commons.lang3.builder.EqualsExclude; +import org.lodder.subtools.sublibrary.Language; +import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; -@Setter -@Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @EqualsAndHashCode public class Subtitle { @EqualsExclude - private final ThrowingSupplier urlSupplier; - private final String url; - private final Path file; - private final SourceLocation sourceLocation; - - private String fileName; - private Language language; - private String releaseGroup; - private String uploader; - private SubtitleMatchType subtitleMatchType; - private SubtitleSource subtitleSource; - private boolean hearingImpaired; - - private String quality; - private int score; + @val ThrowingSupplier urlSupplier; + @val String url; + @val Path file; + @val SourceLocation sourceLocation; + + @var String fileName; + @var Language language; + @var String releaseGroup; + @var String uploader; + @var SubtitleMatchType subtitleMatchType; + @var SubtitleSource subtitleSource; + @var boolean hearingImpaired; + @var String quality; + @var int score; public enum SourceLocation { URL, URL_SUPPLIER, FILE @@ -75,7 +70,7 @@ public static Subtitle downloadSource(Path file) { @Override public String toString() { - return this.getClass().getSimpleName() + ": " + this.getFileName() + " " + this.getQuality(); + return "${getClass().getSimpleName()}: $fileName $quality"; } public Subtitle fileName(String fileName) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SubtitleSource.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SubtitleSource.java index af89b0fc..e59d405a 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SubtitleSource.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/SubtitleSource.java @@ -1,10 +1,9 @@ package org.lodder.subtools.sublibrary.model; import lombok.AllArgsConstructor; -import lombok.Getter; +import manifold.ext.props.rt.api.val; @AllArgsConstructor -@Getter public enum SubtitleSource { OPENSUBTITLES("OpenSubtitles"), PODNAPISI("Podnapisi"), @@ -13,5 +12,5 @@ public enum SubtitleSource { LOCAL("Local"), SUBSCENE("Subscene"); - private final String name; + @val String name; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/TvRelease.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/TvRelease.java index 3c0e75ba..e1eafb3e 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/TvRelease.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/TvRelease.java @@ -5,31 +5,29 @@ import java.util.List; import java.util.OptionalInt; -import org.apache.commons.lang3.StringUtils; -import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbEpisode; - -import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; +import org.apache.commons.lang3.StringUtils; +import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbEpisode; -@Getter -@Setter -public class TvRelease extends Release { +public final class TvRelease extends Release { // parsed from the filename - private final String name; - private String title; - private final int season; - private int tvdbId; - private final List episodeNumbers; + @val String name; + @val List episodeNumbers; + @val int season; + @var String title; + @var int tvdbId; // tvdb name - private String originalName; - private boolean special; + @var String originalName; + @val boolean special; // custom name which can be used to search subtitle providers - private String customName; + @val String customName; public String getNameWithSeasonEpisode() { - return formatName(name, season, episodeNumbers.isEmpty() ? -1 : episodeNumbers.get(0)); + return formatName(name, season, episodeNumbers.isEmpty() ? -1 : firstEpisodeNumber); } public static String formatName(String serieName, int season, int episode) { @@ -37,8 +35,7 @@ public static String formatName(String serieName, int season, int episode) { } public static String formatSeasonEpisode(int season, int episode) { - return "S%sE%s".formatted(StringUtils.leftPad(String.valueOf(season), 2, "0"), - StringUtils.leftPad(String.valueOf(episode), 2, "0")); + return "S%02dE%02d".formatted(season, episode); } public interface TvReleaseBuilderShowName { @@ -82,7 +79,8 @@ public static TvReleaseBuilderShowName builder() { @Setter @Accessors(chain = true, fluent = true) public static class TvReleaseBuilder - implements TvReleaseBuilderOther, TvReleaseBuilderEpisode, TvReleaseBuilderSeason, TvReleaseBuilderShowName { + implements TvReleaseBuilderOther, TvReleaseBuilderEpisode, TvReleaseBuilderSeason, + TvReleaseBuilderShowName { private String name; private String title; private int season; @@ -109,13 +107,14 @@ public TvReleaseBuilder episodes(List episodes) { @Override public TvRelease build() { - return new TvRelease(file, description, releaseGroup, quality, name, originalName, customName, title, season, episodes, special); + return new TvRelease(file, description, releaseGroup, quality, name, originalName, customName, title, + season, episodes, special); } } - private TvRelease(Path file, String description, String releaseGroup, String quality, String name, String originalName, String customName, - String title, int season, - List episodeNumbers, boolean special) { + private TvRelease(Path file, String description, String releaseGroup, String quality, String name, + String originalName, String customName, String title, int season, List episodeNumbers, + boolean special) { super(VideoType.EPISODE, file, description, releaseGroup, quality); this.name = name; this.title = title; @@ -127,21 +126,20 @@ private TvRelease(Path file, String description, String releaseGroup, String qua } public void updateTvdbEpisodeInfo(TheTvdbEpisode tvdbEpisode) { - this.title = tvdbEpisode.getEpisodeName(); // update to reflect correct episode title + this.title = tvdbEpisode.episodeName; // update to reflect correct episode title } - public OptionalInt getTvdbId() { + public OptionalInt getTvdbIdOptional() { return tvdbId == 0 ? OptionalInt.empty() : OptionalInt.of(tvdbId); } public int getFirstEpisodeNumber() { - return episodeNumbers.get(0); + return episodeNumbers.first; } @Override public String toString() { - return this.getClass().getSimpleName() + ": " + this.getName() + " s" + this.getSeason() + " e" - + this.getEpisodeNumbers().toString() + " " + this.getQuality() + " " + this.getReleaseGroup(); + return "${getClass().getSimpleName()}: $name s$season e$episodeNumbers $quality $releaseGroup"; } @Override @@ -150,6 +148,6 @@ public String getReleaseDescription() { } public String getDisplayName() { - return StringUtils.isNotBlank(getOriginalName()) ? getOriginalName() : getName(); + return StringUtils.defaultIfBlank(originalName, name); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Video.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Video.java deleted file mode 100755 index e9bc212c..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Video.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.lodder.subtools.sublibrary.model; - -import lombok.Getter; -import lombok.Setter; - -@Getter @Setter -public abstract class Video { - - private final VideoType videoType; - - public Video(VideoType videoType) { - this.videoType = videoType; - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/VideoSearchType.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/VideoSearchType.java index cf0c0b71..e5cdca6f 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/VideoSearchType.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/VideoSearchType.java @@ -1,14 +1,13 @@ package org.lodder.subtools.sublibrary.model; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; -@Getter -@RequiredArgsConstructor +@AllArgsConstructor public enum VideoSearchType { EPISODE("App.Episode"), MOVIE("App.Movie"), RELEASE("App.Release"); - private final String msgCode; + @val String msgCode; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/settings/model/SerieMapping.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/settings/model/SerieMapping.java index 6f6d7682..6cd82a52 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/settings/model/SerieMapping.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/settings/model/SerieMapping.java @@ -2,24 +2,24 @@ import java.io.Serial; import java.io.Serializable; -import java.util.function.Function; +import java.util.function.UnaryOperator; import io.gsonfire.annotations.PostDeserialize; -import lombok.Getter; import lombok.ToString; +import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; -@Getter @ToString public class SerieMapping implements Serializable { // implements SerieMappingIntf { @Serial private static final long serialVersionUID = 6551798252915028805L; - private static final Function NAME_FORMATTER = name -> name.replaceAll("[^A-Za-z]", ""); - private final String name; - private final String providerId; - private final String providerName; - private final int season; - private transient String formattedName; + private static final UnaryOperator NAME_FORMATTER = n -> n.replaceAll("[^A-Za-z]", ""); + @val String name; + @val String providerId; + @val String providerName; + @val int season; + @var transient String formattedName; public SerieMapping(String name, int providerId, String providerName) { this(name, providerId, providerName, 0); @@ -39,11 +39,7 @@ public SerieMapping(String name, String providerId, String providerName, int sea @PostDeserialize public void postDeserializeLogic() { - formattedName = getName().replaceAll("[^A-Za-z]", ""); - } - - public static Function getNameFormatter() { - return NAME_FORMATTER; + formattedName = name.replaceAll("[^A-Za-z]", ""); } public static String formatName(String name) { @@ -52,11 +48,11 @@ public static String formatName(String name) { public boolean matches(String serieName) { String serieNameFormatted = formatName(serieName); - return getFormattedName().contains(serieNameFormatted) - || (serieNameFormatted.contains(getFormattedName()) && getFormattedName().length() > 3); + return formattedName.contains(serieNameFormatted) + || (serieNameFormatted.contains(formattedName) && formattedName.length() > 3); } public boolean exactMatch(String serieName) { - return getFormattedName().equalsIgnoreCase(formatName(serieName)); + return formattedName.equalsIgnoreCase(formatName(serieName)); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandler.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandler.java index 1b874ac7..7c10bced 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandler.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandler.java @@ -5,17 +5,19 @@ import java.util.function.Function; import java.util.function.Predicate; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; public interface UserInteractionHandler { - UserInteractionSettingsIntf getSettings(); + @val UserInteractionSettingsIntf settings; boolean confirm(String message, String title); Optional selectFromList(Collection options, String message, String title); - Optional selectFromList(Collection options, String message, String title, Function toStringMapper); + Optional selectFromList(Collection options, String message, String title, + Function toStringMapper); Optional choice(Collection options, String message, String title); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerCLI.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerCLI.java index 5419f24d..b27e9cea 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerCLI.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerCLI.java @@ -5,6 +5,8 @@ import java.util.function.Function; import java.util.function.Predicate; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.codehaus.plexus.components.interactivity.DefaultPrompter; import org.codehaus.plexus.components.interactivity.Prompter; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; @@ -12,15 +14,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor public class UserInteractionHandlerCLI implements UserInteractionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(UserInteractionHandlerCLI.class); - private final Prompter prompter = new DefaultPrompter(); - private final UserInteractionSettingsIntf settings; + @val Prompter prompter = new DefaultPrompter(); + @val @override UserInteractionSettingsIntf settings; + + public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { + this.settings = settings; + } @Override public Optional selectFromList(Collection options, String message, String title) { @@ -28,8 +29,13 @@ public Optional selectFromList(Collection options, String messag } @Override - public Optional selectFromList(Collection options, String message, String title, Function toStringMapper) { - return PrompterUtil.getElementFromList(options).toStringMapper(toStringMapper).message(message).includeNull().prompt(prompter); + public Optional selectFromList(Collection options, String message, String title, + Function toStringMapper) { + return PrompterUtil.getElementFromList(options) + .toStringMapper(toStringMapper) + .message(message) + .includeNull() + .prompt(prompter); } @Override @@ -38,7 +44,8 @@ public Optional choice(Collection options, String message, String titl } @Override - public Optional choice(Collection options, String message, String title, Function toStringMapper) { + public Optional choice(Collection options, String message, String title, + Function toStringMapper) { return selectFromList(options, message, title, toStringMapper); } @@ -49,7 +56,11 @@ public boolean confirm(String message, String title) { @Override public Optional enter(String title, String message, String errorMessage, Predicate validator) { - return PrompterUtil.getString().message(message).errorMessage(errorMessage).objectValidator(validator).prompt(prompter); + return PrompterUtil.getString() + .message(message) + .errorMessage(errorMessage) + .objectValidator(validator) + .prompt(prompter); } @Override diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerGUI.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerGUI.java index 8865fd5e..897f2856 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerGUI.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/userinteraction/UserInteractionHandlerGUI.java @@ -7,18 +7,18 @@ import java.util.function.Function; import java.util.function.Predicate; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.gui.InputPane; import org.lodder.subtools.sublibrary.gui.OptionsPane; -@Getter -@RequiredArgsConstructor +@AllArgsConstructor public class UserInteractionHandlerGUI implements UserInteractionHandler { - private final UserInteractionSettingsIntf settings; - private final JFrame frame; + @val @override UserInteractionSettingsIntf settings; + @val JFrame frame; @Override public Optional selectFromList(Collection options, String message, String title) { @@ -26,11 +26,18 @@ public Optional selectFromList(Collection options, String messag } @Override - public Optional selectFromList(Collection options, String message, String title, Function toStringMapper) { + public Optional selectFromList(Collection options, String message, String title, + Function toStringMapper) { if (options.isEmpty()) { return Optional.empty(); } - return OptionsPane.options(options).toStringMapper(toStringMapper).title(title).message(message).defaultOption().parent(frame).prompt(); + return OptionsPane.options(options) + .toStringMapper(toStringMapper) + .title(title) + .message(message) + .defaultOption() + .parent(frame) + .prompt(); } @Override @@ -39,10 +46,14 @@ public Optional choice(Collection options, String message, String titl } @Override - public Optional choice(Collection options, String message, String title, Function toStringMapper) { - String[] optionsAsStrings = options.stream().map(Objects.requireNonNullElseGet(toStringMapper, () -> String::valueOf)).toArray(String[]::new); + public Optional choice(Collection options, String message, String title, + Function toStringMapper) { + String[] optionsAsStrings = options.stream() + .map(Objects.requireNonNullElseGet(toStringMapper, () -> String::valueOf)) + .toArray(String[]::new); int selection = - JOptionPane.showOptionDialog(frame, message, title, JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, optionsAsStrings, + JOptionPane.showOptionDialog(frame, message, title, JOptionPane.DEFAULT_OPTION, + JOptionPane.QUESTION_MESSAGE, null, optionsAsStrings, optionsAsStrings[0]); return selection == JOptionPane.CLOSED_OPTION ? Optional.empty() : options.stream().skip(selection).findFirst(); } @@ -55,7 +66,12 @@ public boolean confirm(String message, String title) { @Override public Optional enter(String title, String message, String errorMessage, Predicate validator) { - return InputPane.create().title(title).message(message).errorMessage(errorMessage).validator(validator).prompt(); + return InputPane.create() + .title(title) + .message(message) + .errorMessage(errorMessage) + .validator(validator) + .prompt(); } public void message(String message, String title) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/ArrayExtension.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/ArrayExtension.java deleted file mode 100644 index 32db7528..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/ArrayExtension.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.util.Arrays; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class ArrayExtension { - - public boolean contains(T[] array, T value) { - return Arrays.asList(array).contains(value); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/CopyDirVisitor.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/CopyDirVisitor.java index fa0bea00..d0d7ee77 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/CopyDirVisitor.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/CopyDirVisitor.java @@ -21,7 +21,7 @@ public class CopyDirVisitor extends SimpleFileVisitor { private final StandardCopyOption[] copyOptions; public CopyDirVisitor(Path fromPath, Path toPath) { - this(fromPath, toPath, new StandardCopyOption[] { StandardCopyOption.REPLACE_EXISTING }); + this(fromPath, toPath, new StandardCopyOption[]{ StandardCopyOption.REPLACE_EXISTING }); } @Override diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/IOUtils.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/IOUtils.java deleted file mode 100644 index 2c830f31..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/IOUtils.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class IOUtils { - - public InputStream toInputStream(String text, Charset charset) { - return new ByteArrayInputStream(text.getBytes(charset)); - } - - public String toString(InputStream inputStream, Charset charset) throws IOException { - return new String(inputStream.readAllBytes(), charset); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/JSONUtils.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/JSONUtils.java deleted file mode 100644 index 46966e91..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/JSONUtils.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.util.Iterator; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import org.json.JSONArray; -import org.json.JSONObject; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class JSONUtils { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Stream stream(JSONArray jsonArray) { - return StreamSupport - .stream(Spliterators.spliteratorUnknownSize((Iterator) (Iterator) jsonArray.iterator(), Spliterator.ORDERED), false); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatchResult.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatchResult.java index 8007e4f9..cad34dc1 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatchResult.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatchResult.java @@ -3,10 +3,13 @@ import java.util.List; import java.util.Map; import java.util.regex.MatchResult; +import java.util.stream.IntStream; public interface NamedMatchResult extends MatchResult { - List orderedGroups(); + default List orderedGroups() { + return IntStream.rangeClosed(1, groupCount()).sequential().mapToObj(this::group).toList(); + } Map namedGroups(); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatcher.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatcher.java index ca09809b..273627dc 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatcher.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatcher.java @@ -1,14 +1,14 @@ package org.lodder.subtools.sublibrary.util; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.IntStream; +import manifold.ext.rt.api.Self; + public class NamedMatcher implements NamedMatchResult { private Matcher matcher; @@ -24,7 +24,7 @@ public class NamedMatcher implements NamedMatchResult { NamedMatcher(NamedPattern parentPattern, CharSequence input) { this.parentPattern = parentPattern; - this.matcher = parentPattern.pattern().matcher(input); + this.matcher = parentPattern.pattern.matcher(input); } public Pattern standardPattern() { @@ -37,7 +37,7 @@ public NamedPattern namedPattern() { public NamedMatcher usePattern(NamedPattern newPattern) { this.parentPattern = newPattern; - matcher.usePattern(newPattern.pattern()); + matcher.usePattern(newPattern.pattern); return this; } @@ -95,11 +95,6 @@ public int groupCount() { return matcher.groupCount(); } - @Override - public List orderedGroups() { - return IntStream.rangeClosed(1, groupCount()).sequential().mapToObj(this::group).collect(Collectors.toList()); - } - @Override public String group(String groupName) { return group(groupIndex(groupName)); @@ -108,13 +103,11 @@ public String group(String groupName) { @Override public Map namedGroups() { return IntStream.rangeClosed(1, groupCount()).sequential() - .collect(LinkedHashMap::new, - (map, i) -> map.put(parentPattern.groupNames().get(i - 1), i), - Map::putAll); + .collect(LinkedHashMap::new, (map, i) -> map.put(parentPattern.groupNames.get(i - 1), i), Map::putAll); } private int groupIndex(String groupName) { - return parentPattern.groupNames().indexOf(groupName) + 1; + return parentPattern.groupNames.indexOf(groupName) + 1; } @Override @@ -194,7 +187,8 @@ public NamedMatcher useTransparentBounds(boolean b) { return this; } - public boolean equals(Matcher obj) { + @Override + public boolean equals(@Self Object obj) { return matcher.equals(obj); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedPattern.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedPattern.java index c0d89bc7..31e1958d 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedPattern.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedPattern.java @@ -5,13 +5,15 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import manifold.ext.props.rt.api.val; + public class NamedPattern { private static final Pattern NAMED_GROUP_PATTERN = Pattern.compile("\\(\\?<(\\w+)>"); - private final Pattern pattern; - private final String namedPattern; - private final List groupNames; + @val Pattern pattern; + @val String namedPattern; + @val List groupNames; public static NamedPattern compile(String regex) { return new NamedPattern(regex, 0); @@ -35,22 +37,10 @@ public NamedMatcher matcher(CharSequence input) { return new NamedMatcher(this, input); } - Pattern pattern() { - return pattern; - } - public String standardPattern() { return pattern.pattern(); } - public String namedPattern() { - return namedPattern; - } - - public List groupNames() { - return groupNames; - } - public String[] split(CharSequence input, int limit) { return pattern.split(input, limit); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/OptionalExtension.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/OptionalExtension.java deleted file mode 100644 index 073d4299..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/OptionalExtension.java +++ /dev/null @@ -1,165 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.util.Optional; -import java.util.OptionalInt; -import java.util.OptionalLong; -import java.util.function.Supplier; - -import org.lodder.subtools.sublibrary.util.throwingfunction.ThrowingIntConsumer; - -import com.pivovarit.function.ThrowingConsumer; -import com.pivovarit.function.ThrowingFunction; -import com.pivovarit.function.ThrowingIntFunction; -import com.pivovarit.function.ThrowingRunnable; -import com.pivovarit.function.ThrowingSupplier; - -import lombok.experimental.UtilityClass; -import name.falgout.jeffrey.throwing.ThrowingIntPredicate; -import name.falgout.jeffrey.throwing.ThrowingLongConsumer; -import name.falgout.jeffrey.throwing.ThrowingLongFunction; -import name.falgout.jeffrey.throwing.ThrowingToIntFunction; - -@UtilityClass -public class OptionalExtension { - - public static Optional ifPresentDo(Optional optional, ThrowingConsumer consumer) throws X { - if (optional.isPresent()) { - consumer.accept(optional.get()); - } - return optional; - } - - public static OptionalInt ifPresentDo(OptionalInt optional, ThrowingIntConsumer consumer) throws X { - if (optional.isPresent()) { - consumer.accept(optional.getAsInt()); - } - return optional; - } - - public static OptionalLong ifPresentDo(OptionalLong optional, ThrowingLongConsumer consumer) throws X { - if (optional.isPresent()) { - consumer.accept(optional.getAsLong()); - } - return optional; - } - - //// - - public static void orElseDo(Optional optional, ThrowingRunnable consumer) throws X { - if (optional.isEmpty()) { - consumer.run(); - } - } - - public static void orElseDo(OptionalInt optional, ThrowingRunnable consumer) throws X { - if (optional.isEmpty()) { - consumer.run(); - } - } - - public static void orElseDo(OptionalLong optional, ThrowingRunnable consumer) throws X { - if (optional.isEmpty()) { - consumer.run(); - } - } - - // - - public static Optional ifEmptyDo(Optional optional, ThrowingRunnable runnable) throws X { - if (optional.isEmpty()) { - runnable.run(); - } - return optional; - } - - // - - public static Optional orElseMap(Optional optional, ThrowingSupplier, X> supplier) throws X { - return optional.isPresent() ? optional : supplier.get(); - } - - public static OptionalInt orElseMap(OptionalInt optionalInt, ThrowingSupplier intSupplier) throws X { - return optionalInt.isPresent() ? optionalInt : intSupplier.get(); - } - - // - - public static T mapOrElseGet(Optional optional, ThrowingFunction ifPresentFunction, - ThrowingSupplier absentSupplier) throws X { - return optional.isPresent() ? ifPresentFunction.apply(optional.get()) : absentSupplier.get(); - } - - public static T mapOrElseGet(OptionalInt optionalInt, ThrowingIntFunction ifPresentFunction, - ThrowingSupplier absentSupplier) throws X { - return optionalInt.isPresent() ? ifPresentFunction.apply(optionalInt.getAsInt()) : absentSupplier.get(); - } - - // - - public static OptionalInt mapToInt(Optional optional, ThrowingToIntFunction mapper) throws X { - return optional.isPresent() ? OptionalInt.of(mapper.applyAsInt(optional.get())) : OptionalInt.empty(); - } - - public static OptionalInt mapToOptionalInt(Optional optional) { - return optional.map(OptionalInt::of).orElseGet(OptionalInt::empty); - } - - // - - public static Optional mapToObj(Optional optional, ThrowingFunction mapper) throws X { - return optional.isPresent() ? Optional.ofNullable(mapper.apply(optional.get())) : Optional.empty(); - } - - public static Optional mapToObj(OptionalInt optionalInt, ThrowingIntFunction mapper) throws X { - return optionalInt.isPresent() ? Optional.ofNullable(mapper.apply(optionalInt.getAsInt())) : Optional.empty(); - } - - public static Optional mapToObj(OptionalLong optionalLong, ThrowingLongFunction mapper) throws X { - return optionalLong.isPresent() ? Optional.ofNullable(mapper.apply(optionalLong.getAsLong())) : Optional.empty(); - } - - // - - public static OptionalLong map(OptionalLong optionalLong, ThrowingLongFunction mapper) throws X { - return optionalLong.isPresent() ? OptionalLong.of(mapper.apply(optionalLong.getAsLong())) : optionalLong; - } - - // - - public static Optional mapToOptionalObj(Optional optional, ThrowingFunction, X> mapper) - throws X { - return optional.isPresent() ? mapper.apply(optional.get()) : Optional.empty(); - } - - public static Optional mapToOptionalObj(OptionalInt optionalInt, ThrowingIntFunction, X> mapper) - throws X { - return optionalInt.isPresent() ? mapper.apply(optionalInt.getAsInt()) : Optional.empty(); - } - - // - - public static OptionalInt filter(OptionalInt optionalInt, ThrowingIntPredicate predicate) - throws X { - return optionalInt.isPresent() && predicate.test(optionalInt.getAsInt()) ? optionalInt : OptionalInt.empty(); - } - - // - - public static void ifPresentOrThrow(Optional optional, ThrowingConsumer consumer, - Supplier exceptionSupplier) throws X { - if (optional.isPresent()) { - consumer.accept(optional.get()); - } else { - throw exceptionSupplier.get(); - } - } - - public static void ifPresentOrThrow(OptionalInt optionalInt, ThrowingIntConsumer consumer, - Supplier exceptionSupplier) throws X { - if (optionalInt.isPresent()) { - consumer.accept(optionalInt.getAsInt()); - } else { - throw exceptionSupplier.get(); - } - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StreamExtension.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StreamExtension.java deleted file mode 100644 index 407e3973..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StreamExtension.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.util.Collection; -import java.util.stream.Stream; - -import lombok.experimental.UtilityClass; -import name.falgout.jeffrey.throwing.stream.ThrowingStream; -import net.jodah.typetools.TypeResolver; - -@UtilityClass -public class StreamExtension { - - public static ThrowingStream asThrowingStream(Stream stream, Class exceptionType) { - return ThrowingStream.of(stream, exceptionType); - } - - public static , T, X extends Exception> ThrowingStream normalFlatMap(ThrowingStream stream) { - @SuppressWarnings("unchecked") - Class exceptionType = (Class) TypeResolver.resolveRawArguments(ThrowingStream.class, stream.getClass())[1]; - return stream.normalFlatMap(collection -> ThrowingStream.of(collection.stream(), exceptionType)); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StringUtil.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StringUtil.java deleted file mode 100755 index 1cfda7f7..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StringUtil.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.lang3.StringUtils; - -import lombok.experimental.ExtensionMethod; -import lombok.experimental.UtilityClass; - -@ExtensionMethod({ StringUtils.class }) -@UtilityClass -public class StringUtil { - - public static String removeIllegalFilenameChars(String s) { - return s.replace("/", "").replace("\0", ""); - } - - public static String removeIllegalWindowsChars(String text) { - return text.replaceAll("[\\\\/:*?\"<>|]", "").removeEnd(".").trim(); - } - - public static String urlEncode(String text) { - return URLEncoder.encode(text, StandardCharsets.UTF_8); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Utils.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Utils.java deleted file mode 100755 index 715eefa9..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Utils.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class Utils { - - public static boolean containsAll(List listA, List listB) { - Set listAAsSet = new HashSet<>(listA); - - for (Integer integer : listB) { - - if (!listAAsSet.contains(integer)) { - return false; - } - } - return true; - } - -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/ExtensionFileFilter.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/ExtensionFileFilter.java index ee245716..39994148 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/ExtensionFileFilter.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/ExtensionFileFilter.java @@ -3,14 +3,14 @@ import javax.swing.filechooser.*; import java.io.File; -import org.lodder.subtools.sublibrary.util.FileUtils; +import manifold.ext.props.rt.api.val; -public abstract class ExtensionFileFilter extends FileFilter { +public abstract sealed class ExtensionFileFilter extends FileFilter permits JsonFileFilter, XmlFileFilter { + + @val abstract String extension; @Override public boolean accept(File f) { - return f.isDirectory() || getExtension().equals(FileUtils.getExtension(f.toPath())); + return f.isDirectory() || extension.equals(f.toPath().getExtension()); } - - public abstract String getExtension(); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/JsonFileFilter.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/JsonFileFilter.java index bd72f79a..ed845781 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/JsonFileFilter.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/JsonFileFilter.java @@ -1,14 +1,10 @@ package org.lodder.subtools.sublibrary.util.filefilter; -public class JsonFileFilter extends ExtensionFileFilter { +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; - @Override - public String getDescription() { - return "json files"; - } +public final class JsonFileFilter extends ExtensionFileFilter { - @Override - public String getExtension() { - return "json"; - } + @val @override String description = "json files"; + @val @override String extension = "json"; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/XmlFileFilter.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/XmlFileFilter.java index 69f51efc..908335b4 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/XmlFileFilter.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/filefilter/XmlFileFilter.java @@ -1,14 +1,10 @@ package org.lodder.subtools.sublibrary.util.filefilter; -public class XmlFileFilter extends ExtensionFileFilter { +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; - @Override - public String getDescription() { - return "xml files"; - } +public final class XmlFileFilter extends ExtensionFileFilter { - @Override - public String getExtension() { - return "xml"; - } + @val @override String description = "xml files"; + @val @override String extension = "xml"; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClient.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClient.java index e3d940a9..413c6048 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClient.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClient.java @@ -22,14 +22,13 @@ import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; +import extensions.java.io.InputStream.InputStreamExt; +import extensions.java.nio.file.Path.PathExt; +import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; -import org.lodder.subtools.sublibrary.util.FileUtils; -import org.lodder.subtools.sublibrary.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.RequiredArgsConstructor; - @RequiredArgsConstructor public class HttpClient { @@ -45,14 +44,14 @@ public String doGet(URL url, String userAgent) throws IOException, HttpClientExc URLConnection conn = url.openConnection(); cookieManager.setCookies(conn); - if (userAgent != null && userAgent.length() > 0) { + if (userAgent != null && !userAgent.isEmpty()) { conn.setRequestProperty("user-agent", userAgent); } int respCode = ((HttpURLConnection) conn).getResponseCode(); if (respCode == 200) { - String result = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8); + String result = InputStreamExt.asString(conn.getInputStream(), StandardCharsets.UTF_8); ((HttpURLConnection) conn).disconnect(); return result; } @@ -74,7 +73,8 @@ public String doPost(URL url, String userAgent, Map data) throws conn.setRequestProperty("user-agent", userAgent); } conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - conn.setRequestProperty("Content-Length", String.valueOf(urlParameters.getBytes(StandardCharsets.UTF_8).length)); + conn.setRequestProperty("Content-Length", + String.valueOf(urlParameters.getBytes(StandardCharsets.UTF_8).length)); conn.setUseCaches(false); conn.setDoInput(true); conn.setDoOutput(true); @@ -91,7 +91,7 @@ public String doPost(URL url, String userAgent, Map data) throws return doGet(new URI(conn.getHeaderField("Location")).toURL(), userAgent); } - String result = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8); + String result = InputStreamExt.asString(conn.getInputStream(), StandardCharsets.UTF_8); conn.disconnect(); return result; @@ -108,22 +108,24 @@ public boolean doDownloadFile(URL url, final Path file) { LOGGER.debug("doDownloadFile: URL [{}], file [{}]", url, file); boolean success = true; - try (InputStream in = url.getFile().endsWith(".gz") ? new GZIPInputStream(url.openStream()) : getInputStream(url)) { + try (InputStream in = url.getFile().endsWith(".gz") ? new GZIPInputStream(url.openStream()) : + getInputStream(url)) { byte[] data = in.readAllBytes(); in.close(); - if (url.getFile().endsWith(".zip") || FileUtils.isZipFile(new ByteArrayInputStream(data))) { - FileUtils.unzip(new ByteArrayInputStream(data), file, ".srt"); + if (url.getFile().endsWith(".zip") || PathExt.isZipFile(new ByteArrayInputStream(data))) { + PathExt.unzip(new ByteArrayInputStream(data), file, ".srt"); } else { - if (FileUtils.isGZipCompressed(data)) { - data = FileUtils.decompressGZip(data); + if (PathExt.isGZipCompressed(data)) { + data = PathExt.decompressGZip(data); } String content = new String(data, StandardCharsets.UTF_8); if (content.contains("Daily Download count exceeded")) { LOGGER.error("Download problem: Addic7ed Daily Download count exceeded!"); success = false; } else { - Files.write(file, data, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); + Files.write(file, data, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE); } } } catch (Exception e) { @@ -152,7 +154,8 @@ private InputStream getInputStream(URL url) throws Exception { } else { String protocol = url.getProtocol(); String host = conn.getURL().getHost(); - url = new URI("%s://%s/%s".formatted(protocol, host, conn.getHeaderField("Location").trim().replace(" ", "%20"))).toURL(); + url = new URI("%s://%s/%s".formatted(protocol, host, + conn.getHeaderField("Location").trim().replace(" ", "%20"))).toURL(); } return getInputStream(url); } @@ -164,14 +167,16 @@ private InputStream getInputStream(URL url) throws Exception { } public static boolean isUrl(String str) { - Pattern urlPattern = Pattern.compile("((https?|ftp|gopher|telnet|file):((//)|(\\\\\\\\))+[\\\\w\\\\d:#@%/;$()~_?\\\\+-=\\\\\\\\\\\\.&]*)", + Pattern urlPattern = Pattern.compile( + "((https?|ftp|gopher|telnet|file):((//)|(\\\\\\\\))+[\\\\w\\\\d:#@%/;$()~_?\\\\+-=\\\\\\\\\\\\.&]*)", Pattern.CASE_INSENSITIVE); Matcher matcher = urlPattern.matcher(str); return matcher.find(); } public String downloadText(String url) throws IOException { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new URI(url).toURL().openStream(), StandardCharsets.UTF_8))) { + try (BufferedReader in = new BufferedReader( + new InputStreamReader(new URI(url).toURL().openStream(), StandardCharsets.UTF_8))) { return in.lines().collect(Collectors.joining()); } catch (URISyntaxException e) { throw new IOException(e.getMessage(), e); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyBiFunction.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyBiFunction.java index fe55374b..54dfc9f6 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyBiFunction.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyBiFunction.java @@ -1,8 +1,7 @@ package org.lodder.subtools.sublibrary.util.lazy; -import org.lodder.subtools.sublibrary.util.Nothing; - import com.pivovarit.function.ThrowingBiFunction; +import org.lodder.subtools.sublibrary.util.Nothing; public class LazyBiFunction extends LazyThrowingBiFunction { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyFunction.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyFunction.java index 6b7a3551..29c99968 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyFunction.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyFunction.java @@ -1,13 +1,12 @@ package org.lodder.subtools.sublibrary.util.lazy; -import org.lodder.subtools.sublibrary.util.Nothing; - import com.pivovarit.function.ThrowingFunction; +import org.lodder.subtools.sublibrary.util.Nothing; public class LazyFunction extends LazyThrowingFunction { public LazyFunction(ThrowingFunction function) { super(function); - } + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyRunnable.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyRunnable.java index 0065d68e..f0304058 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyRunnable.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyRunnable.java @@ -1,8 +1,7 @@ package org.lodder.subtools.sublibrary.util.lazy; -import org.lodder.subtools.sublibrary.util.Nothing; - import com.pivovarit.function.ThrowingRunnable; +import org.lodder.subtools.sublibrary.util.Nothing; public class LazyRunnable extends LazyThrowingRunnable { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazySupplier.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazySupplier.java index 480e1c1c..eeb8d823 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazySupplier.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazySupplier.java @@ -1,16 +1,15 @@ package org.lodder.subtools.sublibrary.util.lazy; -import org.lodder.subtools.sublibrary.util.Nothing; - import com.pivovarit.function.ThrowingSupplier; +import org.lodder.subtools.sublibrary.util.Nothing; public class LazySupplier extends LazyThrowingSupplier { - public LazySupplier(ThrowingSupplier supplier) { - super(supplier); - } + public LazySupplier(ThrowingSupplier supplier) { + super(supplier); + } - public LazySupplier(T value) { - super(value); - } + public LazySupplier(T value) { + super(value); + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingBiFunction.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingBiFunction.java index 485b17fe..89f08038 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingBiFunction.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingBiFunction.java @@ -1,7 +1,6 @@ package org.lodder.subtools.sublibrary.util.lazy; import com.pivovarit.function.ThrowingBiFunction; - import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -11,27 +10,27 @@ public class LazyThrowingBiFunction { private V object; - private final Object lock = new Object(); + private final Object lock = new Object(); - private volatile boolean initialized = false; + private volatile boolean initialized = false; public V apply(T arg1, S arg2) throws X { - if (!initialized) { - synchronized (lock) { - if (!initialized) { + if (!initialized) { + synchronized (lock) { + if (!initialized) { object = function.apply(arg1, arg2); - initialized = true; - } - } - } + initialized = true; + } + } + } return object; - } + } - public boolean isInitialized() { - return initialized; - } + public boolean isInitialized() { + return initialized; + } - public void reset() { - initialized = false; - } + public void reset() { + initialized = false; + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingFunction.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingFunction.java index 0910eb7e..80e38067 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingFunction.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingFunction.java @@ -1,7 +1,6 @@ package org.lodder.subtools.sublibrary.util.lazy; import com.pivovarit.function.ThrowingFunction; - import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -11,27 +10,27 @@ public class LazyThrowingFunction { private S object; - private final Object lock = new Object(); + private final Object lock = new Object(); - private volatile boolean initialized = false; + private volatile boolean initialized = false; public S apply(T arg) throws X { - if (!initialized) { - synchronized (lock) { - if (!initialized) { + if (!initialized) { + synchronized (lock) { + if (!initialized) { object = function.apply(arg); - initialized = true; - } - } - } + initialized = true; + } + } + } return object; - } + } - public boolean isInitialized() { - return initialized; - } + public boolean isInitialized() { + return initialized; + } - public void reset() { - initialized = false; - } + public void reset() { + initialized = false; + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingRunnable.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingRunnable.java index be7fa9ca..8b3f4d2b 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingRunnable.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingRunnable.java @@ -1,34 +1,33 @@ package org.lodder.subtools.sublibrary.util.lazy; import com.pivovarit.function.ThrowingRunnable; - import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class LazyThrowingRunnable { - private final ThrowingRunnable runnable; + private final ThrowingRunnable runnable; - private final Object lock = new Object(); + private final Object lock = new Object(); - private volatile boolean initialized = false; + private volatile boolean initialized = false; public void run() throws X { - if (!initialized) { - synchronized (lock) { - if (!initialized) { + if (!initialized) { + synchronized (lock) { + if (!initialized) { runnable.run(); - initialized = true; - } - } - } - } - - public boolean isInitialized() { - return initialized; - } - - public void reset() { - initialized = false; - } + initialized = true; + } + } + } + } + + public boolean isInitialized() { + return initialized; + } + + public void reset() { + initialized = false; + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingSupplier.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingSupplier.java index 1feca2d8..a623e06f 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingSupplier.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingSupplier.java @@ -2,49 +2,48 @@ import com.pivovarit.function.ThrowingConsumer; import com.pivovarit.function.ThrowingSupplier; - import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class LazyThrowingSupplier { - private final ThrowingSupplier supplier; - - private T object; - - private final Object lock = new Object(); - - private volatile boolean initialized = false; - - public LazyThrowingSupplier(T value) { - supplier = null; - object = value; - initialized = true; - } - - public T get() throws X { - if (!initialized) { - synchronized (lock) { - if (!initialized) { - object = supplier.get(); - initialized = true; - } - } - } - return object; - } - - public boolean isInitialized() { - return initialized; - } - - public void doIfInitialized(ThrowingConsumer consumer) throws X { - if (initialized) { - consumer.accept(object); - } - } - - public void reset() { - initialized = false; - } + private final ThrowingSupplier supplier; + + private T object; + + private final Object lock = new Object(); + + private volatile boolean initialized = false; + + public LazyThrowingSupplier(T value) { + supplier = null; + object = value; + initialized = true; + } + + public T get() throws X { + if (!initialized) { + synchronized (lock) { + if (!initialized) { + object = supplier.get(); + initialized = true; + } + } + } + return object; + } + + public boolean isInitialized() { + return initialized; + } + + public void doIfInitialized(ThrowingConsumer consumer) throws X { + if (initialized) { + consumer.accept(object); + } + } + + public void reset() { + initialized = false; + } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderBoolean.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderBoolean.java index 38d94fb9..95ef4859 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderBoolean.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderBoolean.java @@ -3,10 +3,9 @@ import java.util.function.BooleanSupplier; import java.util.function.Predicate; -import org.codehaus.plexus.components.interactivity.Prompter; - import lombok.Setter; import lombok.experimental.Accessors; +import org.codehaus.plexus.components.interactivity.Prompter; public class PrompterBuilderBoolean { @@ -73,13 +72,13 @@ public ValueBuilder defaultValue(boolean defaultValue) { @Override public ValueBuilder message(String message, Object... replacements) { - this.message = String.format(message, replacements); + this.message = message.formatted(replacements); return this; } @Override public ValueBuilder errorMessage(String errorMessage, Object... replacements) { - this.errorMessage = String.format(errorMessage, replacements); + this.errorMessage = errorMessage.formatted(replacements); return this; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderCommon.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderCommon.java index 91cdde72..cfbdfaf8 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderCommon.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderCommon.java @@ -16,8 +16,9 @@ private PrompterBuilderCommon() { // hide constructor } - protected static Optional prompt(Prompter prompter, Function toObjectMapper, Predicate validator, - Predicate objValidator, T defaultValue, Supplier defaultValueSupplier, String message, String errorMessage) { + protected static Optional prompt(Prompter prompter, Function toObjectMapper, + Predicate validator, Predicate objValidator, T defaultValue, Supplier defaultValueSupplier, + String message, String errorMessage) { try { String value = prompter.prompt(message + System.lineSeparator()); if (StringUtils.isEmpty(value)) { @@ -26,17 +27,22 @@ protected static Optional prompt(Prompter prompter, Function t } else if (defaultValueSupplier != null) { return Optional.ofNullable(defaultValueSupplier.get()); } else { - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, message, errorMessage); + return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, + message, errorMessage); } } else { if (validator != null && !validator.test(value)) { - prompter.showMessage(StringUtils.isNotBlank(errorMessage) ? errorMessage : Messages.getString("Prompter.ValueIsNotValid")); - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, message, errorMessage); + prompter.showMessage(StringUtils.isNotBlank(errorMessage) ? errorMessage : + Messages.getText("Prompter.ValueIsNotValid")); + return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, + message, errorMessage); } T object = toObjectMapper.apply(value); if (objValidator != null && !objValidator.test(object)) { - prompter.showMessage(StringUtils.isNotBlank(errorMessage) ? errorMessage : Messages.getString("Prompter.ValueIsNotValid")); - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, message, errorMessage); + prompter.showMessage(StringUtils.isNotBlank(errorMessage) ? errorMessage : + Messages.getText("Prompter.ValueIsNotValid")); + return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, + message, errorMessage); } return Optional.ofNullable(object); } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderInt.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderInt.java index 9b0b99ab..d98b3981 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderInt.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderInt.java @@ -5,10 +5,9 @@ import java.util.function.Predicate; import java.util.function.ToIntFunction; -import org.codehaus.plexus.components.interactivity.Prompter; - import lombok.Setter; import lombok.experimental.Accessors; +import org.codehaus.plexus.components.interactivity.Prompter; public class PrompterBuilderInt { @@ -89,13 +88,13 @@ public ValueBuilder defaultValue(int defaultValue) { @Override public ValueBuilder message(String message, Object... replacements) { - this.message = String.format(message, replacements); + this.message = message.formatted(replacements); return this; } @Override public ValueBuilder errorMessage(String errorMessage, Object... replacements) { - this.errorMessage = String.format(errorMessage, replacements); + this.errorMessage = errorMessage.formatted(replacements); return this; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValue.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValue.java index 2c010eb0..fedeef5f 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValue.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValue.java @@ -5,10 +5,9 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import org.codehaus.plexus.components.interactivity.Prompter; - import lombok.Setter; import lombok.experimental.Accessors; +import org.codehaus.plexus.components.interactivity.Prompter; public class PrompterBuilderValue { @@ -84,13 +83,13 @@ public ValueBuilderValidatorMapperIntf toObjectMapper(Function @Override public ValueBuilder message(String message, Object... replacements) { - this.message = String.format(message, replacements); + this.message = message.formatted(replacements); return this; } @Override public ValueBuilder errorMessage(String errorMessage, Object... replacements) { - this.errorMessage = String.format(errorMessage, replacements); + this.errorMessage = errorMessage.formatted(replacements); return this; } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValueFromList.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValueFromList.java index 8665f883..c4d66411 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValueFromList.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValueFromList.java @@ -10,13 +10,12 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import lombok.Setter; +import lombok.experimental.Accessors; import org.apache.commons.lang3.StringUtils; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; -import lombok.Setter; -import lombok.experimental.Accessors; - public class PrompterBuilderValueFromList { private PrompterBuilderValueFromList() { @@ -84,7 +83,7 @@ public ValueFromListBuilder sort(Comparator comparator) { @Override public ValueFromListBuilder message(String message, Object... replacements) { - this.message = String.format(message, replacements); + this.message = message.formatted(replacements); return this; } @@ -116,7 +115,7 @@ public Optional prompt(Prompter prompter) { value = prompter.prompt(message); } else { String choicesMessage = IntStream.range(0, elements.size()) - .mapToObj(number -> " - " + (number + 1) + ": " + toStringMapper.apply(elements.get(number))) + .mapToObj(nbr -> " - " + (nbr + 1) + ": " + toStringMapper.apply(elements.get(nbr))) .collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator(); value = prompter.prompt(StringUtils.isBlank(message) ? choicesMessage : message + System.lineSeparator() + choicesMessage); @@ -126,7 +125,8 @@ public Optional prompt(Prompter prompter) { } int number = Integer.parseInt(value); if (number < 1 || number > elements.size()) { - PrompterUtil.showMessage(prompter, "The entered value isn't in the range [1, %s], try again.", elements.size()); + PrompterUtil.showMessage(prompter, "The entered value isn't in the range [1, %s], try again.", + elements.size()); return prompt(prompter); } return Optional.ofNullable(elements.get(number - 1)); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValuesFromList.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValuesFromList.java index 3f189cc2..1b846c87 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValuesFromList.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValuesFromList.java @@ -9,13 +9,12 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import lombok.Setter; +import lombok.experimental.Accessors; import org.apache.commons.lang3.StringUtils; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; -import lombok.Setter; -import lombok.experimental.Accessors; - public class PrompterBuilderValuesFromList { private PrompterBuilderValuesFromList() { @@ -80,7 +79,7 @@ public ValuesFromListBuilder sort(Comparator comparator) { @Override public ValuesFromListBuilder message(String message, Object... replacements) { - this.message = String.format(message, replacements); + this.message = message.formatted(replacements); return this; } @@ -105,9 +104,11 @@ public List prompt(Prompter prompter) { value = prompter.prompt(message); } else { String choicesMessage = IntStream.range(0, elements.size()) - .mapToObj(number -> " - " + (number + 1) + ": " + toStringMapper.apply(elements.get(number))) + .mapToObj( + number -> " - " + (number + 1) + ": " + toStringMapper.apply(elements.get(number))) .collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator(); - value = prompter.prompt(StringUtils.isBlank(message) ? choicesMessage : message + System.lineSeparator() + choicesMessage); + value = prompter.prompt(StringUtils.isBlank(message) ? choicesMessage : + message + System.lineSeparator() + choicesMessage); } if (StringUtils.isBlank(value) && includeNull) { return new ArrayList<>(); @@ -115,19 +116,21 @@ public List prompt(Prompter prompter) { if (StringUtils.isBlank(value)) { return prompt(PrompterUtil.showMessage(prompter, "Enter a valid value, try again.")); } - List choices = Arrays.stream(value.split(",")).map(Integer::parseInt).map(i -> i - 1).toList(); + List choices = value.split(",").stream().map(Integer::parseInt).map(i -> i - 1).toList(); if (choices.stream().distinct().count() != choices.size()) { return prompt(PrompterUtil.showMessage(prompter, "Choose all distinct options, try again.")); } if (choices.stream().anyMatch(number -> number < 0 || number > elements.size() - 1)) { - PrompterUtil.showMessage(prompter, "The entered number(s) aren't in the range [1, %s], try again.", elements.size()); + PrompterUtil.showMessage(prompter, "The entered number(s) aren't in the range [1, %s], try again.", + elements.size()); return prompt(prompter); } return choices.stream().map(elements::get).collect(Collectors.toList()); } catch (PrompterException e) { throw new IllegalStateException(e); } catch (NumberFormatException e) { - PrompterUtil.showMessage(prompter, "Invalid number(s) encountered. Enter a comma separated list of the choices."); + PrompterUtil.showMessage(prompter, + "Invalid number(s) encountered. Enter a comma separated list of the choices."); return prompt(prompter); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterUtil.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterUtil.java index cb813ef9..b6d9c9a1 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterUtil.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterUtil.java @@ -20,7 +20,7 @@ private PrompterUtil() { public static Prompter showMessage(Prompter prompter, String message, Object... replacements) { try { - prompter.showMessage(String.format(message, replacements)); + prompter.showMessage(message.formatted(replacements)); return prompter; } catch (PrompterException e) { throw new IllegalStateException(e); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java index abbfc9a4..013950fb 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.util.prompter; -import java.util.Arrays; import java.util.List; import dnl.utils.text.table.TextTable; @@ -14,8 +13,9 @@ public class TableDisplayer { @SafeVarargs public final void display(T... tableElements) { String[] columnNames = columnDisplayers.stream().map(ColumnDisplayer::columnName).toArray(String[]::new); - Object[][] dataTable = Arrays.stream(tableElements) - .map(tableElement -> columnDisplayers.stream().map(columnDisplayer -> columnDisplayer.toStringMapper().apply(tableElement)) + Object[][] dataTable = tableElements.stream() + .map(tableElement -> columnDisplayers.stream() + .map(columnDisplayer -> columnDisplayer.toStringMapper().apply(tableElement)) .toArray()) .toArray(Object[][]::new); @@ -29,7 +29,8 @@ public void display(List tableElements) { String[] columnNames = columnDisplayers.stream().map(ColumnDisplayer::columnName).toArray(String[]::new); Object[][] dataTable = tableElements.stream() - .map(tableElement -> columnDisplayers.stream().map(columnDisplayer -> columnDisplayer.toStringMapper().apply(tableElement)) + .map(tableElement -> columnDisplayers.stream() + .map(columnDisplayer -> columnDisplayer.toStringMapper().apply(tableElement)) .toArray()) .toArray(Object[][]::new); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/StringUtils.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/StringUtils.java index cd6c993d..770a3cfb 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/StringUtils.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/StringUtils.java @@ -2,6 +2,7 @@ import java.util.HashMap; + /** * Source */ @@ -10,6 +11,7 @@ private StringUtils() { } private static final HashMap htmlEntities; + static { htmlEntities = new HashMap<>(); htmlEntities.put("<", "<"); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XMLHelper.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XMLHelper.java index 4673c7da..ead88f9e 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XMLHelper.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XMLHelper.java @@ -1,15 +1,5 @@ package org.lodder.subtools.sublibrary.xml; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -18,6 +8,14 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import org.lodder.subtools.sublibrary.util.http.HttpClient; import org.slf4j.Logger; @@ -32,17 +30,17 @@ public class XMLHelper { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class); - private static String XMLCleanup(String text) { + private static String xmlCleanup(String text) { return text.replace("&", "&"); } - private static String HTMLCleanup(String text) { + private static String htmlCleanup(String text) { return StringUtils.unescapeHTML(text); } public static String getStringTagValue(String sTag, Element eElement) { LOGGER.trace("getStringTagValue: sTag [{}]", sTag); - return HTMLCleanup(XMLCleanup(getStringTagRawValue(sTag, eElement))); + return htmlCleanup(xmlCleanup(getStringTagRawValue(sTag, eElement))); } public static String getStringTagRawValue(String sTag, Element eElement) { @@ -59,7 +57,7 @@ public static String getStringTagRawValue(String sTag, Element eElement) { public static String getStringAttributeValue(String sTag, String sAttribute, Element eElement) { LOGGER.trace("getStringAttributeValue: sTag [{}], sAttribute [{}]", sTag, sAttribute); NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes(); - return XMLCleanup(((Element) nlList).getAttribute(sAttribute)); + return xmlCleanup(((Element) nlList).getAttribute(sAttribute)); } public static int getIntTagValue(String sTag, Element eElement) { @@ -77,8 +75,8 @@ public static boolean getBooleanTagValue(String sTag, Element eElement) { public static boolean getBooleanAttributeValue(String sTag, String sAttribute, Element eElement) { LOGGER.trace("getBooleanAttributeValue: sTag [{}], sAttribute [{}]", sTag, sAttribute); NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes(); - return ((Element) nlList).getAttribute(sAttribute) != null - && Boolean.parseBoolean(((Element) nlList).getAttribute(sAttribute)); + return ((Element) nlList).getAttribute(sAttribute) != null && + Boolean.parseBoolean(((Element) nlList).getAttribute(sAttribute)); } public static String cleanBadChars(String string) { @@ -124,30 +122,19 @@ public static String getXMLAsString(Element eElement) throws Exception { return result.getWriter().toString(); } - public static Optional getDocument(String string) throws ParserConfigurationException { - try { - return getDocument(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8))); - } catch (IOException e) { - // should not happen - return Optional.empty(); - } + public static Document getDocument(String string) throws ParserConfigurationException, IOException { + return getDocument(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8))); } - public static Optional getDocument(InputStream inputStream) throws ParserConfigurationException, - IOException { + public static Document getDocument(InputStream inputStream) throws ParserConfigurationException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // Use the factory to create a builder DocumentBuilder builder; builder = factory.newDocumentBuilder(); try { - return Optional.of(builder.parse(inputStream)); - } catch (SAXException e) { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("getDocument: Not a valid XML document, setting a blank document!"); - } else { - LOGGER.debug("Not a valid XML document, setting a blank document!"); - } + return builder.parse(inputStream); + } catch (SAXException | IOException e) { + throw new IOException("XML input could not be converted to a document"); } - return Optional.empty(); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XmlExtension.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XmlExtension.java deleted file mode 100644 index c6d937e2..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XmlExtension.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.lodder.subtools.sublibrary.xml; - -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class XmlExtension { - - public static Stream stream(NodeList nodeList) { - return IntStream.range(0, nodeList.getLength()).mapToObj(nodeList::item); - } -} diff --git a/SubLibrary/src/main/java/util/Utils.java b/SubLibrary/src/main/java/util/Utils.java new file mode 100644 index 00000000..89a84cf7 --- /dev/null +++ b/SubLibrary/src/main/java/util/Utils.java @@ -0,0 +1,46 @@ +package util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Collector; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class Utils { + + public static Collector, Map> mapCollector( + BiConsumer, T> accumulator) { + return mapCollector(HashMap::new, accumulator); + } + + public static , K, V> Collector mapCollector(Supplier supplier, + BiConsumer accumulator) { + return Collector.of( + supplier, + accumulator, + (R m1, R m2) -> { + m1.putAll(m2); + return m1; + }); + } + + public static Collector, Set> setCollector(BiConsumer, T> accumulator) { + return setCollector(HashSet::new, accumulator); + } + + public static , K> Collector setCollector(Supplier supplier, + BiConsumer accumulator) { + return Collector.of( + supplier, + accumulator, + (R m1, R m2) -> { + m1.addAll(m2); + return m1; + }); + } +} diff --git a/SubLibrary/src/main/resources/messages.properties b/SubLibrary/src/main/resources/messages.properties index 6aae81c3..b7abeda4 100644 --- a/SubLibrary/src/main/resources/messages.properties +++ b/SubLibrary/src/main/resources/messages.properties @@ -291,7 +291,7 @@ StructureBuilderDialog.MovieName=Movie name StructureBuilderDialog.MovieYear=Movie year StructureBuilderDialog.NameTvShow=Serie StructureBuilderDialog.NumberOfEpisodeLeadingZero=Number of episode (with 0). -StructureBuilderDialog.NumberOfEpisodeLeadingZeroForMultipe=Number of episode (with 0) for multiple episodes +StructureBuilderDialog.NumberOfEpisodeLeadingZeroForMultiple=Number of episode (with 0) for multiple episodes StructureBuilderDialog.NumberOfEpisodeLeadingZeroMultiple=Number of episode (without 0) for multiple episodes StructureBuilderDialog.NumberOfEpisodeWithoutLeadingZero=Number of episode (without 0). StructureBuilderDialog.NumberOfSeasonLeading=Number of season (with 0). @@ -300,11 +300,11 @@ StructureBuilderDialog.Preview=Preview StructureBuilderDialog.QualityOfMovie=Quality of the movie file , eg . HDTV 720p. StructureBuilderDialog.QualityOfRelease=Quality of the movie file , eg . HDTV 720p. StructureBuilderDialog.Structure=Structure -StructureBuilderDialog.SystemdependendSeparator=System independent separation for new directory , eg \\. +StructureBuilderDialog.SystemDependentSeparator=System independent separation for new directory , eg \\. StructureFilePanel.AddLanguage=Add new language StructureFilePanel.Delete=Delete SubtitleTableColumnName.Filename=Filename -SubtitleTableColumnName.hearingimpaired=hearing Impaired +SubtitleTableColumnName.hearingImpaired=hearing Impaired SubtitleTableColumnName.Quality=Quality SubtitleTableColumnName.Releasegroup=Release Group SubtitleTableColumnName.Score=Score % diff --git a/SubLibrary/src/main/resources/messages_nl.properties b/SubLibrary/src/main/resources/messages_nl.properties index 246ac73b..18a5e8a4 100644 --- a/SubLibrary/src/main/resources/messages_nl.properties +++ b/SubLibrary/src/main/resources/messages_nl.properties @@ -79,7 +79,7 @@ App.OptionDryRun=Gebruik maken van dry run om enkel de beschikbare ondertitels w App.OptionHelpMsg=Dit bericht tonen App.OptionNoGuiMsg=uitvoeren in CLI modus App.OptionOptionDebugMsg=Logging beschikbaar maken -App.OptionOptionDownloadAllMsg=Download alle gevonden ondertitels gebruik makend van '-v1' systeem +App.OptionOptionDownloadAllMsg=Download alle gevonden ondertitels gebruikmakend van '-v1' systeem App.OptionOptionFolderMsg=Zoek in map App.OptionOptionForceMsg=Overschrijf reeds bestaande ondertitels App.OptionOptionImportPreferencesMsg=Importeer voorkeuren @@ -164,7 +164,7 @@ Menu.Path=Bestand Menu.Preferences=Voorkeuren Menu.RenameMovie=Films Hernoemen Menu.RenameSerie=Series Hernoemen -Menu.SearchResults=Zoek Resulaten +Menu.SearchResults=Zoek Resultaten Menu.SerieNames=Serie Namen Menu.Title=Titel Menu.Type=Type @@ -196,7 +196,7 @@ PreferenceDialog.IncludeLanguageInFileName=Taal in bestandsnaam van ondertitel p PreferenceDialog.IncludeSourceInFileName=Bron in bestandsnaam van ondertitel plaatsen PreferenceDialog.invalidInput=Ongeldige invoer gedetecteerd. Gelieve dit te verbeteren alvorens verder te gaan. PreferenceDialog.Language=Taal -PreferenceDialog.LibraryFolder=Bibiliotheek map +PreferenceDialog.LibraryFolder=Bibliotheek map PreferenceDialog.Local=Lokaal PreferenceDialog.LocalFolderWithSubtitles=Lokale mappen met ondertitels PreferenceDialog.Location=Locatie @@ -204,7 +204,7 @@ PreferenceDialog.MinAutomaticScoreSelection=Minimale score nodig voor automatisc PreferenceDialog.MoveToLibrary=Verplaats naar bibliotheek PreferenceDialog.MovieLibrary=Film Bibliotheek PreferenceDialog.Name=MultiSubDownloader -PreferenceDialog.NewUpdateCheck=Update contole frequentie +PreferenceDialog.NewUpdateCheck=Update controle frequentie PreferenceDialog.OpenSubtitlesLogin=OpenSubtitles login PreferenceDialog.Options=Opties PreferenceDialog.Password=Wachtwoord @@ -247,7 +247,7 @@ Prompter.EnterImdbIdForSerie=Voer de juiste IMDB id in voor serie [%s] Prompter.SelectAddic7edMatchForSerie=Selecteer de juiste Addic7ed match voor serie [%s] Prompter.SelectImdbMatchForSerie=Selecteer de juiste IMDB match voor serie [%s] Prompter.SelectTvdbMatchForSerie=Selecteer de juiste tvdb match voor serie [%s] -Prompter.SpecialValue.SkipForever=-- Niet meer zoeken -- +Prompter.SpecialValue.SkipForever=-- Niet meer zoeken -- Prompter.SpecialValue.SkipOneWeek=-- Zoeken een week overslaan -- Prompter.SpecialValue.SkipTwoWeek=-- Zoeken twee weken overslaan -- Prompter.ValueIsNotValid=De ingevoerde waarde is niet geldig, probeer opnieuw @@ -291,7 +291,7 @@ StructureBuilderDialog.MovieName=Naam van de film. StructureBuilderDialog.MovieYear=Jaar waarin de film is uitgekomen. StructureBuilderDialog.NameTvShow=Naam van de tv serie. StructureBuilderDialog.NumberOfEpisodeLeadingZero=Nummer van de aflevering (met 0). -StructureBuilderDialog.NumberOfEpisodeLeadingZeroForMultipe=Nummer van de aflevering (met 0) voor multi episode. +StructureBuilderDialog.NumberOfEpisodeLeadingZeroForMultiple=Nummer van de aflevering (met 0) voor multi episode. StructureBuilderDialog.NumberOfEpisodeLeadingZeroMultiple=Nummer van de aflevering (zonder 0) voor multi episode. StructureBuilderDialog.NumberOfEpisodeWithoutLeadingZero=Nummer van de aflevering (zonder 0). StructureBuilderDialog.NumberOfSeasonLeading=Nummer van het seizoen (met 0). @@ -300,11 +300,11 @@ StructureBuilderDialog.Preview=Voorbeeld StructureBuilderDialog.QualityOfMovie=Kwaliteit van het filmbestand, bijv. BluRay 720p. StructureBuilderDialog.QualityOfRelease=Kwaliteit van het filmbestand, bijv. HDTV 720p. StructureBuilderDialog.Structure=Structuur -StructureBuilderDialog.SystemdependendSeparator=Systeemonafhankelijk scheidingsteken voor nieuwe directory, bijv. \\. +StructureBuilderDialog.SystemDependentSeparator=Systeemonafhankelijk scheidingsteken voor nieuwe directory, bijv. \\. StructureFilePanel.AddLanguage=Voeg nieuwe taal toe StructureFilePanel.Delete=Verwijder SubtitleTableColumnName.Filename=Bestandsnaam -SubtitleTableColumnName.hearingimpaired=Slechthorend +SubtitleTableColumnName.hearingImpaired=Slechthorend SubtitleTableColumnName.Quality=Kwaliteit SubtitleTableColumnName.Releasegroup=Release groep SubtitleTableColumnName.Score=Score % diff --git a/SubLibrary/src/main/resources/opensubtitles/open-api.json b/SubLibrary/src/main/resources/opensubtitles/open-api.json deleted file mode 100644 index bf7b724f..00000000 --- a/SubLibrary/src/main/resources/opensubtitles/open-api.json +++ /dev/null @@ -1,2067 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "OpenSubtitles API", - "description": "Explore subtitles API here", - "contact": { - "name": "OpenSubtitles API Support", - "url": "http://www.opensubtitles.com/en/contact/", - "email": "support@opensubtitles.org" - }, - "license": { - "name": "MIT", - "url": "https://opensource.org/licenses/MIT" - }, - "version": "1.0.1" - }, - "servers": [ - { - "url": "https://api.opensubtitles.com/api/v1" - } - ], - "tags": [ - { - "name": "Infos", - "description": "General API infos" - }, - { - "name": "Authentication", - "description": "Authentification of user" - }, - { - "name": "Discover", - "description": "Discover popular, latest and most downloaded subtitles" - }, - { - "name": "Download", - "description": "Download of subtitles" - }, - { - "name": "Subtitles", - "description": "Subtitles search for a specific release" - }, - { - "name": "Features", - "description": "Search for feature" - }, - { - "name": "Utilities", - "description": "Various utilities" - } - ], - "paths": { - "/login": { - "post": { - "tags": [ - "Authentication" - ], - "summary": "Login", - "description": "Create a token to authenticate a user. If response code is ```401 Unathorized``` stop sending further requests with same credentials.\n\nRequest limit is set 1 request per 1 second.", - "operationId": "login", - "parameters": [ - { - "name": "Content-Type", - "in": "header", - "description": "application/json", - "required": true, - "schema": { - "type": "string", - "default": "application/json" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "required": [ - "password", - "username" - ], - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - }, - "description": "" - } - } - }, - "required": false - }, - "responses": { - "200": { - "description": "Create session and token", - "content": { - "application/json": { - "schema": { - "required": [ - "status", - "token", - "user" - ], - "type": "object", - "properties": { - "user": { - "required": [ - "allowed_downloads", - "ext_installed", - "level", - "user_id", - "vip" - ], - "type": "object", - "properties": { - "allowed_translations": { - "type": "number" - }, - "allowed_downloads": { - "type": "number" - }, - "level": { - "minLength": 1, - "type": "string" - }, - "user_id": { - "type": "number" - }, - "ext_installed": { - "type": "boolean" - }, - "vip": { - "type": "boolean" - } - } - }, - "token": { - "minLength": 1, - "type": "string" - }, - "status": { - "type": "number" - } - }, - "description": "" - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/logout": { - "delete": { - "tags": [ - "Authentication" - ], - "summary": "Logout", - "description": "Destroy a user token to end a session. Bearer token is required for this endpoint.", - "operationId": "logout", - "responses": { - "200": { - "description": "Destroy session and current token", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Bearer": [] - }, - { - "Api-Key": [] - } - ] - } - }, - "/infos/formats": { - "get": { - "tags": [ - "Infos" - ], - "summary": "Subtitle Formats", - "description": "List subtitle formats recognized by the API ", - "operationId": "formats", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "description": "", - "type": "object", - "properties": { - "data": { - "type": "object", - "required": [ - "output_formats" - ], - "properties": { - "output_formats": { - "type": "array", - "items": {} - } - } - } - }, - "required": [ - "data" - ] - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/infos/languages": { - "get": { - "tags": [ - "Infos" - ], - "summary": "Languages", - "description": "Get the languages information", - "operationId": "languages", - "responses": { - "200": { - "description": "Get the languages table containing the codes and names used through the API", - "content": { - "application/json": { - "schema": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "required": [ - "language_code", - "language_name" - ], - "type": "object", - "properties": { - "language_code": { - "minLength": 1, - "type": "string" - }, - "language_name": { - "minLength": 1, - "type": "string" - } - } - } - } - }, - "description": "" - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/infos/user": { - "get": { - "tags": [ - "Infos" - ], - "summary": "User Informations", - "description": "Gather informations about the user authenticated by a bearer token. User information are already sent when user is authenticated, and the remaining downloads is returned with each download, but you can also get these information here.", - "operationId": "userinfo", - "responses": { - "200": { - "description": "Get user data", - "content": { - "application/json": { - "schema": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "allowed_downloads": { - "type": "number" - }, - "level": { - "minLength": 1, - "type": "string" - }, - "user_id": { - "type": "number" - }, - "ext_installed": { - "type": "boolean" - }, - "vip": { - "type": "boolean" - }, - "downloads_count": { - "type": "number" - }, - "remaining_downloads": { - "type": "number" - } - } - } - }, - "description": "" - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Bearer": [] - }, - { - "Api-Key": [] - } - ] - } - }, - "/discover/popular": { - "get": { - "tags": [ - "Discover" - ], - "summary": "Popular features", - "description": "Discover popular features on opensubtitles.com, according to last 30 days downloads.", - "operationId": "popular", - "parameters": [ - { - "name": "languages", - "in": "query", - "description": "Language code(s), coma separated (en,fr) or \"all\"", - "schema": { - "type": "string" - } - }, - { - "name": "type", - "in": "query", - "description": "Type (movie or tvshow)", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Subtitle" - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/discover/latest": { - "get": { - "tags": [ - "Discover" - ], - "summary": "Latest subtitles", - "description": "Lists 60 latest uploaded subtitles", - "operationId": "latest", - "parameters": [ - { - "name": "languages", - "in": "query", - "description": "Language code(s), coma separated (en,fr) or \"all\"", - "schema": { - "type": "string" - } - }, - { - "name": "type", - "in": "query", - "description": "Type (movie or tvshow)", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "required": [ - "data", - "page", - "total_count", - "total_pages" - ], - "type": "object", - "properties": { - "total_pages": { - "type": "number" - }, - "total_count": { - "type": "number" - }, - "page": { - "type": "number" - }, - "data": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "id": { - "minLength": 1, - "type": "string" - }, - "type": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "type": "object", - "properties": { - "subtitle_id": { - "minLength": 1, - "type": "string" - }, - "language": { - "minLength": 1, - "type": "string" - }, - "download_count": { - "type": "number" - }, - "new_download_count": { - "type": "number" - }, - "hearing_impaired": { - "type": "boolean" - }, - "hd": { - "type": "boolean" - }, - "format": { - "type": "object" - }, - "fps": { - "type": "number" - }, - "votes": { - "type": "number" - }, - "points": { - "type": "number" - }, - "ratings": { - "type": "number" - }, - "from_trusted": { - "type": "boolean" - }, - "foreign_parts_only": { - "type": "boolean" - }, - "ai_translated": { - "type": "boolean" - }, - "machine_translated": { - "type": "object" - }, - "upload_date": { - "minLength": 1, - "type": "string" - }, - "release": { - "minLength": 1, - "type": "string" - }, - "legacy_subtitle_id": { - "type": "number" - }, - "uploader": { - "required": [ - "name", - "rank", - "uploader_id" - ], - "type": "object", - "properties": { - "uploader_id": { - "type": "number" - }, - "name": { - "minLength": 1, - "type": "string" - }, - "rank": { - "minLength": 1, - "type": "string" - } - } - }, - "feature_details": { - "type": "object", - "properties": { - "feature_id": { - "type": "number" - }, - "feature_type": { - "minLength": 1, - "type": "string" - }, - "year": { - "type": "number" - }, - "title": { - "minLength": 1, - "type": "string" - }, - "movie_name": { - "minLength": 1, - "type": "string" - }, - "imdb_id": { - "type": "number" - }, - "tmdb_id": { - "type": "object" - } - } - }, - "url": { - "minLength": 1, - "type": "string" - }, - "related_links": { - "required": [ - "img_url", - "label", - "url" - ], - "type": "object", - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "url": { - "minLength": 1, - "type": "string" - }, - "img_url": { - "minLength": 1, - "type": "string" - } - } - }, - "files": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "required": [ - "cd_number", - "file_id", - "file_name" - ], - "type": "object", - "properties": { - "file_id": { - "type": "number" - }, - "cd_number": { - "type": "number" - }, - "file_name": { - "minLength": 1, - "type": "string" - } - } - } - } - } - } - } - } - } - }, - "description": "" - } - } - } - } - }, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/discover/most_downloaded": { - "get": { - "tags": [ - "Discover" - ], - "summary": "Most downloaded subtitles", - "description": "Discover popular subtitles, according to last 30 days downloads on opensubtitles.com. This list can be filtered by language code or feature type (movie, episode)", - "operationId": "most_downloaded", - "parameters": [ - { - "name": "languages", - "in": "query", - "description": "Language code(s), coma separated (en,fr), or \"all\"", - "schema": { - "type": "string" - } - }, - { - "name": "type", - "in": "query", - "description": "Type (movie or tvshow)", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Lists most downloaded movie subtitles ", - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "", - "properties": { - "total_pages": { - "type": "number" - }, - "total_count": { - "type": "number" - }, - "page": { - "type": "number" - }, - "data": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "$ref": "#/components/schemas/Subtitle" - } - } - }, - "required": [ - "total_pages", - "total_count", - "page", - "data" - ] - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/features": { - "get": { - "tags": [ - "Features" - ], - "summary": "Search for features", - "description": "With the \"query\" parameter, search for a Feature from a simple text input. Typically used for a text search or autocomplete.\n\nWith an ID, get basic information and subtitles count for a specific title.\n\n\n\n> ### Watch Out!\n>\n> If you create an autocomplete, don't set a too small refresh limit, remember you must not go over 40 requests per 10 seconds!", - "operationId": "features", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "query to search, release/file name accepted", - "schema": { - "minLength": 3, - "type": "string" - } - }, - { - "name": "type", - "in": "query", - "description": "empty to list all or **movie**, **tvshow** or **episode**.", - "schema": { - "type": "string" - } - }, - { - "name": "feature_id", - "in": "query", - "description": "opensubtitles **feature_id**", - "schema": { - "type": "integer" - } - }, - { - "name": "imdb_id", - "in": "query", - "description": "IMDB ID, delete leading zeroes", - "schema": { - "type": "string" - } - }, - { - "name": "tmdb_id", - "in": "query", - "description": "TheMovieDB ID - combine with type to avoid errors", - "schema": { - "type": "string" - } - }, - { - "name": "year", - "in": "query", - "description": "Filter by year. Can only be used in combination with a query", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Search for a feature", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "movie": { - "$ref": "#/components/schemas/Feature-Movie" - }, - "episode": { - "$ref": "#/components/schemas/Feature-Episode" - }, - "tv": { - "$ref": "#/components/schemas/Feature-Tvshow" - } - }, - "description": "" - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/subtitles": { - "get": { - "tags": [ - "Subtitles" - ], - "summary": "Search for subtitles", - "description": "Find subtitle for a video file. All parameters can be combined following various logics: searching by a specific external id (imdb, tmdb), a file moviehash, or a simple text query.\n\n\n> Something wrong? Read about [common mistakes and best practices](docs/2-Best-Practices.md)\n\nImplement the logic that best fit your needs, remembering that:\n- if you can get moviehash from file - send it\n- if you know the ID, IMDB or TMDB, send this rather than a query, an ID will always be more precise\n- if you search for TV show episodes, sending the parent ID, episode and season number will give best results\n- send the filename as query parameter together with the moviehash for better results. If your filenames are generally irrelevant, for example a dynamically generated filename from a streaming service, no need to send it.\n- you can combine all together (send everything what you have and we will take of rest)\n\n\n> Avoid http redirection by sending request parameters sorted and without default values, and send all queries in lowercase. Remove leading zeroes in IMDB ID\n\n### Moviehash \nIf a moviehash is sent with a request, a \"moviehash_match\" boolean field will be added to the response.\n\nThe matching subtitles will always come first in the response.\n\n\n### Ordering\nYou can order the results using the *order_by* parameter. Ordering is possible on the following fields:\n_ language, download_count, new_download_count, hearing_impaired, hd, format, fps, votes, points, ratings, from_trusted, foreign_parts_only, ai_translated, machine_translated, upload_date, release, comments _\n\nChange the order direction with *order_direction* (asc/desc)\n\n", - "operationId": "subtitles", - "parameters": [ - { - "name": "id", - "in": "query", - "description": "ID of the movie or episode", - "schema": { - "type": "integer" - } - }, - { - "name": "imdb_id", - "in": "query", - "description": "IMDB ID of the movie or episode", - "schema": { - "type": "integer" - } - }, - { - "name": "tmdb_id", - "in": "query", - "description": "TMDB ID of the movie or episode", - "schema": { - "type": "integer" - } - }, - { - "name": "type", - "in": "query", - "description": "movie, episode or all, (default: all) ", - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "file name or text search", - "schema": { - "type": "string" - } - }, - { - "name": "languages", - "in": "query", - "description": "Language code(s), coma separated (en,fr)", - "schema": { - "type": "string" - } - }, - { - "name": "moviehash", - "in": "query", - "description": "Moviehash of the movie", - "schema": { - "maxLength": 16, - "minLength": 16, - "pattern": "^[a-f0-9]{16}$", - "type": "string" - } - }, - { - "name": "user_id", - "in": "query", - "description": "To be used alone - for user uploads listing", - "schema": { - "type": "integer" - } - }, - { - "name": "hearing_impaired", - "in": "query", - "description": "include, exclude, only. (default: include)", - "schema": { - "type": "string" - } - }, - { - "name": "foreign_parts_only", - "in": "query", - "description": "exclude, include, only (default: include)", - "schema": { - "type": "string" - } - }, - { - "name": "trusted_sources", - "in": "query", - "description": "include, only (default: include)", - "schema": { - "type": "string" - } - }, - { - "name": "machine_translated", - "in": "query", - "description": "exclude, include (default: exclude)", - "schema": { - "type": "string" - } - }, - { - "name": "ai_translated", - "in": "query", - "description": "exclude, include (default: exclude)", - "schema": { - "type": "string" - } - }, - { - "name": "order_by", - "in": "query", - "description": "Order of the returned results, accept any of above fields", - "schema": { - "type": "string" - } - }, - { - "name": "order_direction", - "in": "query", - "description": "Order direction of the returned results (asc,desc)", - "schema": { - "type": "string" - } - }, - { - "name": "parent_feature_id", - "in": "query", - "description": "For Tvshows", - "schema": { - "type": "integer" - } - }, - { - "name": "parent_imdb_id", - "in": "query", - "description": "For Tvshows", - "schema": { - "type": "integer" - } - }, - { - "name": "parent_tmdb_id", - "in": "query", - "description": "For Tvshows", - "schema": { - "type": "integer" - } - }, - { - "name": "season_number", - "in": "query", - "description": "For Tvshows\n", - "schema": { - "type": "integer" - } - }, - { - "name": "episode_number", - "in": "query", - "description": "For Tvshows", - "schema": { - "type": "integer" - } - }, - { - "name": "year", - "in": "query", - "description": "Filter by movie/episode year", - "schema": { - "type": "integer" - } - }, - { - "name": "moviehash_match", - "in": "query", - "description": "include, only (default: include)", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "in": "query", - "description": "Results page to display", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Find subtitles for a video file ", - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "", - "properties": { - "total_pages": { - "type": "number" - }, - "total_count": { - "type": "number" - }, - "page": { - "type": "number" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Subtitle" - } - } - }, - "required": [ - "total_pages", - "total_count", - "page", - "data" - ] - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Api-Key": [] - } - ] - } - }, - "/download": { - "post": { - "tags": [ - "Download" - ], - "summary": "Download", - "description": "Request a download url for a subtitle. \n\n\n> The download count is calculated on this action, not the file download itself\n\n> IN and OUT FPS must be indicated for subtitle conversions, we want to make sure you know what you are doing, and therefore collected the current FPS from the subtitle search result, or calculated it somehow.\n\n\n\n> The download URL is temporary, and cannot be used more than 3 hours, so do not cache it, but you can download the file more than once if needed.", - "operationId": "download", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "", - "properties": { - "file_id": { - "type": "integer", - "description": "file_id from /subtitles search results", - "format": "int32", - "example": 123 - }, - "sub_format": { - "type": "string", - "description": "from /infos/formats" - }, - "file_name": { - "type": "string", - "description": "desired file name" - }, - "in_fps": { - "type": "number", - "description": "used for conversions, in_fps and out_fps must then be indicated" - }, - "out_fps": { - "type": "number", - "description": "used for conversions, in_fps and out_fps must then be indicated" - }, - "timeshift": { - "type": "number", - "description": "" - }, - "force_download": { - "type": "boolean", - "description": "(1/0) set subtitle file headers to \"application/force-download\"" - } - }, - "required": [ - "file_id" - ] - } - } - }, - "required": false, - "description": "" - }, - "responses": { - "200": { - "description": "Request a download url for a subtitle. \n", - "content": { - "application/json": { - "schema": { - "description": "", - "type": "object", - "properties": { - "link": { - "type": "string", - "minLength": 1 - }, - "file_name": { - "type": "string", - "minLength": 1 - }, - "requests": { - "type": "number" - }, - "remaining": { - "type": "number" - }, - "message": { - "type": "string", - "minLength": 1 - }, - "reset_time": { - "type": "string", - "minLength": 1 - }, - "reset_time_utc": { - "type": "string", - "minLength": 1 - } - } - } - } - } - } - }, - "deprecated": false, - "security": [ - { - "Bearer": [] - }, - { - "Api-Key": [] - } - ], - "x-codegen-request-body-name": "body" - }, - "parameters": [] - }, - "/utilities/guessit": { - "get": { - "tags": [ - "Utilities" - ], - "summary": "Guessit", - "description": "Extracts as much information as possible from a video filename.\n\nIt has a very powerful matcher that allows to guess properties from a video using its filename only. This matcher works with both movies and tv shows episodes.\n\nThis is a simple implementation of the python guessit library.\nhttps://doc.guessit.io\n\nFind examples of the returned data.\nhttps://doc.guessit.io/properties/", - "operationId": "guessit", - "parameters": [ - { - "name": "filename", - "in": "query", - "description": "File name", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "title": { - "minLength": 1, - "type": "string" - }, - "year": { - "type": "number" - }, - "language": { - "minLength": 1, - "type": "string" - }, - "subtitle_language": { - "minLength": 1, - "type": "string" - }, - "screen_size": { - "minLength": 1, - "type": "string" - }, - "streaming_service": { - "minLength": 1, - "type": "string" - }, - "source": { - "minLength": 1, - "type": "string" - }, - "other": { - "minLength": 1, - "type": "string" - }, - "audio_codec": { - "minLength": 1, - "type": "string" - }, - "audio_channels": { - "minLength": 1, - "type": "string" - }, - "video_codec": { - "minLength": 1, - "type": "string" - }, - "release_group": { - "minLength": 1, - "type": "string" - }, - "type": { - "minLength": 1, - "type": "string" - } - }, - "description": "" - } - } - } - } - }, - "security": [ - { - "Api-Key": [] - } - ] - } - } - }, - "components": { - "schemas": { - "Subtitle": { - "title": "", - "type": "object", - "description": "", - "x-tags": [ - "Models" - ], - "properties": { - "id": { - "minLength": 1, - "type": "string" - }, - "type": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "type": "object", - "properties": { - "subtitle_id": { - "minLength": 1, - "type": "string" - }, - "language": { - "minLength": 1, - "type": "string" - }, - "download_count": { - "type": "number" - }, - "new_download_count": { - "type": "number" - }, - "hearing_impaired": { - "type": "boolean" - }, - "hd": { - "type": "boolean" - }, - "format": { - "type": "string" - }, - "fps": { - "type": "number" - }, - "votes": { - "type": "number" - }, - "points": { - "type": "number" - }, - "ratings": { - "type": "number" - }, - "from_trusted": { - "type": "boolean" - }, - "foreign_parts_only": { - "type": "boolean" - }, - "ai_translated": { - "type": "boolean" - }, - "machine_translated": { - "type": "boolean" - }, - "upload_date": { - "minLength": 1, - "type": "string" - }, - "release": { - "minLength": 1, - "type": "string" - }, - "legacy_subtitle_id": { - "type": "number" - }, - "uploader": { - "type": "object", - "required": [ - "name", - "rank" - ], - "properties": { - "uploader_id": { - "type": "number" - }, - "name": { - "type": "string" - }, - "rank": { - "type": "string" - } - } - }, - "feature_details": { - "type": "object", - "properties": { - "feature_id": { - "type": "number" - }, - "feature_type": { - "minLength": 1, - "type": "string" - }, - "year": { - "type": "number" - }, - "title": { - "minLength": 1, - "type": "string" - }, - "movie_name": { - "minLength": 1, - "type": "string" - }, - "imdb_id": { - "type": "number" - }, - "tmdb_id": { - "type": "number" - } - } - }, - "url": { - "minLength": 1, - "type": "string" - }, - "related_links": { - "type": "array", - "items": { - "type": "object" - } - }, - "files": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "type": "object", - "properties": { - "file_id": { - "type": "number" - }, - "cd_number": { - "type": "number" - }, - "file_name": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "file_id", - "cd_number", - "file_name" - ] - } - } - } - } - }, - "required": [ - "id", - "type", - "attributes" - ] - }, - "Feature-Tvshow": { - "required": [ - "attributes", - "id", - "type" - ], - "type": "object", - "properties": { - "id": { - "minLength": 1, - "type": "string" - }, - "type": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "type": "object", - "properties": { - "title": { - "minLength": 1, - "type": "string" - }, - "original_title": { - "minLength": 1, - "type": "string" - }, - "year": { - "minLength": 1, - "type": "string" - }, - "imdb_id": { - "type": "number" - }, - "tmdb_id": { - "type": "number" - }, - "title_aka": { - "type": "array", - "items": { - "type": "object", - "properties": {} - } - }, - "feature_id": { - "minLength": 1, - "type": "string" - }, - "url": { - "minLength": 1, - "type": "string" - }, - "img_url": { - "minLength": 1, - "type": "string" - }, - "subtitles_counts": { - "type": "object", - "properties": { - "pl": { - "type": "number" - }, - "en": { - "type": "number" - }, - "pt-BR": { - "type": "number" - }, - "ro": { - "type": "number" - }, - "nl": { - "type": "number" - }, - "pt-PT": { - "type": "number" - }, - "es": { - "type": "number" - }, - "he": { - "type": "number" - }, - "hu": { - "type": "number" - }, - "el": { - "type": "number" - }, - "fr": { - "type": "number" - }, - "tr": { - "type": "number" - }, - "cs": { - "type": "number" - }, - "fi": { - "type": "number" - }, - "ar": { - "type": "number" - }, - "hr": { - "type": "number" - }, - "sl": { - "type": "number" - }, - "bg": { - "type": "number" - }, - "sr": { - "type": "number" - }, - "sv": { - "type": "number" - }, - "de": { - "type": "number" - }, - "et": { - "type": "number" - }, - "da": { - "type": "number" - }, - "bs": { - "type": "number" - }, - "it": { - "type": "number" - }, - "mk": { - "type": "number" - }, - "ru": { - "type": "number" - }, - "no": { - "type": "number" - }, - "th": { - "type": "number" - }, - "vi": { - "type": "number" - }, - "ja": { - "type": "number" - }, - "fa": { - "type": "number" - }, - "zh-CN": { - "type": "number" - }, - "ca": { - "type": "number" - }, - "id": { - "type": "number" - }, - "sk": { - "type": "number" - }, - "ko": { - "type": "number" - }, - "zh-TW": { - "type": "number" - } - } - }, - "subtitles_count": { - "type": "number" - }, - "seasons": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "required": [ - "season_number" - ], - "type": "object", - "properties": { - "season_number": { - "type": "number" - }, - "episodes": { - "minItems": 1, - "uniqueItems": true, - "type": "array", - "items": { - "type": "object", - "properties": { - "episode_number": { - "type": "number" - }, - "title": { - "minLength": 1, - "type": "string" - }, - "feature_id": { - "type": "number" - }, - "feature_imdb_id": { - "type": "number" - } - } - } - } - } - } - } - } - } - }, - "description": "", - "x-tags": [ - "Models" - ] - }, - "Feature-Episode": { - "required": [ - "attributes", - "id", - "type" - ], - "type": "object", - "properties": { - "id": { - "minLength": 1, - "type": "string" - }, - "type": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "type": "object", - "properties": { - "title": { - "minLength": 1, - "type": "string" - }, - "original_title": { - "type": "object" - }, - "year": { - "minLength": 1, - "type": "string" - }, - "parent_imdb_id": { - "type": "object" - }, - "parent_title": { - "minLength": 1, - "type": "string" - }, - "season_number": { - "type": "number" - }, - "episode_number": { - "type": "number" - }, - "imdb_id": { - "type": "number" - }, - "tmdb_id": { - "type": "number" - }, - "title_aka": { - "type": "array", - "items": { - "type": "object", - "properties": {} - } - }, - "feature_id": { - "minLength": 1, - "type": "string" - }, - "url": { - "minLength": 1, - "type": "string" - }, - "img_url": { - "minLength": 1, - "type": "string" - }, - "subtitles_counts": { - "type": "object", - "properties": { - "pl": { - "type": "number" - }, - "en": { - "type": "number" - }, - "pt-BR": { - "type": "number" - }, - "es": { - "type": "number" - }, - "ro": { - "type": "number" - }, - "nl": { - "type": "number" - }, - "tr": { - "type": "number" - }, - "he": { - "type": "number" - }, - "pt-PT": { - "type": "number" - }, - "cs": { - "type": "number" - }, - "fi": { - "type": "number" - }, - "hu": { - "type": "number" - }, - "ar": { - "type": "number" - }, - "bg": { - "type": "number" - }, - "fr": { - "type": "number" - }, - "sl": { - "type": "number" - }, - "el": { - "type": "number" - }, - "hr": { - "type": "number" - }, - "sr": { - "type": "number" - }, - "et": { - "type": "number" - }, - "sv": { - "type": "number" - }, - "th": { - "type": "number" - }, - "bs": { - "type": "number" - }, - "da": { - "type": "number" - }, - "de": { - "type": "number" - }, - "mk": { - "type": "number" - }, - "no": { - "type": "number" - }, - "ru": { - "type": "number" - } - } - }, - "subtitles_count": { - "type": "number" - } - } - } - }, - "description": "", - "x-tags": [ - "Models" - ] - }, - "Feature-Movie": { - "required": [ - "attributes", - "id", - "type" - ], - "type": "object", - "properties": { - "id": { - "minLength": 1, - "type": "string" - }, - "type": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "type": "object", - "properties": { - "title": { - "minLength": 1, - "type": "string" - }, - "original_title": { - "minLength": 1, - "type": "string" - }, - "year": { - "minLength": 1, - "type": "string" - }, - "subtitles_counts": { - "type": "object", - "properties": { - "en": { - "type": "number" - }, - "pt-PT": { - "type": "number" - }, - "fi": { - "type": "number" - }, - "pt-BR": { - "type": "number" - }, - "es": { - "type": "number" - }, - "ar": { - "type": "number" - }, - "pl": { - "type": "number" - }, - "sr": { - "type": "number" - }, - "id": { - "type": "number" - }, - "ro": { - "type": "number" - }, - "zh-CN": { - "type": "number" - }, - "nl": { - "type": "number" - }, - "el": { - "type": "number" - }, - "hu": { - "type": "number" - }, - "fr": { - "type": "number" - }, - "sl": { - "type": "number" - }, - "tr": { - "type": "number" - }, - "et": { - "type": "number" - }, - "bg": { - "type": "number" - }, - "cs": { - "type": "number" - }, - "de": { - "type": "number" - }, - "he": { - "type": "number" - }, - "it": { - "type": "number" - }, - "vi": { - "type": "number" - }, - "hr": { - "type": "number" - }, - "ko": { - "type": "number" - }, - "no": { - "type": "number" - }, - "sv": { - "type": "number" - }, - "ta": { - "type": "number" - }, - "eu": { - "type": "number" - }, - "da": { - "type": "number" - }, - "fa": { - "type": "number" - }, - "sk": { - "type": "number" - }, - "uk": { - "type": "number" - }, - "zh-TW": { - "type": "number" - }, - "bn": { - "type": "number" - }, - "ka": { - "type": "number" - }, - "ja": { - "type": "number" - }, - "lt": { - "type": "number" - }, - "mk": { - "type": "number" - }, - "ml": { - "type": "number" - }, - "ms": { - "type": "number" - }, - "ru": { - "type": "number" - }, - "ze": { - "type": "number" - } - } - }, - "subtitles_count": { - "type": "number" - }, - "seasons_count": { - "type": "number" - }, - "parent_title": { - "type": "string" - }, - "season_number": { - "type": "number" - }, - "episode_number": { - "type": "object" - }, - "imdb_id": { - "type": "number" - }, - "tmdb_id": { - "type": "number" - }, - "parent_imdb_id": { - "type": "object" - }, - "feature_id": { - "minLength": 1, - "type": "string" - }, - "title_aka": { - "type": "array", - "items": { - "type": "object", - "properties": {} - } - }, - "feature_type": { - "minLength": 1, - "type": "string" - }, - "url": { - "minLength": 1, - "type": "string" - }, - "img_url": { - "minLength": 1, - "type": "string" - } - } - } - }, - "description": "", - "x-tags": [ - "Models" - ] - } - }, - "securitySchemes": { - "Api-Key": { - "type": "apiKey", - "description": "Application API key obtained in the user profile of app developer on opensubtitles.com to authorise the **application**", - "name": "Api-Key", - "in": "header" - }, - "Bearer": { - "type": "http", - "description": "User token created in the login endpoint to authorise opensubtitles.com **user**", - "scheme": "bearer", - "bearerFormat": "JWT" - } - } - }, - "security": [ - { - "Api-Key": [] - } - ] -} \ No newline at end of file diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/control/ReleaseParserTest.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/control/ReleaseParserTest.java index eb0f50e5..059a08c3 100644 --- a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/control/ReleaseParserTest.java +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/control/ReleaseParserTest.java @@ -1,7 +1,6 @@ package org.lodder.subtools.sublibrary.control; import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; import java.nio.file.Path; import java.util.List; @@ -16,17 +15,17 @@ class ReleaseParserTest { @Test - void testReleaseGroup() throws Exception { - String releaseGroup = ReleaseParser.extractReleasegroup("The.Following.S03E01.HDTV.XviD-AFG", false); + void testReleaseGroup() { + String releaseGroup = ReleaseParser.extractReleaseGroup("The.Following.S03E01.HDTV.XviD-AFG", false); assertThat(releaseGroup).isEqualTo("AFG"); - releaseGroup = ReleaseParser.extractReleasegroup("The.Following.S03E01.HDTV.XviD-AFG", true); + releaseGroup = ReleaseParser.extractReleaseGroup("The.Following.S03E01.HDTV.XviD-AFG", true); assertThat(releaseGroup).isEqualTo("A"); - releaseGroup = ReleaseParser.extractReleasegroup("The.Following.S03E01.HDTV.XviD-AFG.srt", false); - assertThat(releaseGroup).isEqualTo(""); + releaseGroup = ReleaseParser.extractReleaseGroup("The.Following.S03E01.HDTV.XviD-AFG.srt", false); + assertThat(releaseGroup).isEmpty(); - releaseGroup = ReleaseParser.extractReleasegroup("The.Following.S03E01.HDTV.XviD-AFG.srt", true); + releaseGroup = ReleaseParser.extractReleaseGroup("The.Following.S03E01.HDTV.XviD-AFG.srt", true); assertThat(releaseGroup).isEqualTo("AFG"); } @@ -37,14 +36,14 @@ void testListGetQualityKeyWords() throws Exception { Path file = Path.of("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv"); Release release = releaseparser.parse(file); - List q = ReleaseParser.getQualityKeyWords(release.getQuality()); + List q = ReleaseParser.getQualityKeyWords(release.quality); assertThat(q).containsExactly("720p", "hdtv", "x264"); file = Path.of("The.Drop.2014.1080p.WEB-DL.DD5.1.H264-RARBG.mkv"); release = releaseparser.parse(file); - q = ReleaseParser.getQualityKeyWords(release.getQuality()); + q = ReleaseParser.getQualityKeyWords(release.quality); assertThat(q).containsExactly("1080p", "web-dl", "dd5 1", "h264"); } @@ -56,83 +55,82 @@ void testTV() throws Exception { Path file = Path.of("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv"); Release release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.EPISODE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertThat(release.getFileName()).isEqualTo("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo("DIMENSION"); - assertThat(release.getQuality()).isEqualTo("720p hdtv x264"); + assertThat(release.videoType).isEqualTo(VideoType.EPISODE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.fileName).isEqualTo("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv"); + assertThat(release.releaseGroup).isEqualTo("DIMENSION"); + assertThat(release.quality).isEqualTo("720p hdtv x264"); TvRelease tvrelease = (TvRelease) release; - assertThat(tvrelease.getSeason()).isEqualTo(10); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).isEqualTo(12); + assertThat(tvrelease.season).isEqualTo(10); + assertThat(tvrelease.episodeNumbers).hasSize(1); + assertThat(tvrelease.firstEpisodeNumber).isEqualTo(12); file = Path.of("S04E02 - White Collar - Most Wanted.mkv"); release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.EPISODE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertThat(release.getFileName()).isEqualTo("S04E02 - White Collar - Most Wanted.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo(""); - assertThat(release.getQuality()).isEqualTo(""); + assertThat(release.videoType).isEqualTo(VideoType.EPISODE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.fileName).isEqualTo("S04E02 - White Collar - Most Wanted.mkv"); + assertThat(release.releaseGroup).isEmpty(); + assertThat(release.quality).isEmpty(); tvrelease = (TvRelease) release; - assertThat(tvrelease.getSeason()).isEqualTo(4); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).isEqualTo(2); + assertThat(tvrelease.season).isEqualTo(4); + assertThat(tvrelease.episodeNumbers).hasSize(1); + assertThat(tvrelease.firstEpisodeNumber).isEqualTo(2); file = Path.of("Spartacus.Gods.of.The.Arena.Pt.I.720p.HDTV.X264-DIMENSION.mkv"); release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.EPISODE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertEquals(release.getFileName(), - "Spartacus.Gods.of.The.Arena.Pt.I.720p.HDTV.X264-DIMENSION.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo("DIMENSION"); - assertThat(release.getQuality()).isEqualTo("720p hdtv x264"); + assertThat(release.videoType).isEqualTo(VideoType.EPISODE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.fileName).isEqualTo("Spartacus.Gods.of.The.Arena.Pt.I.720p.HDTV.X264-DIMENSION.mkv"); + assertThat(release.releaseGroup).isEqualTo("DIMENSION"); + assertThat(release.quality).isEqualTo("720p hdtv x264"); tvrelease = (TvRelease) release; - assertThat(tvrelease.getSeason()).isEqualTo(1); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).isEqualTo(1); + assertThat(tvrelease.season).isEqualTo(1); + assertThat(tvrelease.episodeNumbers).hasSize(1); + assertThat(tvrelease.firstEpisodeNumber).isEqualTo(1); file = Path.of("hawaii.five-0.2010.410.hdtv-lol.mp4"); release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.EPISODE); - assertThat(release.getExtension()).isEqualTo("mp4"); - assertThat(release.getFileName()).isEqualTo("hawaii.five-0.2010.410.hdtv-lol.mp4"); - assertThat(release.getReleaseGroup()).isEqualTo("lol"); - assertThat(release.getQuality()).isEqualTo("hdtv"); + assertThat(release.videoType).isEqualTo(VideoType.EPISODE); + assertThat(release.extension).isEqualTo("mp4"); + assertThat(release.fileName).isEqualTo("hawaii.five-0.2010.410.hdtv-lol.mp4"); + assertThat(release.releaseGroup).isEqualTo("lol"); + assertThat(release.quality).isEqualTo("hdtv"); tvrelease = (TvRelease) release; - assertThat(tvrelease.getSeason()).isEqualTo(4); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).isEqualTo(10); + assertThat(tvrelease.season).isEqualTo(4); + assertThat(tvrelease.episodeNumbers).hasSize(1); + assertThat(tvrelease.firstEpisodeNumber).isEqualTo(10); file = Path.of("Greys.Anatomy.S10E01E02.720p.HDTV.X264-DIMENSION.mkv"); release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.EPISODE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertThat(release.getFileName()).isEqualTo("Greys.Anatomy.S10E01E02.720p.HDTV.X264-DIMENSION.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo("DIMENSION"); - assertThat(release.getQuality()).isEqualTo("720p hdtv x264"); + assertThat(release.videoType).isEqualTo(VideoType.EPISODE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.fileName).isEqualTo("Greys.Anatomy.S10E01E02.720p.HDTV.X264-DIMENSION.mkv"); + assertThat(release.releaseGroup).isEqualTo("DIMENSION"); + assertThat(release.quality).isEqualTo("720p hdtv x264"); tvrelease = (TvRelease) release; - assertThat(tvrelease.getSeason()).isEqualTo(10); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(2); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(1)).isEqualTo(2); + assertThat(tvrelease.season).isEqualTo(10); + assertThat(tvrelease.episodeNumbers).hasSize(2); + assertThat(tvrelease.firstEpisodeNumber).isEqualTo(1); + assertThat(tvrelease.episodeNumbers.get(1)).isEqualTo(2); } @Test - void testReleaseParseExceptionMessage() throws ReleaseParseException { + void testReleaseParseExceptionMessage() { Path file = Path.of("exceptiontesting.mkv"); assertThatExceptionOfType(ReleaseParseException.class) @@ -147,44 +145,43 @@ void testMovie() throws Exception { Path file = Path.of("Back.to.the.Future.Part.II.1989.720p.BluRay.X264-AMIABLE.mkv"); Release release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.MOVIE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertThat(release.getFileName()).isEqualTo("Back.to.the.Future.Part.II.1989.720p.BluRay.X264-AMIABLE.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo("AMIABLE"); - assertThat(release.getQuality()).isEqualTo("720p bluray x264"); + assertThat(release.videoType).isEqualTo(VideoType.MOVIE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.fileName).isEqualTo("Back.to.the.Future.Part.II.1989.720p.BluRay.X264-AMIABLE.mkv"); + assertThat(release.releaseGroup).isEqualTo("AMIABLE"); + assertThat(release.quality).isEqualTo("720p bluray x264"); MovieRelease movieRelease = (MovieRelease) release; - assertThat((int) movieRelease.getYear()).isEqualTo(1989); - assertThat(movieRelease.getName()).isEqualTo("Back to the Future Part II"); + assertThat(movieRelease.year).isEqualTo(1989); + assertThat(movieRelease.name).isEqualTo("Back to the Future Part II"); file = Path.of("The.Equalizer.2014.720p.BluRay.x264-SPARKS.mkv"); release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.MOVIE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertThat(release.getFileName()).isEqualTo("The.Equalizer.2014.720p.BluRay.x264-SPARKS.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo("SPARKS"); - assertThat(release.getQuality()).isEqualTo("720p bluray x264"); + assertThat(release.videoType).isEqualTo(VideoType.MOVIE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.fileName).isEqualTo("The.Equalizer.2014.720p.BluRay.x264-SPARKS.mkv"); + assertThat(release.releaseGroup).isEqualTo("SPARKS"); + assertThat(release.quality).isEqualTo("720p bluray x264"); movieRelease = (MovieRelease) release; - assertThat((int) movieRelease.getYear()).isEqualTo(2014); - assertThat(movieRelease.getName()).isEqualTo("The Equalizer"); + assertThat(movieRelease.year).isEqualTo(2014); + assertThat(movieRelease.name).isEqualTo("The Equalizer"); file = Path.of("The.Trip.to.Italy.2014.LIMITED.720p.BluRay.x264-GECKOS.mkv"); release = releaseparser.parse(file); - assertThat(release.getVideoType()).isEqualTo(VideoType.MOVIE); - assertThat(release.getExtension()).isEqualTo("mkv"); - assertEquals(release.getFileName(), - "The.Trip.to.Italy.2014.LIMITED.720p.BluRay.x264-GECKOS.mkv"); - assertThat(release.getReleaseGroup()).isEqualTo("GECKOS"); - assertThat(release.getQuality()).isEqualTo("720p bluray x264"); + assertThat(release.videoType).isEqualTo(VideoType.MOVIE); + assertThat(release.extension).isEqualTo("mkv"); + assertThat(release.getFileName()).isEqualTo("The.Trip.to.Italy.2014.LIMITED.720p.BluRay.x264-GECKOS.mkv"); + assertThat(release.releaseGroup).isEqualTo("GECKOS"); + assertThat(release.quality).isEqualTo("720p bluray x264"); movieRelease = (MovieRelease) release; - assertThat((int) movieRelease.getYear()).isEqualTo(2014); - assertThat(movieRelease.getName()).isEqualTo("The Trip to Italy"); + assertThat(movieRelease.year).isEqualTo(2014); + assertThat(movieRelease.name).isEqualTo("The Trip to Italy"); } } diff --git a/pom.xml b/pom.xml index 0189cdb4..7276d67f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,295 +1,326 @@ - 4.0.0 - org.lodder.subtools - subtools - 1.5.1-SNAPSHOT - SubTools - pom + 4.0.0 + org.lodder.subtools + subtools + 1.5.1-SNAPSHOT + SubTools + pom - - UTF-8 - + + UTF-8 + 2024.1.43 + 1.18.36 + 23 + 23 + 23 + - - scm:git:git@github.com:phdelodder/SubTools.git - scm:git:git@github.com:phdelodder/SubTools.git - scm:git:git@github.com:phdelodder/SubTools.git - HEAD - + + scm:git:git@github.com:phdelodder/SubTools.git + scm:git:git@github.com:phdelodder/SubTools.git + scm:git:git@github.com:phdelodder/SubTools.git + HEAD + - - SubLibrary - MultiSubDownloader - + + SubLibrary + MultiSubDownloader + - - github - https://github.com/phdelodder/SubTools/issues - + + github + https://github.com/phdelodder/SubTools/issues + - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - 16 - 16 - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.1 - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.1 - - Max - Medium - true - findbugs-exclude.xml - - - - - check - - - - - - org.jacoco - jacoco-maven-plugin - 0.5.8.201207111220 - - - - prepare-agent - - - - report - test - - report - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.1.0 - - - maven-assembly-plugin - 3.6.0 - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - org.codehaus.mojo - properties-maven-plugin - 1.1.0 - - - - + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + UTF-8 + true + + -Xplugin:Manifold + -verbose + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-release-plugin + 3.0.1 + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.5 + + Max + Medium + true + findbugs-exclude.xml + + + + + check + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + prepare-agent + + + + report + test + + report + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.3.0 + + + maven-assembly-plugin + 3.7.1 + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.1 + + + org.codehaus.mojo + properties-maven-plugin + 1.2.1 + + + + - - - - org.lodder.subtools - multisubdownloader - ${project.version} - - - org.lodder.subtools - sublibrary - ${project.version} - - - commons-cli - commons-cli - 1.5.0 - - - org.projectlombok - lombok - 1.18.28 - provided - - - ch.qos.logback - logback-classic - 1.1.2 - - - org.openapitools - openapi-generator-core - 6.6.0 - - - javax.annotation - javax.annotation-api - 1.3.2 - - - io.swagger - swagger-annotations - 1.6.10 - - - com.squareup.okhttp3 - logging-interceptor - 4.11.0 - - - com.google.code.gson - gson - 2.10.1 - - - io.gsonfire - gson-fire - 1.8.5 - - - com.fasterxml.jackson.core - jackson-databind - 2.15.2 - - - com.pivovarit - throwing-function - 1.5.1 - - - name.falgout.jeffrey - throwing-streams - 3.2.0 - - - name.falgout.jeffrey - throwing-interfaces - 1.2.0 - - - com.optimaize.languagedetector - language-detector - 0.6 - - - org.apache.commons - commons-lang3 - 3.12.0 - - - org.jsoup - jsoup - 1.16.1 - - - org.apache.commons - commons-collections4 - 4.4 - - - org.hsqldb - hsqldb - 2.7.2 - - - javax.ws.rs - javax.ws.rs-api - 2.1.1 - - - com.uwetrottmann.thetvdb-java - thetvdb-java - 2.4.0 - - - com.miglayout - miglayout-swing - 11.1 - - - org.reflections - reflections - 0.10.2 - - - com.massisframework - j-text-utils - 0.3.4 - - - - commons-lang - commons-lang - - - - - org.openapitools - jackson-databind-nullable - 0.2.6 - - - org.codehaus.plexus - plexus-interactivity-api - 1.1 - - - junit - junit - - - - - org.json - json - 20230618 - - - net.jodah - typetools - 0.6.3 - - - org.jooq - joor - 0.9.14 - + + + + org.lodder.subtools + multisubdownloader + ${project.version} + + + org.lodder.subtools + sublibrary + ${project.version} + + + commons-cli + commons-cli + 1.8.0 + + + systems.manifold + manifold-ext-rt + ${manifold.version} + + + systems.manifold + manifold-props-rt + ${manifold.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + ch.qos.logback + logback-classic + 1.1.2 + + + org.openapitools + openapi-generator-core + 7.6.0 + + + javax.annotation + javax.annotation-api + 1.3.2 + + + io.swagger + swagger-annotations + 1.6.14 + + + com.squareup.okhttp3 + logging-interceptor + 4.12.0 + + + com.google.code.gson + gson + 2.11.0 + + + io.gsonfire + gson-fire + 1.9.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.17.1 + + + com.pivovarit + throwing-function + 1.5.1 + + + name.falgout.jeffrey + throwing-streams + 3.2.0 + + + name.falgout.jeffrey + throwing-interfaces + 1.2.0 + + + com.optimaize.languagedetector + language-detector + 0.6 + + + org.apache.commons + commons-lang3 + 3.14.0 + + + org.jsoup + jsoup + 1.18.3 + + + org.jspecify + jspecify + 1.0.0 + + + org.apache.commons + commons-collections4 + 4.4 + + + org.hsqldb + hsqldb + 2.7.3 + + + javax.ws.rs + javax.ws.rs-api + 2.1.1 + + + com.uwetrottmann.thetvdb-java + thetvdb-java + 2.4.0 + + + com.miglayout + miglayout-swing + 11.3 + + + org.reflections + reflections + 0.10.2 + + + com.massisframework + j-text-utils + 0.3.4 + + + + commons-lang + commons-lang + + + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + org.codehaus.plexus + plexus-interactivity-api + 1.1 + + + junit + junit + + + + + org.json + json + 20240303 + + + net.jodah + typetools + 0.6.3 + + + org.jooq + joor + 0.9.15 + - - org.mockito - mockito-core - 5.4.0 - test - - - org.assertj - assertj-core - 3.24.2 - test - - - org.junit.jupiter - junit-jupiter-api - 5.9.3 - test - - - + + org.mockito + mockito-core + 5.12.0 + test + + + org.assertj + assertj-core + 3.26.0 + test + + + org.junit.jupiter + junit-jupiter-api + 5.10.2 + test + + + \ No newline at end of file