From 723fed0f289bf6a862032da7dc6fae4925b8ade2 Mon Sep 17 00:00:00 2001 From: EotT123 <32986779+EotT123@users.noreply.github.com> Date: Wed, 9 Apr 2025 18:53:40 +0200 Subject: [PATCH 01/58] Changes (#15) * use manifold * upgrade to jdk 24 * fix opensubtitles api * update gestdown api * Rewrite file name parsing * dependency updates * Various changes and improvements --------- Co-authored-by: EotT123 --- .github/workflows/maven.yml | 32 +- MultiSubDownloader/pom.xml | 519 +- .../java/awt/Component/ComponentExt.java | 82 + .../java/awt/Container/ContainerExt.java | 33 + .../GridBagConstraintsExt.java | 33 + .../awt/GridBagLayout/GridBagLayoutExt.java | 33 + .../AbstractButton/AbstractButtonExt.java | 46 + .../javax/swing/JButton/JButtonExt.java | 18 + .../javax/swing/JCheckBox/JCheckBoxExt.java | 29 + .../javax/swing/JComboBox/JComboBoxExt.java | 80 + .../javax/swing/JComponent/JComponentExt.java | 72 + .../swing/JProgressBar/JProgressBarExt.java | 19 + .../swing/JScrollPane/JScrollPaneExt.java | 20 + .../javax/swing/JSlider/JSliderExt.java | 23 + .../swing/JTabbedPane/TabbedPaneExt.java | 43 + .../javax/swing/JTable/JTableExt.java | 37 + .../javax/swing/JTextArea/JTextAreaExt.java | 18 + .../javax/swing/JTextField/JTextFieldExt.java | 18 + .../JTextComponent/JTextComponentExt.java | 32 + .../cli/CommandLine/CommandLineExt.java | 19 + .../subtools/multisubdownloader/App.java | 67 +- .../subtools/multisubdownloader/CLI.java | 72 +- .../subtools/multisubdownloader/GUI.java | 394 +- .../UpdateAvailableGithub.java | 141 +- .../UserInteractionHandler.java | 10 +- .../UserInteractionHandlerCLI.java | 30 +- .../UserInteractionHandlerGUI.java | 14 +- .../actions/CleanAction.java | 56 +- .../actions/DownloadAction.java | 54 +- .../actions/FileListAction.java | 77 +- .../actions/RenameAction.java | 36 +- .../actions/SearchAction.java | 87 +- .../actions/UserInteractionHandlerAction.java | 44 +- .../multisubdownloader/cli/CliOption.java | 18 +- .../cli/actions/CliSearchAction.java | 97 +- ...gress.java => CLIFileIndexerProgress.java} | 27 +- .../cli/progress/CLIProgress.java | 30 +- .../cli/progress/CLISearchProgress.java | 30 +- .../exceptions/SearchSetupException.java | 3 +- .../framework/Bootstrapper.java | 2 +- .../framework/Container.java | 25 + .../framework/event/Emitter.java | 2 +- .../providers/EventServiceProvider.java | 7 +- .../service/providers/ServiceProvider.java | 3 +- .../providers/ServiceProviderComparator.java | 2 +- .../subtools/multisubdownloader/gui/Menu.java | 66 +- .../multisubdownloader/gui/Splash.java | 20 +- .../gui/ToStringListCellRenderer.java | 25 +- .../actions/search/FileGuiSearchAction.java | 58 +- .../gui/actions/search/GuiSearchAction.java | 47 +- .../actions/search/TextGuiSearchAction.java | 57 +- .../gui/dialog/MappingEpisodeNameDialog.java | 406 +- .../gui/dialog/MultiSubDialog.java | 11 +- .../gui/dialog/PreferenceDialog.java | 137 +- .../gui/dialog/ProgressDialog.java | 48 +- .../gui/dialog/RenameDialog.java | 122 +- .../gui/dialog/SelectDialog.java | 136 +- .../gui/dialog/StructureBuilderDialog.java | 133 +- .../progress/search/SearchProgressDialog.java | 84 +- .../search/SearchProgressTableModel.java | 12 +- .../gui/extra/ArrowButton.java | 57 +- .../gui/extra/ImageListCellRenderer.java | 36 +- .../gui/extra/JListWithImages.java | 26 +- .../gui/extra/LogTextAppender.java | 7 +- .../gui/extra/MemoryFolderChooser.java | 2 +- .../gui/extra/PanelCheckBox.java | 39 +- .../gui/extra/PartialDisableComboBox.java | 9 +- .../gui/extra/PopupListener.java | 4 +- .../gui/extra/TitlePanel.java | 30 +- .../gui/extra/progress/StatusLabel.java | 2 +- .../gui/extra/progress/StatusMessenger.java | 14 +- .../gui/extra/table/CustomColumnName.java | 10 +- .../gui/extra/table/CustomTable.java | 49 +- .../gui/extra/table/SearchColumnName.java | 25 +- .../extra/table/SubtitleTableColumnName.java | 39 +- .../gui/extra/table/SubtitleTableModel.java | 29 +- .../gui/extra/table/VideoTableModel.java | 74 +- .../gui/extra/table/ZebraJTable.java | 3 +- .../button/AbstractButtonExtension.java | 39 - .../jcomponent/button/JButtonExtension.java | 15 - .../component/ComponentExtension.java | 15 - .../container/ContainerExtension.java | 15 - .../jcheckbox/JCheckBoxExtension.java | 21 - .../gui/jcomponent/jcombobox/MyComboBox.java | 179 - .../jcomponent/JComponentExtension.java | 87 - .../jcomponent/jpopupmenu/MyPopupMenu.java | 20 +- .../jscrollpane/JScrollPaneExtension.java | 16 - .../jcomponent/jslider/JSliderExtension.java | 19 - .../jtextarea/JTextAreaExtension.java | 14 - .../JTextComponentExtension.java | 14 - .../jtextfield/JTextFieldExtension.java | 14 - .../jtextfield/MyPasswordField.java | 56 +- .../jtextfield/MyPasswordFieldOthersIntf.java | 22 + .../jcomponent/jtextfield/MyTextField.java | 17 - .../jtextfield/MyTextFieldCommon.java | 32 +- .../jtextfield/MyTextFieldInteger.java | 10 +- .../jtextfield/MyTextFieldOthersIntf.java | 4 +- .../jtextfield/MyTextFieldPath.java | 13 +- .../jtextfield/MyTextFieldString.java | 14 +- .../jtextfield/MypasswordFieldOthersIntf.java | 22 - .../gui/panels/InputPanel.java | 22 +- .../gui/panels/LoggingPanel.java | 39 +- .../gui/panels/ResultPanel.java | 37 +- .../gui/panels/SearchFileInputPanel.java | 28 +- .../gui/panels/SearchPanel.java | 11 +- .../gui/panels/SearchTextInputPanel.java | 74 +- .../preference/DefaultSelectionPanel.java | 59 +- .../preference/EpisodeLibraryPanel.java | 2 +- .../gui/panels/preference/GeneralPanel.java | 243 +- .../panels/preference/MovieLibraryPanel.java | 2 +- .../gui/panels/preference/OptionsPanel.java | 154 +- .../preference/SerieProvidersPanel.java | 159 +- .../panels/preference/StructureFilePanel.java | 254 +- .../preference/StructureFolderPanel.java | 144 +- .../gui/panels/preference/StructurePanel.java | 62 - .../preference/SubtitleBackupPanel.java | 66 +- .../panels/preference/VideoLibraryPanel.java | 111 +- .../gui/workers/DownloadWorker.java | 15 +- .../gui/workers/RenameWorker.java | 33 +- .../subtools/multisubdownloader/lib/Info.java | 31 +- .../lib/ReleaseFactory.java | 23 +- .../lib/control/MovieReleaseControl.java | 28 +- .../lib/control/ReleaseControl.java | 23 +- .../lib/control/TvReleaseControl.java | 60 +- .../control/subtitles/SubtitleFiltering.java | 16 +- .../subtitles/filters/ExactNameFilter.java | 10 +- .../subtitles/filters/KeywordFilter.java | 15 +- ...oupFilter.java => ReleaseGroupFilter.java} | 17 +- .../subtitles/filters/SubtitleFilter.java | 12 +- .../subtitles/sorting/ScoreCalculator.java | 12 +- .../control/subtitles/sorting/SortWeight.java | 42 +- .../subtitles/sorting/SubtitleComparator.java | 2 +- .../sorting/replacers/GroupReplacer.java | 2 +- .../lib/library/FilenameLibraryBuilder.java | 136 +- .../lib/library/LibraryActionType.java | 13 +- .../lib/library/LibraryBuilder.java | 25 +- .../library/LibraryOtherFileActionType.java | 9 +- .../lib/library/PathLibraryBuilder.java | 92 +- .../Addic7edServiceProvider.java | 40 +- .../LocalServiceProvider.java | 28 +- .../OpenSubtitlesServiceProvider.java | 32 +- .../PodnapisiServiceProvider.java | 22 +- .../SubsceneServiceProvider.java | 21 +- .../SubtitleServiceProvider.java | 7 +- .../TvSubtitlesServiceProvider.java | 22 +- .../settings/SettingValue.java | 707 +- .../settings/SettingsControl.java | 170 +- .../settings/model/LibrarySettings.java | 47 +- .../settings/model/PathMatchType.java | 8 +- .../settings/model/PathOrRegex.java | 30 +- .../settings/model/ScreenSettings.java | 18 +- .../settings/model/Settings.java | 125 +- .../settings/model/SettingsExcludeItem.java | 15 +- .../settings/model/State.java | 7 +- .../settings/model/UpdateCheckPeriod.java | 9 +- .../settings/model/UpdateType.java | 9 +- .../model/structure/FolderStructureTag.java | 17 +- .../model/structure/MovieStructureTag.java | 24 +- .../model/structure/SerieStructureTag.java | 15 +- .../model/structure/StructureTag.java | 7 +- .../subtitleproviders/Local.java | 110 +- .../subtitleproviders/SubtitleApi.java | 3 +- .../subtitleproviders/SubtitleProvider.java | 34 +- .../adapters/AbstractAdapter.java | 78 +- .../subtitleproviders/adapters/Adapter.java | 143 +- .../adapters/JAddic7edAdapter.java | 102 +- .../adapters/JAddic7edViaProxyAdapter.java | 117 +- .../adapters/JOpenSubAdapter.java | 170 +- .../adapters/JPodnapisiAdapter.java | 92 +- .../adapters/JSubsceneAdapter.java | 127 +- .../adapters/JTVsubtitlesAdapter.java | 109 +- .../addic7ed/JAddic7edApi.java | 218 +- .../addic7ed/LanguageId.java | 16 +- .../addic7ed/exception/Addic7edException.java | 5 +- .../model/Addic7edSubtitleDescriptor.java | 3 +- .../gestdown/JAddic7edProxyGestdownApi.java | 40 +- .../opensubtitles/DownloadSubtitle.java | 12 +- .../opensubtitles/OpenSubtitlesApi.java | 54 +- .../opensubtitles/OpenSubtitlesExecuter.java | 5 +- .../opensubtitles/OpenSubtitlesHasher.java | 18 +- .../opensubtitles/SearchSubtitles.java | 53 +- .../exception/OpenSubtitlesException.java | 5 +- .../model/OpenSubtitlesMovieDescriptor.java | 23 +- .../OpenSubtitlesSubtitleDescriptor.java | 77 +- .../model/OpensubtitleSerieId.java | 9 +- .../opensubtitles/param/AiTranslatedEnum.java | 12 +- .../param/ForeignPartsOnlyEnum.java | 12 +- .../param/HearingImpairedEnum.java | 12 +- .../param/MachineTranslatedEnum.java | 13 +- .../param/MoviehashMatchEnum.java | 12 +- .../param/OrderDirectionEnum.java | 12 +- .../opensubtitles/param/ParamIntf.java | 4 +- .../param/SearchSubtitlesEnum.java | 9 +- .../param/TrustedSourcesEnum.java | 15 +- .../opensubtitles/param/TypeEnum.java | 12 +- .../podnapisi/JPodnapisiApi.java | 255 +- .../exception/PodnapisiException.java | 5 +- .../model/PodnapisiSubtitleDescriptor.java | 41 +- .../subscene/SubsceneApi.java | 323 +- .../subscene/exception/SubsceneException.java | 5 +- .../subscene/model/SubSceneSerieId.java | 7 +- .../model/SubsceneSubtitleDescriptor.java | 8 +- .../tvsubtitles/JTVSubtitlesApi.java | 145 +- .../exception/TvSubtitlesException.java | 5 +- .../model/TVsubtitlesSubtitleDescriptor.java | 11 +- .../multisubdownloader/util/CLIExtension.java | 17 - .../multisubdownloader/util/ExportImport.java | 177 +- .../util/PropertiesReader.java | 16 +- .../workers/SearchManager.java | 44 +- .../workers/SearchWorker.java | 12 +- .../resources/gestdown/gestdown-swagger.json | 85 +- .../resources/opensubtitles/open-api.json | 5842 +++++++++++++++++ .../subtitles/SubtitleFilteringTest.java | 72 +- .../sorting/ScoreCalculatorTest.java | 15 +- .../subtitles/sorting/SortWeightTest.java | 22 +- .../sorting/replacers/GroupReplacerTest.java | 15 +- SubLibrary/pom.xml | 331 +- .../java/io/InputStream/InputStreamExt.java | 18 + .../java/lang/String/StringExt.java | 33 + .../java/nio/file/Path/PathExt.java} | 159 +- .../java/util/Collection/CollectionExt.java | 12 + .../java/util/Optional/OptionalExt.java | 47 + .../java/util/OptionalInt/OptionalIntExt.java | 50 + .../util/OptionalLong/OptionalLongExt.java | 50 + .../java/util/stream/Stream/StreamExt.java | 17 + .../manifold/rt/api/Array/ArrayExt.java | 35 + .../org/json/JSONArray/JSONArrayExt.java | 24 + .../org/jsoup/nodes/Element/ElementExt.java | 239 + .../jsoup/nodes/Element/NthElementFinder.java | 37 + .../org/jsoup/nodes/Node/NodeExt.java | 110 + .../jsoup/select/Elements/ElementsExt.java | 29 + .../select/Elements/UnmodifiableElements.java | 123 + .../org/w3c/dom/Document/DocumentExt.java | 31 + .../extensions/org/w3c/dom/Node/NodeExt.java | 28 + .../org/w3c/dom/NodeList/NodeListExt.java | 20 + .../subtools/multisubdownloader/Messages.java | 34 +- .../sublibrary/CacheKeyMatchEnum.java | 5 - .../subtools/sublibrary/ConfigProperties.java | 29 +- .../subtools/sublibrary/DetectLanguage.java | 33 +- .../lodder/subtools/sublibrary/Language.java | 23 +- .../lodder/subtools/sublibrary/Manager.java | 219 +- .../lodder/subtools/sublibrary/OsCheck.java | 36 +- .../subtools/sublibrary/cache/Cache.java | 47 +- .../sublibrary/cache/CacheObject.java | 23 +- .../subtools/sublibrary/cache/DiskCache.java | 165 +- .../sublibrary/cache/ExpiringCacheObject.java | 20 +- .../ExpiringSerializableCacheObject.java | 7 +- .../sublibrary/cache/InMemoryCache.java | 37 +- .../subtools/sublibrary/cache/LRUMap.java | 10 + .../cache/SerializableDiskCache.java | 20 +- .../cache/TemporaryCacheObject.java | 19 +- .../TemporarySerializableCacheObject.java | 7 +- .../sublibrary/cache/TypedDiskCache.java | 44 +- .../sublibrary/control/RegexUtils.java | 132 + .../sublibrary/control/ReleaseParser.java | 568 +- .../subtools/sublibrary/control/Roman.java | 33 +- .../subtools/sublibrary/control/Tags.java | 46 + .../sublibrary/control/VideoPatterns.java | 235 +- .../lodder/subtools/sublibrary/data/Html.java | 9 +- .../sublibrary/data/ProviderSerieId.java | 11 +- .../sublibrary/data/ReleaseDBIntf.java | 6 +- .../data/UserInteractionSettings.java | 21 +- .../data/UserInteractionSettingsIntf.java | 13 +- .../sublibrary/data/imdb/ImdbAdapter.java | 93 +- .../sublibrary/data/imdb/ImdbApi.java | 17 +- .../sublibrary/data/imdb/ImdbSearchIdApi.java | 73 +- .../data/imdb/exception/ImdbException.java | 2 +- .../imdb/exception/ImdbSearchIdException.java | 2 +- .../data/imdb/model/ImdbDetails.java | 10 +- .../sublibrary/data/omdb/OmdbAdapter.java | 18 +- .../sublibrary/data/omdb/OmdbApi.java | 22 +- .../data/omdb/exception/OmdbException.java | 2 +- .../data/omdb/model/OmdbDetails.java | 19 +- .../sublibrary/data/tvdb/TheTvdbAdapter.java | 97 +- .../sublibrary/data/tvdb/TheTvdbApi.java | 112 +- .../sublibrary/data/tvdb/TheTvdbMirrors.java | 41 +- .../data/tvdb/model/TheTvdbEpisode.java | 64 +- .../data/tvdb/model/TheTvdbSerie.java | 51 +- .../exception/ControlFactoryException.java | 7 +- .../SubtitlesProviderInitException.java | 6 +- .../exception/WebpageException.java | 12 + .../subtools/sublibrary/gui/InputPane.java | 30 +- .../subtools/sublibrary/gui/OptionsPane.java | 22 +- .../sublibrary/model/MovieRelease.java | 38 +- .../subtools/sublibrary/model/Release.java | 44 +- .../sublibrary/model/SeasonEpisode.java | 8 +- .../subtools/sublibrary/model/Subtitle.java | 45 +- .../sublibrary/model/SubtitleSource.java | 5 +- .../subtools/sublibrary/model/TvRelease.java | 64 +- .../subtools/sublibrary/model/Video.java | 14 - .../sublibrary/model/VideoSearchType.java | 9 +- .../settings/model/SerieMapping.java | 30 +- .../UserInteractionHandler.java | 12 +- .../UserInteractionHandlerCLI.java | 35 +- .../UserInteractionHandlerGUI.java | 40 +- .../sublibrary/util/ArrayExtension.java | 13 - .../sublibrary/util/CopyDirVisitor.java | 4 +- .../sublibrary/util/DeleteDirVisitor.java | 3 +- .../subtools/sublibrary/util/IOUtils.java | 20 - .../subtools/sublibrary/util/JSONUtils.java | 22 - .../sublibrary/util/NamedMatchResult.java | 7 +- .../sublibrary/util/NamedMatcher.java | 211 - .../sublibrary/util/NamedPattern.java | 80 - .../sublibrary/util/OptionalExtension.java | 165 - .../sublibrary/util/StreamExtension.java | 22 - .../subtools/sublibrary/util/StringUtil.java | 26 - .../subtools/sublibrary/util/Utils.java | 21 - .../util/filefilter/ExtensionFileFilter.java | 10 +- .../util/filefilter/JsonFileFilter.java | 14 +- .../util/filefilter/XmlFileFilter.java | 14 +- .../sublibrary/util/http/CookieManager.java | 48 +- .../sublibrary/util/http/HttpClient.java | 106 +- .../util/http/HttpClientException.java | 53 +- .../sublibrary/util/lazy/LazyBiFunction.java | 3 +- .../sublibrary/util/lazy/LazyFunction.java | 5 +- .../sublibrary/util/lazy/LazyRunnable.java | 3 +- .../sublibrary/util/lazy/LazySupplier.java | 15 +- .../util/lazy/LazyThrowingBiFunction.java | 33 +- .../util/lazy/LazyThrowingFunction.java | 33 +- .../util/lazy/LazyThrowingRunnable.java | 39 +- .../util/lazy/LazyThrowingSupplier.java | 79 +- .../util/prompter/PrompterBuilderBoolean.java | 7 +- .../util/prompter/PrompterBuilderCommon.java | 20 +- .../util/prompter/PrompterBuilderInt.java | 7 +- .../util/prompter/PrompterBuilderValue.java | 7 +- .../PrompterBuilderValueFromList.java | 12 +- .../PrompterBuilderValuesFromList.java | 21 +- .../util/prompter/PrompterUtil.java | 2 +- .../util/prompter/TableDisplayer.java | 9 +- .../subtools/sublibrary/xml/StringUtils.java | 15 +- .../subtools/sublibrary/xml/XMLHelper.java | 112 +- .../subtools/sublibrary/xml/XmlExtension.java | 17 - SubLibrary/src/main/java/util/Utils.java | 46 + .../resources/opensubtitles/open-api.json | 2067 ------ .../Message.properties} | 17 +- .../Message_nl.properties} | 23 +- .../assertions/MovieReleaseAssert.java | 38 + .../sublibrary/assertions/ReleaseAssert.java | 62 + .../assertions/SubLibraryAssertions.java | 22 + .../assertions/TvReleaseAssert.java | 62 + .../sublibrary/cache/InMemoryCacheTest.java | 32 +- .../sublibrary/control/ReleaseParserTest.java | 454 +- pom.xml | 613 +- 343 files changed, 15482 insertions(+), 10851 deletions(-) create mode 100644 MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java create mode 100644 MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java rename MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/{CLIFileindexerProgress.java => CLIFileIndexerProgress.java} (68%) delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/AbstractButtonExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/button/JButtonExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/component/ComponentExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/container/ContainerExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcheckbox/JCheckBoxExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcombobox/MyComboBox.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jcomponent/JComponentExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jscrollpane/JScrollPaneExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jslider/JSliderExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextarea/JTextAreaExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextcomponent/JTextComponentExtension.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/JTextFieldExtension.java create mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextField.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MypasswordFieldOthersIntf.java delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructurePanel.java rename MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/{ReleasegroupFilter.java => ReleaseGroupFilter.java} (54%) delete mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/CLIExtension.java create mode 100644 MultiSubDownloader/src/main/resources/opensubtitles/open-api.json create mode 100644 SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java rename SubLibrary/src/main/java/{org/lodder/subtools/sublibrary/util/FileUtils.java => extensions/java/nio/file/Path/PathExt.java} (67%) create mode 100644 SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java create mode 100644 SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java create mode 100644 SubLibrary/src/main/java/extensions/org/jsoup/nodes/Node/NodeExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/ElementsExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java create mode 100644 SubLibrary/src/main/java/extensions/org/w3c/dom/Document/DocumentExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/w3c/dom/Node/NodeExt.java create mode 100644 SubLibrary/src/main/java/extensions/org/w3c/dom/NodeList/NodeListExt.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheKeyMatchEnum.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/RegexUtils.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Tags.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/exception/WebpageException.java delete mode 100755 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/model/Video.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/ArrayExtension.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/IOUtils.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/JSONUtils.java delete mode 100755 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatcher.java delete mode 100755 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedPattern.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/OptionalExtension.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StreamExtension.java delete mode 100755 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/StringUtil.java delete mode 100755 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Utils.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/xml/XmlExtension.java create mode 100644 SubLibrary/src/main/java/util/Utils.java delete mode 100644 SubLibrary/src/main/resources/opensubtitles/open-api.json rename SubLibrary/src/main/resources/{messages.properties => resourcebundle/Message.properties} (95%) rename SubLibrary/src/main/resources/{messages_nl.properties => resourcebundle/Message_nl.properties} (95%) create mode 100644 SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java create mode 100644 SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/ReleaseAssert.java create mode 100644 SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/SubLibraryAssertions.java create mode 100644 SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index a126d9af..affdaf97 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -3,7 +3,7 @@ name: Java CI with Maven -on: [push, pull_request] +on: [ push, pull_request ] jobs: build: @@ -11,18 +11,18 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up JDK 16 - uses: actions/setup-java@v3 - with: - java-version: '16' - distribution: 'temurin' - cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml - - name: Upload jar - if: success() - uses: actions/upload-artifact@v3 - with: - name: multisubdownloader.jar - path: "/home/runner/work/SubTools/SubTools/MultiSubDownloader/target/multisubdownloader*.jar" \ No newline at end of file + - uses: actions/checkout@v4 + - name: Set up JDK 24 + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml + - name: Upload jar + if: success() + uses: actions/upload-artifact@v4 + with: + name: multisubdownloader.jar + path: "/home/runner/work/SubTools/SubTools/MultiSubDownloader/target/multisubdownloader*.jar" \ No newline at end of file diff --git a/MultiSubDownloader/pom.xml b/MultiSubDownloader/pom.xml index a8026fcd..bb838ade 100644 --- a/MultiSubDownloader/pom.xml +++ b/MultiSubDownloader/pom.xml @@ -1,228 +1,305 @@ - - org.lodder.subtools - subtools - 1.5.1-SNAPSHOT - + + org.lodder.subtools + subtools + 1.5.1-SNAPSHOT + - 4.0.0 - multisubdownloader - MultiSubDownloader + 4.0.0 + multisubdownloader + MultiSubDownloader - - - d-maven - http://d-maven.googlecode.com/svn/trunk/repo - - + + + - - ${maven.build.timestamp} - + + + org.lodder.subtools + sublibrary + + + commons-cli + commons-cli + + + com.miglayout + miglayout-swing + + + org.jsoup + jsoup + + + org.reflections + reflections + + + ch.qos.logback + logback-classic + + + com.squareup.okhttp3 + logging-interceptor + + + com.google.code.gson + gson + + + io.gsonfire + gson-fire + + + com.fasterxml.jackson.core + jackson-databind + + + systems.manifold + manifold-ext-rt + + + systems.manifold + manifold-props-rt + + + org.projectlombok + lombok + provided + + + org.openapitools + jackson-databind-nullable + + + net.jodah + typetools + + + org.jooq + joor + + + name.falgout.jeffrey + throwing-streams + + + com.pivovarit + throwing-function + + + jakarta.annotation + jakarta.annotation-api + + + org.mockito + mockito-core + test + + + org.assertj + assertj-core + test + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + src/main/resources + true + + + + + 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 + exec-maven-plugin + + org.lodder.subtools.multisubdownloader.App + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.lodder.subtools.multisubdownloader.App + + + + + + maven-assembly-plugin + + + jar-with-dependencies + + + + true + true + lib/ + org.lodder.subtools.multisubdownloader.App + + + + + + make-assembly + package + + single + + + + + + org.openapitools + openapi-generator-maven-plugin + + + gestdown + + generate + + + java + ${project.basedir}/src/main/resources/gestdown/gestdown-swagger.json + ${project.build.directory}/generated-source/openapi + org.gestdown.model + org.gestdown.api + org.gestdown.invoker + org.gestdown + + true + Subtitles,TvShows + AbstractOpenApiSchema,EpisodeDto,EpisodeWithSubtitlesDto,ErrorResponse,SearchRequest,ShowDto,ShowSearchResponse,SubtitleDto,SubtitleSearchResponse,TvShowSubtitleResponse,WrongFormatResponse + false + + integer=int,int=int + false + true + is + false + + + java8-localdatetime + true + - - - org.lodder.subtools - sublibrary - - - commons-cli - commons-cli - - - com.miglayout - miglayout-swing - - - org.jsoup - jsoup - - - org.reflections - reflections - - - ch.qos.logback - logback-classic - - - com.squareup.okhttp3 - logging-interceptor - - - com.google.code.gson - gson - - - io.gsonfire - gson-fire - - - com.fasterxml.jackson.core - jackson-databind - - - org.projectlombok - lombok - provided - - - org.openapitools - jackson-databind-nullable - - - net.jodah - typetools - - - org.jooq - joor - - - name.falgout.jeffrey - throwing-streams - - - com.pivovarit - throwing-function - - - org.mockito - mockito-core - test - - - org.assertj - assertj-core - test - - - org.junit.jupiter - junit-jupiter-api - test - - - - - - src/main/resources - true - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - org.codehaus.mojo - exec-maven-plugin - - org.lodder.subtools.multisubdownloader.App - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - org.lodder.subtools.multisubdownloader.App - - - - - - maven-assembly-plugin - - - jar-with-dependencies - - - - true - true - lib/ - org.lodder.subtools.multisubdownloader.App - - - - - - make-assembly - package - - single - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - org.openapitools - openapi-generator-maven-plugin - - - - generate - - - java - ${project.basedir}/src/main/resources/gestdown/gestdown-swagger.json - ${project.build.directory}/generated-source/openapi - org.gestdown.model - org.gestdown.api - org.gestdown.invoker - org.gestdown - - true - Subtitles,TvShows - AbstractOpenApiSchema,EpisodeDto,EpisodeWithSubtitlesDto,ErrorResponse,SearchRequest,ShowDto,ShowSearchResponse,SubtitleDto,SubtitleSearchResponse,TvShowSubtitleResponse,WrongFormatResponse - false - - integer=int,int=int - false - true - is - false - - - java8-localdatetime - - - - OffsetDateTime=Instant - LocalDateTime=Instant - - - java.time.OffsetDateTime=java.time.Instant - java.time.LocalDateTime=java.time.Instant - - - - - - - - org.codehaus.mojo - properties-maven-plugin - - - generate-resources - - write-project-properties - - - ${project.build.outputDirectory}/properties-from-pom.properties - - - - - - + + OffsetDateTime=Instant + LocalDateTime=Instant + + + 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 + + true + + + + + + + org.codehaus.mojo + properties-maven-plugin + + ${project.build.outputDirectory}/properties-from-pom.properties + + + build.timestamp + ${maven.build.timestamp} + + + + + + generate-resources + + write-project-properties + + + + + + \ No newline at end of file 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..c086a3bc --- /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.jspecify.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/java/awt/GridBagConstraints/GridBagConstraintsExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java new file mode 100644 index 00000000..88f0fb3d --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java @@ -0,0 +1,33 @@ +package extensions.java.awt.GridBagConstraints; + +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; + +@Extension +@UtilityClass +public class GridBagConstraintsExt { + + public static @Self GridBagConstraints insets(@This GridBagConstraints gridBagConstraints, Insets insets) { + gridBagConstraints.insets = insets; + return gridBagConstraints; + } + + public static @Self GridBagConstraints fill(@This GridBagConstraints gridBagConstraints, int fill) { + gridBagConstraints.fill = fill; + return gridBagConstraints; + } + + public static @Self GridBagConstraints gridx(@This GridBagConstraints gridBagConstraints, int gridx) { + gridBagConstraints.gridx = gridx; + return gridBagConstraints; + } + + public static @Self GridBagConstraints gridy(@This GridBagConstraints gridBagConstraints, int gridy) { + gridBagConstraints.gridy = gridy; + return gridBagConstraints; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java new file mode 100644 index 00000000..616d8aa0 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java @@ -0,0 +1,33 @@ +package extensions.java.awt.GridBagLayout; + +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; + +@Extension +@UtilityClass +public class GridBagLayoutExt { + + public static @Self GridBagLayout columnWidths(@This GridBagLayout gridBagLayout, int[] columnWidths) { + gridBagLayout.columnWidths = columnWidths; + return gridBagLayout; + } + + public static @Self GridBagLayout rowHeights(@This GridBagLayout gridBagLayout, int[] rowHeights) { + gridBagLayout.rowHeights = rowHeights; + return gridBagLayout; + } + + public static @Self GridBagLayout columnWeights(@This GridBagLayout gridBagLayout, double[] columnWeights) { + gridBagLayout.columnWeights = columnWeights; + return gridBagLayout; + } + + public static @Self GridBagLayout rowWeights(@This GridBagLayout gridBagLayout, double[] rowWeights) { + gridBagLayout.rowWeights = rowWeights; + return gridBagLayout; + } +} 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..e92af081 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java @@ -0,0 +1,80 @@ +package extensions.javax.swing.JComboBox; + +import javax.swing.*; +import java.awt.event.ItemEvent; +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 org.jspecify.annotations.Nullable; +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) { + return new JComboBox<>(Iterables.toArray(items, (Class) items.iterator().next().getClass())); + } + + 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 itemListener(@This JComboBox comboBox, Consumer itemListener) { + // comboBox.addItemListener(event -> itemListener.accept((E) event.getItem())); + // return comboBox; + // } + + public static @Self JComboBox itemListener(@This JComboBox comboBox, Consumer itemListener) { + comboBox.addItemListener(itemListener::accept); + 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; + } + + public static @Self JComboBox model(@This JComboBox comboBox, ComboBoxModel model) { + comboBox.setModel(model); + 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..bbf70bad --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java @@ -0,0 +1,72 @@ +package extensions.javax.swing.JComponent; + +import javax.swing.*; +import javax.swing.border.*; +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.jspecify.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; + } + + public static @Self JComponent border(@This JComponent component, Border border) { + component.setBorder(border); + return component; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java new file mode 100644 index 00000000..7589f8e1 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java @@ -0,0 +1,19 @@ +package extensions.javax.swing.JProgressBar; + +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 JProgressBarExt { + + public static @Self JProgressBar indeterminate(@This JProgressBar progressBar, boolean indeterminate) { + progressBar.setIndeterminate(indeterminate); + return progressBar; + } +} 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/JTabbedPane/TabbedPaneExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java new file mode 100644 index 00000000..47753797 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java @@ -0,0 +1,43 @@ +package extensions.javax.swing.JTabbedPane; + +import javax.swing.*; +import java.awt.*; +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 TabbedPaneExt { + + public static @Self JTabbedPane tabLayoutPolicy(@This JTabbedPane tabbedPane, int tabLayoutPolicy) { + tabbedPane.tabLayoutPolicy = tabLayoutPolicy; + return tabbedPane; + } + + // public static @Self JTabbedPane changeListener(@This JTabbedPane tabbedPane, ChangeListener changeListener) { + // tabbedPane.addChangeListener(changeListener); + // return tabbedPane; + // } + + public static @Self JTabbedPane changeListener(@This JTabbedPane tabbedPane, + Consumer changeListener) { + tabbedPane.addChangeListener(_ -> changeListener.accept(tabbedPane)); + return tabbedPane; + } + + public static @Self JTabbedPane withTab(@This JTabbedPane tabbedPane, + String title, Component component) { + tabbedPane.addTab(title, null, component, null); + return tabbedPane; + } + + public static @Self JTabbedPane withTab(@This JTabbedPane tabbedPane, + String title, Icon icon, Component component, String tip) { + tabbedPane.addTab(title, icon, component, tip); + return tabbedPane; + } +} diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java new file mode 100644 index 00000000..29a034fa --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java @@ -0,0 +1,37 @@ +package extensions.javax.swing.JTable; + +import javax.swing.*; +import javax.swing.table.*; +import java.util.function.Function; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; +import manifold.ext.rt.api.This; + +@Extension +@UtilityClass +public class JTableExt { + + public static @Self JTable model(@This JTable jTable, TableModel dataModel) { + jTable.setModel(dataModel); + return jTable; + } + + public static @Self JTable rowSorter(@This JTable jTable, RowSorter sorter) { + jTable.setRowSorter(sorter); + return jTable; + } + + public static @Self JTable rowSorter(@This JTable jTable, + Function> sorter) { + jTable.setRowSorter(sorter.apply(jTable.getModel())); + return jTable; + } + + public static @Self JTable autoResizeMode(@This JTable jTable, + int autoResizeMode) { + jTable.setAutoResizeMode(autoResizeMode); + return jTable; + } +} 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..29bb6851 --- /dev/null +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java @@ -0,0 +1,32 @@ +package extensions.javax.swing.text.JTextComponent; + +import javax.swing.event.*; +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); + } + + public static @Self JTextComponent documentListener(@This JTextComponent textComponent, DocumentListener listener) { + textComponent.getDocument().addDocumentListener(listener); + return textComponent; + } +} 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..48af06f4 --- /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.value); + } + + public static String getCliOptionValue(@This CommandLine line, CliOption cliOption) { + return line.getOptionValue(cliOption.value); + } +} \ 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..3a2170c6 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; @@ -25,9 +25,8 @@ import org.lodder.subtools.multisubdownloader.gui.Splash; 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.ConfigProperties.Property; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.cache.DiskCache; @@ -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()); @@ -59,19 +59,19 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu } if (!line.hasCliOption(CliOption.NO_GUI)) { - splash = new Splash(); - splash.showSplash(); + splash = new Splash().showSplash(); } Preferences preferences = Preferences.userRoot(); - preferences.putBoolean("speedy", line.hasCliOption(CliOption.SPEEDY)); - preferences.putBoolean("confirmProviderMapping", line.hasCliOption(CliOption.CONFIRM_PROVIDER_MAPPING)); + preferences.putBoolean(CliOption.SPEEDY.value, line.hasCliOption(CliOption.SPEEDY)); + preferences.putBoolean(CliOption.CONFIRM_PROVIDER_MAPPING.value, + line.hasCliOption(CliOption.CONFIRM_PROVIDER_MAPPING)); 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 */ @@ -89,7 +89,7 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu try { cmd.setUp(line); if (line.hasCliOption(CliOption.HELP)) { - formatter.printHelp(ConfigProperties.getInstance().getProperty("name"), getCLIOptions()); + formatter.printHelp(ConfigProperties.getProperty(Property.NAME), getCLIOptions()); return; } } catch (CliException e) { @@ -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); @@ -114,20 +114,21 @@ 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) + List providerNames = + app.makeSubtitleProviderStore().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)) - .clear(); + .cacheType(CacheType.DISK) + .keyFilter((String key) -> providerNames.stream().noneMatch(key::startsWith)) + .clear(); }).start(); } 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,27 +148,27 @@ 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.progressMsg = Messages.getText("App.Starting"); } DiskCache diskCache = - SerializableDiskCache.cacheBuilder().keyType(String.class).valueType(Serializable.class) - .timeToLive(TimeUnit.SECONDS.convert(500, TimeUnit.DAYS)) - .maxItems(2500) - .build(); + SerializableDiskCache.cacheBuilder().keyType(String.class).valueType(Serializable.class) + .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)) - .timerInterval(100L) - .maxItems(500) - .build(); + InMemoryCache.builder().keyType(String.class).valueType(Serializable.class) + .timeToLive(SECONDS.convert(10, MINUTES)) + .timerInterval(100L) + .maxItems(500) + .build(); return new Manager(new HttpClient(), inMemoryCache, diskCache); } 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..134e0f87 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; @@ -23,8 +22,6 @@ import org.lodder.subtools.multisubdownloader.lib.control.subtitles.SubtitleFiltering; 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 +30,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 +50,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 = app.makeManager(); 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 +73,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 +88,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); } } } @@ -103,22 +97,21 @@ public void download(List releases) { public void search() { try { CliSearchAction - .createWithSettings(settings) - .manager(manager) - .subtitleProviderStore((SubtitleProviderStore) app.make("SubtitleProviderStore")) - .indexingProgressListener(new CLIFileindexerProgress().verbose(verboseProgress)) - .searchProgressListener(new CLISearchProgress().verbose(verboseProgress)) - .cli(this) - .fileListAction(new FileListAction(this.settings)) - .language(language) - .releaseFactory(new ReleaseFactory(this.settings, (Manager) app.make("Manager"))) - .filtering(new SubtitleFiltering(this.settings)) - .folders(folders) - .recursive(recursive) - .overwriteSubtitles(force) - .build() - /* CLI has no benefit of running this in a separate Thread */ - .run(); + .createWithSettings(settings) + .subtitleProviderStore(app.makeSubtitleProviderStore()) + .indexingProgressListener(new CLIFileIndexerProgress().verbose(verboseProgress)) + .searchProgressListener(new CLISearchProgress().verbose(verboseProgress)) + .cli(this) + .fileListAction(new FileListAction(this.settings)) + .language(language) + .releaseFactory(new ReleaseFactory(this.settings, app.makeManager())) + .filtering(new SubtitleFiltering(this.settings)) + .folders(folders) + .recursive(recursive) + .overwriteSubtitles(force) + .build() + /* CLI has no benefit of running this in a separate Thread */ + .run(); } catch (SearchSetupException e) { LOGGER.error("executeArgs: search (%s)".formatted(e.getMessage()), e); } @@ -129,20 +122,21 @@ 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 +146,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..0caf2bf9 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,9 +20,7 @@ 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; import org.lodder.subtools.multisubdownloader.gui.actions.search.FileGuiSearchAction; import org.lodder.subtools.multisubdownloader.gui.actions.search.TextGuiSearchAction; @@ -49,10 +50,11 @@ import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.ScreenSettings; import org.lodder.subtools.multisubdownloader.settings.model.Settings; -import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.util.ExportImport; import org.lodder.subtools.multisubdownloader.util.PropertiesReader; +import org.lodder.subtools.multisubdownloader.util.PropertiesReader.PomProperty; import org.lodder.subtools.sublibrary.ConfigProperties; +import org.lodder.subtools.sublibrary.ConfigProperties.Property; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.ManagerException; @@ -61,27 +63,21 @@ 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; private final UserInteractionHandlerGUI userInteractionHandler; private final SettingsControl settingsControl; private ProgressDialog progressDialog; - private MyPopupMenu popupMenu; private SearchPanel pnlSearchFile; private SearchPanel pnlSearchText; - private LoggingPanel pnlLogging; private SearchFileInputPanel pnlSearchFileInput; private Menu menuBar; private IndexingProgressDialog fileIndexerProgressDialog; @@ -93,10 +89,10 @@ public class GUI extends JFrame implements PropertyChangeListener { */ 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); - setTitle(ConfigProperties.getInstance().getProperty("name")); + this.manager = app.makeManager(); + this.settings = app.makeSettings(); + this.userInteractionHandler = new UserInteractionHandlerGUI(settingsControl.settings, this); + setTitle(ConfigProperties.getProperty(Property.NAME)); /* * setIconImage(Toolkit.getDefaultToolkit().getImage( * getClass().getResource("/resources/Bierdopje_bigger.png"))); @@ -104,8 +100,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(); } @@ -113,26 +109,27 @@ public GUI(final SettingsControl settingsControl, Container app) { public void redraw() { close(); // setVisible(false); - getContentPane().removeAll(); + contentPane.removeAll(); initialize(); } 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,10 +137,11 @@ private void checkUpdate(final boolean forceUpdateCheck) { } } }); - JOptionPane.showMessageDialog(this, editorPane, ConfigProperties.getInstance().getProperty("name"), JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, editorPane, ConfigProperties.getProperty(Property.NAME), + JOptionPane.INFORMATION_MESSAGE); } else if (forceUpdateCheck) { - JOptionPane.showMessageDialog(this, Messages.getString("MainWindow.NoUpdateAvailable"), - ConfigProperties.getInstance().getProperty("name"), JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, getText("MainWindow.NoUpdateAvailable"), + ConfigProperties.getProperty(Property.NAME), JOptionPane.INFORMATION_MESSAGE); } } @@ -152,7 +150,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,99 +160,101 @@ 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 }; - getContentPane().setLayout(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 }; + contentPane.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; + contentPane.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); + LoggingPanel pnlLogging = new LoggingPanel(); + final GridBagConstraints gbcPnlLogging = new GridBagConstraints(); + gbcPnlLogging.fill = GridBagConstraints.BOTH; + gbcPnlLogging.insets = new Insets(0, 0, 5, 0); + gbcPnlLogging.gridx = 0; + gbcPnlLogging.gridy = 1; + contentPane.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); - - createMenu(); + StatusMessenger.getInstance().addListener(lblStatus); + final GridBagConstraints gbcLblStatus = new GridBagConstraints(); + gbcLblStatus.anchor = GridBagConstraints.SOUTHWEST; + gbcLblStatus.gridx = 0; + gbcLblStatus.gridy = 2; + contentPane.add(lblStatus, gbcLblStatus); + + createMenu(pnlLogging); setJMenuBar(menuBar); } - private void createMenu() { - Settings settings = settingsControl.getSettings(); - BiConsumer visibilityFunction = pnlSearchFile.getResultPanel().getTable()::setColumnVisibility; + private void createMenu(LoggingPanel pnlLogging) { + 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()) - .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())) - .withViewShowOnlyFoundAction(() -> { - settings.setOptionsShowOnlyFound(menuBar.isShowOnlyFound()); - ((VideoTableModel) pnlSearchFile.getResultPanel().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"))) - .withEditPreferencesAction( - () -> 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)) - .withExportPreferencesAction(() -> exportImport.exportSettings(ExportImport.SettingsType.PREFERENCES)) - .withImportPreferencesAction(() -> exportImport.importSettings(ExportImport.SettingsType.PREFERENCES)) - .withCheckUpdateAction(() -> checkUpdate(true)) - .withAboutAction(this::showAbout); + .withShowOnlyFound(settings.optionsShowOnlyFound) + .withFileQuitAction(this::close) + .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.optionsShowOnlyFound = menuBar.isShowOnlyFound(); + ((VideoTableModel) pnlSearchFile.resultPanel.getTable().getModel()) + .setShowOnlyFound(menuBar.isShowOnlyFound()); + }) + .withViewClearLogAction(() -> pnlLogging.setLogText("")) + .withEditRenameTVAction(() -> showRenameDialog.accept(VideoType.EPISODE, getText("Menu.RenameSerie"))) + .withEditRenameMovieAction(() -> showRenameDialog.accept(VideoType.MOVIE, getText("Menu.RenameMovie"))) + .withEditPreferencesAction( + () -> new PreferenceDialog(self(), settingsControl, app.makeEventEmitter(), manager, + userInteractionHandler).setVisible(true)) + .withTranslateShowNamesAction(this::showTranslateShowNames) + .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)) + .withAboutAction(this::showAbout); } 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) - .releaseFactory(new ReleaseFactory(settings, (Manager) app.make("Manager"))) - .build(); + .subtitleProviderStore(app.makeSubtitleProviderStore()) + .mainWindow(this) + .searchPanel(pnlSearchText) + .releaseFactory(new ReleaseFactory(settings, app.makeManager())) + .build(); pnlSearchTextInput.addSearchAction(searchAction); } @@ -268,35 +268,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) - .subtitleProviderStore((SubtitleProviderStore) this.app.make("SubtitleProviderStore")) - .mainWindow(this) - .searchPanel(pnlSearchFile) - .releaseFactory(new ReleaseFactory(settings, (Manager) app.make("Manager"))) - .build(); + FileGuiSearchAction searchAction = FileGuiSearchAction.createWithSettings(settings) + .subtitleProviderStore(app.makeSubtitleProviderStore()) + .mainWindow(this) + .searchPanel(pnlSearchFile) + .releaseFactory(new ReleaseFactory(settings, app.makeManager())) + .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 +304,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 +327,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 -> { + MyPopupMenu popupMenu = new MyPopupMenu(); + 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 +365,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,79 +374,81 @@ 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, app.makeManager(), + app.makeSubtitleProviderStore(), userInteractionHandler); tDialog.setVisible(true); } private void showAbout() { - String version = ConfigProperties.getInstance().getProperty(Messages.getString("MainWindow.Version")); - StringBuilder sb = new StringBuilder(); - sb.append(Messages.getString("MainWindow.CurrentVersion")).append(": ").append(version); + String version = ConfigProperties.getProperty(Property.VERSION); + String currentVersionText = getText("MainWindow.CurrentVersion"); + String buildTimestamp = PropertiesReader.getProperty(PomProperty.BUILD_TIMESTAMP); + String text = "$currentVersionText: $version"; if (version.contains("-SNAPSHOT")) { - sb.append(" (%s)".formatted(PropertiesReader.getProperty("build.timestamp"))); + text += " ($buildTimestamp)"; } - JOptionPane.showConfirmDialog(this, sb.toString(), ConfigProperties.getInstance().getProperty("name"), JOptionPane.CLOSED_OPTION); + JOptionPane.showConfirmDialog(this, text, ConfigProperties.getProperty(Property.NAME), + JOptionPane.CLOSED_OPTION); } protected void rename() { - CustomTable customTable = pnlSearchFile.getResultPanel().getTable(); + CustomTable customTable = pnlSearchFile.resultPanel.getTable(); RenameWorker renameWorker = - new RenameWorker(customTable, settingsControl.getSettings(), (Manager) this.app.make("Manager"), userInteractionHandler); + new RenameWorker(customTable, settingsControl.settings, app.makeManager(), 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, app.makeManager(), 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")) - .ifPresent(path -> { - CustomTable subtitleTable = pnlSearchText.getResultPanel().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)); - String filename = ""; - if (!subtitle.getFileName().endsWith(".srt")) { - filename = subtitle.getFileName() + ".srt"; - } - if (OsCheck.getOperatingSystemType() == OSType.Windows) { - filename = StringUtil.removeIllegalWindowsChars(filename); - } + MemoryFolderChooser.getInstance() + .selectDirectory(contentPane, getText("MainWindow.SelectFolder")) + .ifPresent(path -> { + 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)); + String filename = ""; + if (!subtitle.fileName.endsWith(".srt")) { + filename = subtitle.fileName + ".srt"; + } + if (OsCheck.operatingSystemType == OSType.WINDOWS) { + filename = filename.removeIllegalWindowsChars(); + } - try { - if (subtitle.getSourceLocation() == Subtitle.SourceLocation.FILE) { - subtitle.getFile().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)); - } - } 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); - throw new RuntimeException(e); + try { + if (subtitle.sourceLocation == Subtitle.SourceLocation.FILE) { + subtitle.file.copyToDir(path); + } else { + String url = subtitle.sourceLocation == Subtitle.SourceLocation.URL ? subtitle.url : + subtitle.urlSupplier.get(); + app.makeManager().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); + throw new RuntimeException(e); } } - }); - + } + }); } protected GUI self() { @@ -455,52 +456,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.getProperty(Property.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..fd3aca81 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java @@ -11,27 +11,28 @@ 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; import org.lodder.subtools.multisubdownloader.util.PropertiesReader; +import org.lodder.subtools.multisubdownloader.util.PropertiesReader.PomProperty; import org.lodder.subtools.sublibrary.ConfigProperties; +import org.lodder.subtools.sublibrary.ConfigProperties.Property; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.Manager.ValueBuilderIsPresentIntf; import org.lodder.subtools.sublibrary.cache.CacheType; 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 +53,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(); }; @@ -67,77 +68,91 @@ public boolean isNewVersionAvailable() { private Optional getUrlLatestNewStableGithubRelease() { return manager.valueBuilder() - .cacheType(CacheType.MEMORY) - .key("GitHub-update") - .optionalSupplier(() -> { - try { - String currentVersion = getVersion(); - Element element = manager.getPageContentBuilder().url(REPO_URL + "/releases") - .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(); - Matcher matcher = versionPattern.matcher(versionText); - matcher.find(); - String version = matcher.group(); - if (isFinalVersion(currentVersion) && compareVersions(version, currentVersion) <= 0) { - return Optional.empty(); - } - String versionBlockUrl = REPO_URL + "/releases/expanded_assets/" + versionText; - Element artifactElement = manager.getPageContentBuilder().url(versionBlockUrl) - .userAgent(null) - .cacheType(CacheType.NONE) - .getAsJsoupDocument() - .selectFirst(".Box-row a[href$='.jar']"); - String url = DOMAIN + artifactElement.attr("href"); - updateLastUpdateCheck(); - return Optional.of(url); - } catch (Exception e) { - LOGGER.error(Messages.getString("LoggingPanel.UpdateCheckFailed")); + .cacheType(CacheType.MEMORY) + .key("GitHub-update") + .optionalSupplier(() -> { + try { + String currentVersion = getVersion(); + Element element = manager.getPageContentBuilder().url(REPO_URL + "/releases") + .userAgent(null) + .cacheType(CacheType.NONE) + .getAsJsoupDocument() + .selectFirstByCss("#repo-content-turbo-frame .box a[href='$REPO_URI/releases/latest']"); + Pattern versionPattern = Pattern.compile("\\d*\\.\\d\\.\\d"); + String versionText = element.parent().selectFirstByTag("a").text(); + Matcher matcher = versionPattern.matcher(versionText); + matcher.find(); + String version = matcher.group(); + if (isFinalVersion(currentVersion) && compareVersions(version, currentVersion) <= 0) { return Optional.empty(); } - }).getOptional(); + String versionBlockUrl = REPO_URL + "/releases/expanded_assets/" + versionText; + Element artifactElement = manager.getPageContentBuilder().url(versionBlockUrl) + .userAgent(null) + .cacheType(CacheType.NONE) + .getAsJsoupDocument() + .selectFirstByCss(".Box-row a[href$='.jar']"); + String url = DOMAIN + artifactElement.attr("href"); + updateLastUpdateCheck(); + return Optional.of(url); + } catch (Exception e) { + if (LOGGER.isTraceEnabled) { + LOGGER.trace(Messages.getText("LoggingPanel.UpdateCheckFailed"), e); + } else { + LOGGER.error(Messages.getText("LoggingPanel.UpdateCheckFailed")); + } + return Optional.empty(); + } + }).getOptional(); } private Optional getUrlLatestNewNightlyGithubRelease() { return manager.valueBuilder() - .cacheType(CacheType.MEMORY) - .key("GitHub-update-nightly") - .optionalSupplier(() -> { - 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")); - 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"); - updateLastUpdateCheck(); - return Optional.of(downloadUrl); - } catch (Exception e) { - LOGGER.error(Messages.getString("LoggingPanel.UpdateCheckFailed")); + .cacheType(CacheType.MEMORY) + .key("GitHub-update-nightly") + .optionalSupplier(() -> { + try { + LocalDateTime buildTista = getBuildTista(); + + 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").attr("datetime")); + if (nightlyBuildTista.isBefore(buildTista)) { return Optional.empty(); } - }).getOptional(); + String url = + "https://nightly.link" + rowElement.selectFirstByCss(".Link--primary").attr("href"); + String downloadUrl = manager.getPageContentBuilder() + .url(url) + .cacheType(CacheType.MEMORY) + .getAsJsoupDocument() + .selectFirstByCss("table td a") + .attr("href"); + updateLastUpdateCheck(); + return Optional.of(downloadUrl); + } catch (Exception e) { + if (LOGGER.isTraceEnabled) { + LOGGER.trace(Messages.getText("LoggingPanel.UpdateCheckFailed"), e); + } else { + LOGGER.error(Messages.getText("LoggingPanel.UpdateCheckFailed")); + } + return Optional.empty(); + } + }).getOptional(); } private LocalDateTime getBuildTista() { - String timestamp = PropertiesReader.getProperty("build.timestamp"); + String timestamp = PropertiesReader.getProperty(PomProperty.BUILD_TIMESTAMP); return zonedDateTimeStringToLocalDateTime(timestamp); } private String getVersion() { - return ConfigProperties.getInstance().getProperty("version"); + return ConfigProperties.getProperty(Property.VERSION); } private boolean isFinalVersion(String version) { 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..a726df15 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java @@ -11,8 +11,6 @@ import org.codehaus.plexus.components.interactivity.DefaultOutputHandler; import org.codehaus.plexus.components.interactivity.DefaultPrompter; import org.codehaus.plexus.components.interactivity.Prompter; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; -import org.joor.Reflect; import org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.Release; @@ -27,39 +25,31 @@ public class UserInteractionHandlerCLI extends org.lodder.subtools.sublibrary.us public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { super(settings); - DefaultOutputHandler defaultOutputHandler = new DefaultOutputHandler(); - DefaultInputHandler defaultInputHandler = new DefaultInputHandler(); - try { - defaultOutputHandler.initialize(); - defaultInputHandler.initialize(); - } catch (InitializationException e) { - throw new RuntimeException(e); - } - prompter = Reflect.on(new DefaultPrompter()) - .set("outputHandler", defaultOutputHandler) - .set("inputHandler", defaultInputHandler) - .get(); + prompter = new DefaultPrompter(new DefaultOutputHandler(), new DefaultInputHandler()); } @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..8f37a710 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,14 +19,13 @@ public UserInteractionHandlerGUI(UserInteractionSettingsIntf settings, JFrame fr @Override public List selectSubtitles(Release release) { - List selection = new SelectDialog(getFrame(), release.getMatchingSubs(), release).getSelection(); - return selection.stream().map(i -> release.getMatchingSubs().get(i)).toList(); + List selection = new SelectDialog(frame, release.getMatchingSubs(), release).getSelection(); + return selection.stream().map(release.getMatchingSubs()::get).toList(); } @Override public void dryRunOutput(Release release) { // TODO Auto-generated method stub - } } 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..9910362e 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,53 +6,52 @@ 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()); + LOGGER.trace("cleanUpFiles: LibraryOtherFileAction {}", librarySettings.otherFileAction); if (!destination.isDirectory()) { throw new IllegalArgumentException("Destination [%s] is not a folder".formatted(destination)); } release.getPath().list().asThrowingStream(IOException.class) - .filter(p -> (p.isDirectory() && p.fileNameContainsIgnoreCase(sampleDirName)) - || (p.isRegularFile() && fileFilters.contains(p.getExtension()))) - .forEach(p -> { - switch (librarySettings.getLibraryOtherFileAction()) { - case MOVE -> move(p, destination); - case MOVEANDRENAME -> moveAndRename(p, destination, videoFileName); - case REMOVE -> delete(p); - case RENAME -> rename(p, destination, videoFileName); - case NOTHING -> {} - default -> {} + .filter(p -> (p.isDirectory() && p.fileNameContainsIgnoreCase(SAMPLE_DIR_NAME)) + || (p.isRegularFile() && FILE_FILTERS.contains(p.getExtension()))) + .forEach(p -> { + switch (librarySettings.otherFileAction) { + case MOVE -> move(p, destination); + case MOVEANDRENAME -> moveAndRename(p, destination, videoFileName); + case REMOVE -> delete(p); + case RENAME -> rename(p, destination, videoFileName); + 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 +63,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 +82,6 @@ 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..d8db919b 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,21 +33,21 @@ 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); } private void download(Release release, Subtitle subtitle, LibrarySettings librarySettings, Integer version) - throws IOException, ManagerException { - LOGGER.trace("cleanUpFiles: LibraryAction {}", librarySettings.getLibraryAction()); + throws IOException, ManagerException { + LOGGER.trace("cleanUpFiles: LibraryAction {}", librarySettings.action); Path path = PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); if (!path.exists()) { LOGGER.debug("Download creating folder [{}] ", path.toAbsolutePath()); @@ -60,31 +58,32 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar } } - FilenameLibraryBuilder filenameLibraryBuilder = FilenameLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler); + FilenameLibraryBuilder filenameLibraryBuilder = + FilenameLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler); String videoFileName = filenameLibraryBuilder.build(release).toString(); String subFileName = filenameLibraryBuilder.buildSubtitle(release, subtitle, videoFileName, version); 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(); + String 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,14 +91,15 @@ 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.removeEmptyFolders && release.path.isEmptyDir()) { + release.path.deletePath(); } } } - if (librarySettings.isLibraryBackupSubtitle()) { - String langFolder = subtitle.getLanguage() == null ? Language.ENGLISH.getName() : subtitle.getLanguage().getName(); - Path backupPath = librarySettings.getLibraryBackupSubtitlePath().resolve(langFolder); + if (librarySettings.backupSubtitle) { + String langFolder = + subtitle.language == null ? Language.ENGLISH.getName() : subtitle.language.getName(); + Path backupPath = librarySettings.backupSubtitlePath.resolve(langFolder); if (!backupPath.exists()) { try { @@ -109,8 +109,8 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar } } - if (librarySettings.isLibraryBackupUseWebsiteFileName()) { - subFile.copyToDirAndRename(backupPath, subtitle.getFileName()); + if (librarySettings.backupUseWebsiteFileName) { + 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..6812e334 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,44 +9,36 @@ 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 int progressFileIndex; - private int progressFilesTotal; - private final Settings settings; - private final static String subtitleExtension = "srt"; - private static final Logger LOGGER = LoggerFactory.getLogger(FileListAction.class); + private static final String SUBTITLE_EXTENSION = "srt"; + + private final Settings settings; + @set IndexingProgressListener indexingProgressListener; - 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, - forceSubtitleOverwrite); - /* Reset progress counters */ - this.progressFileIndex = 0; - this.progressFilesTotal = 0; + LOGGER.trace("getFileListing: dir [{}] Recursive [{}] languageCode [{}] forceSubtitleOverwrite [{}]", + dir, recursive, language, forceSubtitleOverwrite); + int progressFileIndex = 0; + int progressFilesTotal = 0; /* Start listing process */ - return this._getFileListing(dir, recursive, language, forceSubtitleOverwrite); - } - - private List _getFileListing(Path dir, boolean recursive, Language language, boolean forceSubtitleOverwrite) { final List filelist = new ArrayList<>(); List contents; try { @@ -57,26 +49,26 @@ private List _getFileListing(Path dir, boolean recursive, Language languag } /* Increase progressTotalFiles count */ - this.progressFilesTotal += contents.size(); + progressFilesTotal += contents.size(); if (this.indexingProgressListener != null) { this.indexingProgressListener.progress(dir.toString()); } for (Path file : contents) { - /* Increase progressFileIndex */ - this.progressFileIndex++; + progressFileIndex++; /* Update progressListener */ if (this.indexingProgressListener != null) { /* Tell the progress listener the overall progress */ - int progress = (int) Math.floor((float) this.progressFileIndex / this.progressFilesTotal * 100); + int progress = (int) Math.floor((float) progressFileIndex / progressFilesTotal * 100); this.indexingProgressListener.progress(progress); } 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 +85,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 +93,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); } @@ -115,9 +107,9 @@ public boolean isValidVideoFile(Path file) { 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)) - .findAny(); + .filter(extension::equals) + .map(_ -> file.changeExtension(SUBTITLE_EXTENSION)) + .findAny(); if (subtitleNameOptional.isEmpty()) { return false; @@ -127,23 +119,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.langCodeMap.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)) - .anyMatch(fileName -> fileName.contains(subtitleNameWithoutExtension)); + 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..a9fe63c4 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,33 +16,30 @@ 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()) { + String filename = switch (librarySettings.action) { case MOVE, NOTHING -> f.getFileNameAsString(); case MOVEANDRENAME, RENAME -> getNewFilename(f, release); }; LOGGER.trace("rename: filename [{}]", filename); - - Path newDir = switch (librarySettings.getLibraryAction()) { - case MOVE, MOVEANDRENAME -> PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); + + Path newDir = switch (librarySettings.action) { + case MOVE, MOVEANDRENAME -> + PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); case RENAME, NOTHING -> release.getPath(); }; if (!newDir.exists()) { @@ -54,21 +53,23 @@ 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)) { + if (librarySettings.hasLibraryAction(LibraryActionType.MOVE) || + librarySettings.hasLibraryAction(LibraryActionType.MOVEANDRENAME)) { LOGGER.info("Moving [{}] to the library folder [{}] , this might take a while... ", filename, newDir); file.moveToDirAndRename(newDir, filename); } else { - LOGGER.info("Moving [{}] to the library folder [{}] , this might take a while... ", filename, release.getPath()); + LOGGER.info("Moving [{}] to the library folder [{}] , this might take a while... ", filename, + release.getPath()); file.moveToDirAndRename(release.getPath(), filename); } if (!librarySettings.hasLibraryOtherFileAction(LibraryOtherFileActionType.NOTHING)) { new CleanAction(librarySettings).cleanUpFiles(release, newDir, filename); } - if (librarySettings.isLibraryRemoveEmptyFolders() && release.getPath().isEmptyDir()) { + if (librarySettings.removeEmptyFolders && release.getPath().isEmptyDir()) { Files.delete(release.getPath()); } } catch (IOException e) { @@ -77,11 +78,12 @@ public void rename(Path f, Release release) { } private String getNewFilename(Path f, Release release) { - FilenameLibraryBuilder filenameLibraryBuilder = FilenameLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler); + FilenameLibraryBuilder filenameLibraryBuilder = + FilenameLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler); String filename = filenameLibraryBuilder.build(release).toString(); if (release.hasExtension("srt")) { Language language = null; - if (librarySettings.isLibraryIncludeLanguageCode()) { + if (librarySettings.includeLanguageCode) { language = DetectLanguage.execute(f); if (language == null) { LOGGER.error("Unable to detect language, leaving language code blank"); 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..3c4edefa 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,31 +76,31 @@ 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 = - SearchManager.createWithSettings(this.settings) - /* Tell the manager which language we want */ - .language(language) - /* Tell the manager where to push progressUpdates */ - .progressListener(getSearchProgressListener()) - /* Tell the manager how to handle user interactions */ - .userInteractionHandler(getUserInteractionHandler()) - /* Listen for when the manager tells us Subtitles are found */ - .onFound(this); + SearchManager.createWithSettings(this.settings) + /* Tell the manager which language we want */ + .language(language) + /* Tell the manager where to push progressUpdates */ + .progressListener(searchProgressListener) + /* Tell the manager how to handle user interactions */ + .userInteractionHandler(userInteractionHandler) + /* Listen for when the manager tells us Subtitles are found */ + .onFound(this); /* Tell the manager which providers to use */ this.subtitleProviderStore.getAllProviders().stream() - .filter(subtitleProvider -> settings.isSerieSource(subtitleProvider.getSubtitleSource())) - .forEach(searchManager::addProvider); + .filter(subtitleProvider -> settings.isSerieSource(subtitleProvider.subtitleSource)) + .forEach(searchManager::addProvider); /* 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..22d02aa8 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 { @@ -36,37 +35,36 @@ public List subtitleSelection(final Release release, final boolean sub * @param dryRun dryRun * @return integer which subtitle is selected for downloading */ - public List subtitleSelection(Release release, final boolean subtitleSelectionDialog, final boolean dryRun) { + public List subtitleSelection(Release release, final boolean subtitleSelectionDialog, + final boolean dryRun) { // Sort subtitles by score - release.getMatchingSubs().sort(new SubtitleComparator()); + List subs = release.getMatchingSubs().stream().sorted(new SubtitleComparator()).toList(); if (dryRun) { - if (!release.getMatchingSubs().isEmpty()) { + if (!subs.isEmpty()) { userInteractionHandler.dryRunOutput(release); } } else { - if (!release.getMatchingSubs().isEmpty()) { + if (!subs.isEmpty()) { LOGGER.debug("determineWhatSubtitleDownload for videoFile: [{}] # found subs: [{}]", - release.getFileName(), release.getMatchingSubs().size()); - if (settings.isOptionsAlwaysConfirm()) { + release.fileName, subs.size()); + if (settings.optionsAlwaysConfirm) { return userInteractionHandler.selectSubtitles(release); - } else if (release.getMatchingSubs().size() == 1 - && release.getMatchingSubs().get(0).getSubtitleMatchType() == SubtitleMatchType.EXACT) { + } else if (subs.size() == 1 && subs.first.subtitleMatchType == SubtitleMatchType.EXACT) { LOGGER.debug("determineWhatSubtitleDownload: Exact Match"); - return List.of(release.getMatchingSubs().get(0)); - } else if (release.getMatchingSubs().size() > 1) { + return List.of(subs.first); + } else if (subs.size() > 1) { LOGGER.debug("determineWhatSubtitleDownload: Multiple subs detected"); // Automatic selection - List shortlist = userInteractionHandler.getAutomaticSelection(release.getMatchingSubs()); + List shortlist = userInteractionHandler.getAutomaticSelection(subs); shortlist.forEach(release::addMatchingSub); - // automatic selection results in 1 result - if (shortlist.size() == 1) { - return List.of(release.getMatchingSubs().get(0)); - } - // nothing match the minimum automatic selection value if (shortlist.isEmpty()) { + // nothing match the minimum automatic selection value return List.of(); + } else if (shortlist.size() == 1) { + // automatic selection results in 1 result + return List.of(subs.first); } // still more than 1 subtitle, let the user decide! @@ -74,15 +72,15 @@ public List subtitleSelection(Release release, final boolean subtitleS LOGGER.debug("determineWhatSubtitleDownload: Select subtitle with dialog"); 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()); + LOGGER.info("Multiple subs detected for: [{}] Unhandleable for CMD! switch to GUI or use " + + "'--selection' as switch in de CMD", release.fileName); } - } else if (release.getMatchingSubs().size() == 1) { + } else { LOGGER.debug("determineWhatSubtitleDownload: only one sub taking it!!!!"); - return List.of(release.getMatchingSubs().get(0)); + return List.of(subs.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..4bcc8f7c 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 CLI cli; + private final FileListAction fileListAction; + private final ReleaseFactory releaseFactory; + private final SubtitleFiltering filtering; private final boolean overwriteSubtitles; - private final @NonNull List folders; + private final 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 Language language; + @get(Protected) @override IndexingProgressListener indexingProgressListener; + @get(Protected) @override 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); } @@ -109,12 +104,11 @@ public static CliSearchActionBuilderManager createWithSettings(Settings settings @Setter @Accessors(fluent = true, chain = true) public static class CliSearchActionBuilder - implements CliSearchActionBuilderSearchProgressListener, CliSearchActionBuilderIndexingProgressListener, - CliSearchActionBuilderSubtitleProviderStore, CliSearchActionBuilderCLI, - CliSearchActionBuilderFileListAction, CliSearchActionBuilderLanguage, CliSearchActionBuilderReleaseFactory, - CliSearchActionBuilderFiltering, CliSearchActionBuilderFolders, CliSearchActionBuilderOther, CliSearchActionBuilderManager { + implements CliSearchActionBuilderSearchProgressListener, CliSearchActionBuilderIndexingProgressListener, + CliSearchActionBuilderSubtitleProviderStore, CliSearchActionBuilderCLI, + CliSearchActionBuilderFileListAction, CliSearchActionBuilderLanguage, CliSearchActionBuilderReleaseFactory, + 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,11 +152,12 @@ 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()) - .toList(); + .flatMap(folder -> fileListAction.getFileListing(folder, recursive, language, overwriteSubtitles) + .stream()) + .toList(); /* fix: remove carriage return from progressbar */ System.out.println(); @@ -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 68% 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..51bdd16b 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 final 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,24 @@ 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 + @Override + 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..50a0f2f8 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 sealed class CLIProgress permits CLIFileIndexerProgress, CLISearchProgress { + + @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..5e0b70de 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 final 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,24 @@ 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 + @Override + 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/Container.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java index bb53d5f4..330c217c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java @@ -2,7 +2,12 @@ import java.util.HashMap; import java.util.Map; +import java.util.prefs.Preferences; +import org.lodder.subtools.multisubdownloader.framework.event.Emitter; +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.util.lazy.LazySupplier; public class Container { @@ -16,4 +21,24 @@ public void bind(String name, LazySupplier resolver) { public Object make(String name) { return bindings.get(name).get(); } + + public SubtitleProviderStore makeSubtitleProviderStore() { + return (SubtitleProviderStore) make("SubtitleProviderStore"); + } + + public Manager makeManager() { + return (Manager) make("Manager"); + } + + public Settings makeSettings() { + return (Settings) make("Settings"); + } + + public Preferences makePreferences() { + return (Preferences) make("Preferences"); + } + + public Emitter makeEventEmitter() { + return (Emitter) make("EventEmitter"); + } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java index bad305a1..ee57eb0d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java @@ -16,7 +16,7 @@ public void fire(Event event) { } public void listen(String eventName, Handler handler) { - eventListeners.computeIfAbsent(eventName, k -> new ArrayList<>()).add(handler); + eventListeners.computeIfAbsent(eventName, _ -> new ArrayList<>()).add(handler); } public void unlisten(String eventName, Handler handler) { 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..5561159d 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,33 +1,30 @@ 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 { @Serial private static final long serialVersionUID = -7795482367449509520L; - private JProgressBar progressBar; + private final JProgressBar progressBar; public Splash() { - initialize_ui(); - } - - public void initialize_ui() { setBounds(100, 100, 501, 100); - getContentPane().setLayout(new MigLayout("", "[][475px,center][]", "[][40px:n]")); + contentPane.setLayout(new MigLayout("", "[][475px,center][]", "[][40px:n]")); - JLabel label = new JLabel(Messages.getString("Splash.starting")); - getContentPane().add(label, "cell 1 0 2 1,alignx left"); + JLabel label = new JLabel(getText("Splash.starting")); + contentPane.add(label, "cell 1 0 2 1,alignx left"); progressBar = new JProgressBar(0, 100); progressBar.setIndeterminate(true); progressBar.setStringPainted(true); - getContentPane().add(progressBar, "cell 1 1,grow"); + contentPane.add(progressBar, "cell 1 1,grow"); Rectangle r = getBounds(); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); @@ -37,9 +34,10 @@ public void initialize_ui() { } - public void showSplash() { + public Splash showSplash() { setVisible(true); toFront(); + return this; } public void setProgressMsg(String msg) { 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..2bd8c0bb 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,18 +19,13 @@ 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; -public class FileGuiSearchAction extends GuiSearchAction { +public final 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); } @@ -59,11 +54,10 @@ public static FileGuiSearchActionBuilderManager createWithSettings(Settings sett @Setter @Accessors(chain = true, fluent = true) public static class FileGuiSearchActionBuilder - implements FileGuiSearchActionBuilderBuild, FileGuiSearchActionBuilderReleaseFactory, - FileGuiSearchActionBuilderSearchPanel, FileGuiSearchActionBuilderGUI, - FileGuiSearchActionBuilderSubtitleProviderStore, FileGuiSearchActionBuilderManager { + implements FileGuiSearchActionBuilderBuild, FileGuiSearchActionBuilderReleaseFactory, + FileGuiSearchActionBuilderSearchPanel, FileGuiSearchActionBuilderGUI, + 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, - SearchPanel searchPanel, ReleaseFactory releaseFactory) { - super(manager, settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); + private FileGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, + SearchPanel searchPanel, ReleaseFactory 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 (path.isEmpty() && !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()) - .toList(); + .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..69bb9f7c 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,11 @@ package org.lodder.subtools.multisubdownloader.gui.actions.search; +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.override; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; import org.lodder.subtools.multisubdownloader.actions.SearchAction; @@ -15,28 +19,23 @@ 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 { +public abstract sealed class GuiSearchAction

extends SearchAction + permits FileGuiSearchAction, TextGuiSearchAction { - 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) GUI mainWindow; + @get(Protected) SearchPanel

searchPanel; + @get(Protected) SubtitleFiltering filtering; + @get(Protected) 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, - GUI mainWindow, SearchPanel

searchPanel, ReleaseFactory releaseFactory) { - super(manager, settings, subtitleProviderStore); + GuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, + GUI mainWindow, SearchPanel

searchPanel, ReleaseFactory releaseFactory) { + 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.table.model; 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..c00ef153 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 @@ -1,6 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.actions.search; import java.nio.file.Path; +import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; @@ -15,18 +16,14 @@ 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.control.VideoPatterns.VideoExtensions; import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.TvRelease; import org.lodder.subtools.sublibrary.model.VideoSearchType; -public class TextGuiSearchAction extends GuiSearchAction { - - public interface FileGuiSearchActionBuilderManager { - TextGuiSearchActionBuilderSubtitleProviderStore manager(Manager manager); - } +public final class TextGuiSearchAction extends GuiSearchAction { public interface TextGuiSearchActionBuilderSubtitleProviderStore { TextGuiSearchActionBuilderGUI subtitleProviderStore(SubtitleProviderStore subtitleProviderStore); @@ -48,7 +45,7 @@ public interface TextGuiSearchActionBuilderBuild { TextGuiSearchAction build(); } - public static FileGuiSearchActionBuilderManager createWithSettings(Settings settings) { + public static TextGuiSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { return new TextGuiSearchActionBuilder(settings); } @@ -56,10 +53,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 +64,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, - SearchPanel searchPanel, ReleaseFactory releaseFactory) { - super(manager, settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); + private TextGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, + SearchPanel searchPanel, ReleaseFactory 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 +85,36 @@ 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()) - .build(); - default -> getReleaseFactory().createRelease(Path.of(name), getUserInteractionHandler()); + .name(name) + .season(inputPanel.season) + .episode(inputPanel.episode) + .quality(inputPanel.quality) + .build(); + case MOVIE -> MovieRelease.builder().name(name).quality(inputPanel.quality).build(); + default -> releaseFactory.createRelease(Path.of( + name + (Arrays.stream(VideoExtensions.values()).anyMatch(ext -> name.endsWith("." + ext)) ? "" : + ".")), + 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..495b2671 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,12 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +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,28 +16,12 @@ 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; import org.lodder.subtools.sublibrary.Manager; @@ -39,86 +30,147 @@ 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; - private final JPanel contentPanel = new JPanel(); - private JTable table; - private final Manager manager; + @Serial private static final long serialVersionUID = 1L; + private final MappingTableModel mappingTableModel; private final SubtitleProviderStore subtitleProviderStore; - private final UserInteractionHandlerGUI userInteractionHandler; - private Optional selectedSubtitleProvider; private final JButton btnAddCustomMapping; + private final 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); - this.manager = manager; + public MappingEpisodeNameDialog(JFrame frame, Manager manager, SubtitleProviderStore subtitleProviderStore, + UserInteractionHandlerGUI userInteractionHandler) { + super(frame, getText("MappingEpisodeNameDialog.Title"), true); this.subtitleProviderStore = subtitleProviderStore; - this.userInteractionHandler = userInteractionHandler; - this.btnAddCustomMapping = new JButton(Messages.getString("MappingEpisodeNameDialog.ChangeMapping")); this.mappingTableModel = new MappingTableModel(manager); - initialize(); + setResizable(true); + setBounds(150, 150, 650, 400); + + table = new JTable().model(mappingTableModel).rowSorter(TableRowSorter::new); + + contentPane + .layout(new BorderLayout()) + .addComponent(BorderLayout.CENTER, new JPanel() + .border(new EmptyBorder(5, 5, 5, 5)) + .layout(new GridBagLayout() + .columnWidths(new int[]{ 0, 0 }) + .rowHeights(new int[]{ 0, 40, 0 }) + .columnWeights(new double[]{ 1.0, Double.MIN_VALUE }) + .rowWeights(new double[]{ 0.0, 1.0, Double.MIN_VALUE })) + // select provider panel + .addComponent(new JPanel() + .addComponent(new JLabel(getText("MappingEpisodeNameDialog.SelectProvider"))) + .addComponent(new JComboBox<>() + .model(new DefaultComboBoxModel<>(MappingType.values())) + .itemListener(event -> selectMappingType((MappingType) event.getItem())))) + .addComponent(new JPanel(), + new GridBagConstraints().insets(new Insets(0, 0, 5, 0)) + .fill(GridBagConstraints.BOTH).gridx(0).gridy(0)) + .addComponent(new JScrollPane().viewportView(table), + new GridBagConstraints().fill(GridBagConstraints.BOTH).gridx(0).gridy(1))) + // button panel + .addComponent(BorderLayout.SOUTH, new JPanel() + .layout(new MigLayout("", "[25px][50px][grow][50px][grow][50px][25px]", + "[][25px,grow,fill]")) + .addComponent("skip", new JButton(getText("MappingEpisodeNameDialog.DeleteRow")) + .actionListener(_ -> { + int rowNbr = table.convertRowIndexToModel(table.getSelectedRow()); + MappingTableModel model = (MappingTableModel) table.getModel(); + Row row = (Row) model.getDataVector().get(rowNbr); + manager.valueBuilder().cacheType(CacheType.DISK).key(row.key).remove(); + if (row.selectionForKeyPrefix.deleteOtherFunction() != null) { + manager.valueBuilder() + .cacheType(CacheType.DISK) + .key(row.selectionForKeyPrefix.deleteOtherFunction().apply(row.key)) + .remove(); + } + model.removeRow(rowNbr); + })) + .addComponent("skip", btnAddCustomMapping = + new JButton(getText("MappingEpisodeNameDialog.ChangeMapping")) + .actionListener(() -> { + int rowNbr = table.convertRowIndexToModel(table.getSelectedRow()); + MappingTableModel model = (MappingTableModel) table.getModel(); + + Row row = (Row) model.getDataVector().get(rowNbr); + String currentName = row.serieMapping.name; + + String message = 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(serieId -> { + row.serieMapping = + new SerieMapping(currentName, serieId.providerId, serieId.providerName, + serieId.season); + List sortKeys = table.rowSorter.sortKeys; + selectMappingType(selectedMappingType); + table.rowSorter.sortKeys = sortKeys; + }, () -> userInteractionHandler.message( + getText("MappingEpisodeNameDialog.NoResultsFoundForSerieName", newName), + getText("App.Info"))); + } catch (Exception e) { + userInteractionHandler.message(getText("App.ErrorOccurred", e.getMessage()), + getText("App.Error")); + } + })); + })) + .addComponent("skip", new JButton(getText("App.Close")) + .defaultButtonFor(getRootPane()) + .actionListener(() -> setVisible(false)) + .actionCommand(getText("App.Close")))); + selectMappingType(MappingType.values()[0]); } private void selectMappingType(MappingType mappingType) { this.selectedMappingType = mappingType; - this.selectedSubtitleProvider = subtitleProviderStore.getAllProviders().stream() - .filter(subtitleProvider -> subtitleProvider.getProviderName().equals(mappingType.getProviderName())) - .findAny(); - btnAddCustomMapping.setEnabled(selectedSubtitleProvider.isPresent()); - mappingTableModel.setMappingType(mappingType); + this.selectedSubtitleProvider = subtitleProviderStore.getAllProviders() + .stream() + .filter(subtitleProvider -> subtitleProvider.providerName.equals(mappingType.providerName)) + .findAny(); + btnAddCustomMapping.enabled = selectedSubtitleProvider.isPresent(); + mappingTableModel.mappingType = 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:"), - new SelectionForKeyPrefix("", "ADDIC7ED-serieName-tvdbId:")), + new SelectionForKeyPrefix("", "TVDB-serieId-", k -> k.replace("-serieId-", "-tvdbSerie-"))), + 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:"), - new SelectionForKeyPrefix("", "SUBSCENE-serieName-tvdbId:")), + new SelectionForKeyPrefix("", "ADDIC7ED-GESTDOWN-serieName-name:"), + new SelectionForKeyPrefix("", "ADDIC7ED-GESTDOWN-serieName-tvdbId:")), + SUBSCENE("Subscene", SubtitleSource.SUBSCENE, new SelectionForKeyPrefix("", "SUBSCENE-serieName-name:"), + new SelectionForKeyPrefix("", "SUBSCENE-serieName-tvdbId:")), TV_SUBTITLES("TVSubtitles", SubtitleSource.TVSUBTITLES, - new SelectionForKeyPrefix("", "TVSUBTITLES-serieName-name:"), - new SelectionForKeyPrefix("", "TVSUBTITLES-serieName-tvdbId:")), + new SelectionForKeyPrefix("", "TVSUBTITLES-serieName-name:"), + new SelectionForKeyPrefix("", "TVSUBTITLES-serieName-tvdbId:")), OPEN_SUBTITLES("OpenSubtitles", SubtitleSource.OPENSUBTITLES, - new SelectionForKeyPrefix("", "OPENSUBTITLES-serieName-name:"), - new SelectionForKeyPrefix("", "OPENSUBTITLES-serieName-tvdbId:")), - PODNAPISI("Podnapisi", SubtitleSource.PODNAPISI, - new SelectionForKeyPrefix("", "PODNAPISI-serieName-name:"), - new SelectionForKeyPrefix("", "PODNAPISI-serieName-tvdbId:")); + new SelectionForKeyPrefix("", "OPENSUBTITLES-serieName-name:"), + new SelectionForKeyPrefix("", "OPENSUBTITLES-serieName-tvdbId:")), + 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() { @@ -127,10 +179,10 @@ public String toString() { static { MAPPING_SUPPLIER = (manager, selectionForKeyPrefix) -> manager.valueBuilder() - .cacheType(CacheType.DISK) - .keyFilter(k -> k.startsWith(selectionForKeyPrefix.keyPrefix)) - .returnType(SerieMapping.class) - .getEntries(); + .cacheType(CacheType.DISK) + .keyFilter(k -> k.startsWith(selectionForKeyPrefix.keyPrefix)) + .returnType(SerieMapping.class) + .getEntries(); } MappingType(String name, SubtitleSource subtitleSource, SelectionForKeyPrefix... selectionForKeyPrefixList) { @@ -140,9 +192,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 = getText("MappingEpisodeNameDialog.SceneShowName"); + this.mappingColumn = getText("MappingEpisodeNameDialog.ProviderId"); + this.providerNameColumn = getText("MappingEpisodeNameDialog.ProviderName"); this.selectionForKeyPrefixList = selectionForKeyPrefixList; } } @@ -153,18 +205,14 @@ 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) { + SelectionForKeyPrefix selectionForKeyPrefix) { this.key = key; this.serieMapping = serieMapping; this.selectionForKeyPrefix = selectionForKeyPrefix; @@ -174,30 +222,31 @@ 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() - .map(serieMappingPair -> { - SerieMapping serieMapping = serieMappingPair.getValue(); - String name = serieMapping.getName(); - String providerId = serieMapping.getProviderId() == null ? "" : serieMapping.getProviderId(); - String providerName = serieMapping.getProviderName(); - if (providerId.contains("/")) { - providerId = providerId.substring(providerId.lastIndexOf("/") + 1); - } - providerId = providerId.replace(".html", ""); - return new Row(serieMappingPair.getKey(), name, providerId, providerName, serieMapping, selectionForKeyPrefix); - })) - .sorted(Comparator.comparing(row -> row.getSerieMapping() == null || row.getSerieMapping().getProviderName() == null ? "zzz" - : row.getSerieMapping().getName())) - .forEach(this::addRow); + 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 providerId = serieMapping.providerId == null ? "" : serieMapping.providerId; + if (providerId.contains("/")) { + providerId = providerId.substring(providerId.lastIndexOf("/") + 1); + } + providerId = providerId.replace(".html", ""); + return new Row(serieMappingPair.getKey(), serieMapping.name, providerId, + serieMapping.providerName, serieMapping, selectionForKeyPrefix); + })) + .sorted(Comparator.comparing( + row -> row.serieMapping == null || row.serieMapping.providerName == null ? "zzz" : + row.serieMapping.name)) + .forEach(this::addRow); } @Override @@ -205,131 +254,4 @@ public boolean isCellEditable(int row, int col) { return false; } } - - private void initialize() { - setResizable(true); - setBounds(150, 150, 650, 400); - 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); - { - JPanel selectionPane = new JPanel(); - contentPanel.add(selectionPane); - - JLabel lblDefaultIncomingFolder = new JLabel(Messages.getString("MappingEpisodeNameDialog.SelectProvider")); - selectionPane.add(lblDefaultIncomingFolder); - - JComboBox mappingTypeList = new JComboBox<>(); - mappingTypeList.setModel(new DefaultComboBoxModel<>(MappingType.values())); - mappingTypeList.addItemListener(arg0 -> selectMappingType((MappingType) arg0.getItem())); - selectMappingType(MappingType.values()[0]); - selectionPane.add(mappingTypeList); - } - { - 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); - } - { - 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); - { - table = new JTable(); - - table.setModel(mappingTableModel); - RowSorter sorter = new TableRowSorter<>(table.getModel()); - table.setRowSorter(sorter); - scrollPane.setViewportView(table); - } - } - { - JPanel buttonPane = new JPanel(); - getContentPane().add(buttonPane, BorderLayout.SOUTH); - 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 -> { - 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) { - manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(row.getSelectionForKeyPrefix().deleteOtherFunction().apply(key)) - .remove(); - } - model.removeRow(rowNbr); - }); - buttonPane.add(btnDeleteSelectedRow, "skip"); - } - - { - btnAddCustomMapping.withActionListener(() -> { - int rowNbr = table.convertRowIndexToModel(table.getSelectedRow()); - MappingTableModel model = (MappingTableModel) table.getModel(); - - Row row = (Row) model.getDataVector().get(rowNbr); - String currentName = row.getSerieMapping().getName(); - - 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")); - } - }); - }); - }); - buttonPane.add(btnAddCustomMapping, "skip"); - } - - { - new JButton(Messages.getString("App.Close")) - .defaultButtonFor(getRootPane()) - .withActionListener(() -> setVisible(false)) - .withActionCommand(Messages.getString("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..802845cd 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 { @@ -26,8 +23,8 @@ public MultiSubDialog(String title, boolean modal) { protected void setDialogLocation(Frame f) { Rectangle r = f.getBounds(); - int x = r.x + (r.width - getSize().width) / 2; - int y = r.y + (r.height - getSize().height) / 2; + int x = r.x + (r.width - size.width) / 2; + int y = r.y + (r.height - size.height) / 2; setLocation(x, y); } 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..878cf48f 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,16 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +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,95 +21,76 @@ 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, getText("PreferenceDialog.Title"), true); this.settingsCtrl = settingsCtrl; this.eventEmitter = eventEmitter; setResizable(false); setModalityType(ModalityType.APPLICATION_MODAL); setBounds(100, 100, 650, 700); - getContentPane().setLayout(new BorderLayout()); - - JPanel contentPanel = new JPanel().addTo(getContentPane(), BorderLayout.CENTER); - contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); - contentPanel.setLayout(new BorderLayout(0, 0)); - { - JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP); - AtomicInteger selectedIndex = new AtomicInteger(); - tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - tabbedPane.addChangeListener(l -> { - if (tabbedPane.getSelectedIndex() != 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); - } else { - selectedIndex.set(tabbedPane.getSelectedIndex()); - } - } - }); - contentPanel.add(tabbedPane); - - this.pnlGeneral = new GeneralPanel(gui, settingsCtrl); - tabbedPane.addTab(Messages.getString("PreferenceDialog.TabGeneral"), null, pnlGeneral, null); - this.pnlEpisodeLibrary = - new EpisodeLibraryPanel(settingsCtrl.getSettings().getEpisodeLibrarySettings(), manager, false, userInteractionHandler); - tabbedPane.addTab(Messages.getString("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.pnlOptions = new OptionsPanel(settingsCtrl); - tabbedPane.addTab(Messages.getString("PreferenceDialog.Options"), null, pnlOptions, null); - - this.pnlSerieSources = new SerieProvidersPanel(settingsCtrl); - tabbedPane.addTab(Messages.getString("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"))) - .addComponent( - new JButton(Messages.getString("App.Cancel")) - .withActionListener(() -> setVisible(false)) - .actionCommand("Cancel")); - } + AtomicInteger selectedIdx = new AtomicInteger(); + contentPane + .layout(new BorderLayout()) + .addComponent(BorderLayout.CENTER, new JLabel() + .border(new EmptyBorder(5, 5, 5, 5)) + .layout(new BorderLayout(0, 0)) + .addComponent(new JTabbedPane(SwingConstants.TOP) + .tabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT) + .changeListener(tabbedPane -> { + if (tabbedPane.selectedIndex != selectedIdx.get()) { + var sourcePanel = (PreferencePanelIntf) tabbedPane.getComponentAt(selectedIdx.get()); + if (!sourcePanel.hasValidSettings()) { + tabbedPane.selectedIndex = selectedIdx.get(); + JOptionPane.showMessageDialog(this, getText("PreferenceDialog.invalidInput"), + "Error", JOptionPane.ERROR_MESSAGE); + } else { + selectedIdx.set(tabbedPane.selectedIndex); + } + } + }) + .withTab(getText("PreferenceDialog.TabGeneral"), + pnlGeneral = new GeneralPanel(gui, settingsCtrl)) + .withTab(getText("PreferenceDialog.SerieLibrary"), + pnlEpisodeLibrary = new EpisodeLibraryPanel(settingsCtrl.settings.episodeLibrarySettings, + manager, false, userInteractionHandler)) + .withTab(getText("PreferenceDialog.MovieLibrary"), + pnlMovieLibrary = new MovieLibraryPanel(settingsCtrl.settings.movieLibrarySettings, + manager, false, userInteractionHandler)) + .withTab(getText("PreferenceDialog.Options"), + pnlOptions = new OptionsPanel(settingsCtrl)) + .withTab(getText("PreferenceDialog.SerieSources"), + pnlSerieSources = new SerieProvidersPanel(settingsCtrl))) + ) + .addComponent(BorderLayout.SOUTH, new JPanel() + .layout(new FlowLayout(FlowLayout.RIGHT)) + .addComponent(new JButton(getText("App.OK")) + .defaultButtonFor(getRootPane()) + .actionListener(this::testAndSaveValues) + .actionCommand(getText("App.OK"))) + .addComponent(new JButton(getText("App.Cancel")) + .actionListener(() -> setVisible(false)) + .actionCommand("Cancel"))); } private void testAndSaveValues() { - if (pnlGeneral.hasValidSettings() && - pnlEpisodeLibrary.hasValidSettings() && - pnlMovieLibrary.hasValidSettings() && - pnlOptions.hasValidSettings() && - pnlSerieSources.hasValidSettings()) { + if (pnlGeneral.hasValidSettings() && pnlEpisodeLibrary.hasValidSettings() && + pnlMovieLibrary.hasValidSettings() && pnlOptions.hasValidSettings() && + pnlSerieSources.hasValidSettings()) { pnlGeneral.savePreferenceSettings(); pnlEpisodeLibrary.savePreferenceSettings(); pnlMovieLibrary.savePreferenceSettings(); @@ -126,7 +100,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, 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..e21105f9 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,26 @@ public class ProgressDialog extends MultiSubDialog implements Messenger { @Serial private static final long serialVersionUID = -2320149791421648965L; - @Getter + private JProgressBar progressBar; private JLabel label; - private final Cancelable worker; public ProgressDialog(JFrame frame, Cancelable sft) { - super(frame, Messages.getString("ProgressDialog.Title"), false); - worker = sft; + super(frame, Messages.getText("ProgressDialog.Title"), false); StatusMessenger.instance.addListener(this); - initialize_ui(); + initializeUi(sft); setDialogLocation(frame); repaint(); } public ProgressDialog(Cancelable sft) { - super(Messages.getString("ProgressDialog.Title"), false); - worker = sft; + super(Messages.getText("ProgressDialog.Title"), false); StatusMessenger.instance.addListener(this); - initialize_ui(); + initializeUi(sft); repaint(); } - private void initialize_ui() { + private void initializeUi(Cancelable worker) { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { @@ -45,41 +41,37 @@ public void windowClosing(WindowEvent e) { } }); setBounds(100, 100, 501, 151); - getContentPane().setLayout(new MigLayout("", "[][475px,center][]", "[][40px:n][][]")); - - label = new JLabel(""); - getContentPane().add(label, "cell 1 0 2 1,alignx left"); - - progressBar = new JProgressBar(0, 100); - progressBar.setIndeterminate(true); - getContentPane().add(progressBar, "cell 1 1,grow"); - JButton btnStop = new JButton("Stop!"); - btnStop.addActionListener(arg0 -> worker.cancel(true)); - getContentPane().add(btnStop, "cell 1 2 1 2,alignx left"); + contentPane + .layout(new MigLayout("", "[][475px,center][]", "[][40px:n][][]")) + .addComponent("cell 1 0 2 1,alignx left", label = new JLabel("")) + .addComponent("cell 1 1,grow", progressBar = new JProgressBar(0, 100).indeterminate((true))) + .addComponent("cell 1 2 1 2,alignx left", new JButton("Stop!") + .actionListener(_ -> worker.cancel(true)) + ); } public void setMessage(String message) { - label.setText(message); + label.text = message; repaint(); } public String getMessage() { - return label.getText(); + return label.text; } @Override public void message(String message) { - setMessage(message); + this.message = 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..6b9bed93 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,64 +34,55 @@ 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, - UserInteractionHandler userInteractionHandler) { + UserInteractionHandler userInteractionHandler) { super(frame, title, false); 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")) - .ifPresent(txtFolder::setObject))) - .addComponent("wrap", this.chkRecursive = new JCheckBox(Messages.getString("RenameDialog.RecursiveSearch"))); + contentPane.setLayout(new MigLayout("fill, nogrid", "[]", "[][]20:push[]")); + + TitlePanel.title(Messages.getText("PreferenceDialog.Settings")) + .padding(0) + .paddingLeft(20) + .fillContents(true) + .addTo(contentPane, "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(contentPane, + Messages.getText("PreferenceDialog.SelectFolderForRenameReplace")) + .ifPresent(txtFolder::setObject))) + .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(contentPane, "grow"); } else { - pnlLibrary = new MovieLibraryPanel(settings.getMovieLibrarySettings(), manager, true, userInteractionHandler) - .addTo(getContentPane(), "grow"); + pnlLibrary = + new MovieLibraryPanel(settings.movieLibrarySettings, manager, true, userInteractionHandler).addTo( + contentPane, "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(contentPane, 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() { @@ -106,18 +90,20 @@ private boolean hasValidSettings() { } private void rename(VideoType videoType, Settings settings, Manager manager, - UserInteractionHandler userInteractionHandler) { + 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, + 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..0bf331ab 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.*; import javax.swing.table.*; import java.awt.*; @@ -8,117 +10,93 @@ 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 CustomTable customTable; + private final CustomTable customTable; + + private List selectedSubtitleIdxs; /** * 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(); - this.release = release; - initialize(); - pack(); - setDialogLocation(frame); - setVisible(true); - } - - private void initialize() { - getContentPane().setLayout(new MigLayout("", "[1000px:n,grow,fill]", "[][::100px,fill][grow]")); - JLabel lblNewLabel = - new JLabel(Messages.getString("SelectDialog.SelectCorrectSubtitleThisRelease") - + release.getFileName()); - getContentPane().add(lblNewLabel, "cell 0 0"); - { - JScrollPane scrollPane = new JScrollPane(); - getContentPane().add(scrollPane, "cell 0 1,grow"); - customTable = createCustomTable(); - scrollPane.setViewportView(customTable); - JPanel buttonPane = new JPanel(); - buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); - getContentPane().add(buttonPane, "cell 0 2,grow"); - - new JButton(Messages.getString("App.OK")) + super(frame, getText("SelectDialog.SelectCorrectSubtitle"), true); + this.subtitles = + subtitles.stream().distinct().sorted(Comparator.comparing(Subtitle::getScore).reversed()).toList(); + contentPane + .layout(new MigLayout("", "[1000px:n,grow,fill]", "[][::100px,fill][grow]")) + .addComponent("cell 0 0", + new JLabel(getText("SelectDialog.SelectCorrectSubtitleThisRelease") + release.fileName)) + .addComponent("cell 0 1,grow", new JScrollPane().viewportView(customTable = createCustomTable())) + .addComponent("cell 0 2,grow", new JPanel() + .layout(new FlowLayout(FlowLayout.RIGHT)) + .addComponent(new JButton(getText("App.OK")) .defaultButtonFor(getRootPane()) - .withActionListener(() -> { + .actionListener(() -> { selectedSubtitleIdxs = getSelectedIdxs(); setVisible(false); }) - .withActionCommand(Messages.getString("App.OK")) - .addTo(buttonPane); - - new JButton(Messages.getString("SelectDialog.Everything")) - .withActionListener(() -> { + .actionCommand(getText("App.OK"))) + .addComponent(new JButton(getText("SelectDialog.Everything")) + .actionListener(() -> { 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(() -> { + .actionCommand(getText("App.All"))) + .addComponent(new JButton(getText("App.Cancel")) + .actionListener(() -> { selectedSubtitleIdxs = List.of(); setVisible(false); }) - .withActionCommand(Messages.getString("App.Cancel")) - .addTo(buttonPane); - } + .actionCommand(getText("App.Cancel")))); + pack(); + setDialogLocation(frame); + setVisible(true); } private CustomTable createCustomTable() { - CustomTable customTable = new CustomTable(); - customTable.setModel(SubtitleTableModel.getDefaultSubtitleTableModel()); - final RowSorter sorter = new TableRowSorter<>(customTable.getModel()); - customTable.setRowSorter(sorter); - customTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - - SubtitleTableModel subtitleTableModel = (SubtitleTableModel) customTable.getModel(); - - int columnId = customTable.getColumnIdByName(SubtitleTableColumnName.SELECT); - customTable.getColumnModel().getColumn(columnId).setResizable(false); - customTable.getColumnModel().getColumn(columnId).setPreferredWidth(55); - customTable.getColumnModel().getColumn(columnId).setMaxWidth(55); - - columnId = customTable.getColumnIdByName(SubtitleTableColumnName.SCORE); - customTable.getColumnModel().getColumn(columnId).setResizable(false); - customTable.getColumnModel().getColumn(columnId).setPreferredWidth(60); - customTable.getColumnModel().getColumn(columnId).setMaxWidth(60); - - columnId = customTable.getColumnIdByName(SubtitleTableColumnName.FILENAME); - customTable.getColumnModel().getColumn(columnId).setResizable(true); - customTable.getColumnModel().getColumn(columnId).setMinWidth(500); - - for (Subtitle subtitle : subtitles) { - subtitleTableModel.addRow(subtitle); - } - - return customTable; + SubtitleTableModel subtitleTableModel = SubtitleTableModel.createDefaultSubtitleTableModel(); + CustomTable table = new CustomTable() + .model(subtitleTableModel) + .rowSorter(TableRowSorter::new) + .autoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + + TableColumnModel columnModel = table.getColumnModel(); + + TableColumn column = columnModel.getColumn(table.getColumnIdByName(SubtitleTableColumnName.SELECT)); + column.resizable = false; + column.preferredWidth = 55; + column.maxWidth = 55; + + column = columnModel.getColumn(table.getColumnIdByName(SubtitleTableColumnName.SCORE)); + column.resizable = false; + column.preferredWidth = 60; + column.maxWidth = 60; + + column = columnModel.getColumn(table.getColumnIdByName(SubtitleTableColumnName.FILENAME)); + column.resizable = true; + column.minWidth = 500; + + subtitles.forEach(subtitleTableModel::addRow); + return table; } 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..b1da2ebf 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; @@ -8,18 +10,9 @@ 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 +27,15 @@ 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; @@ -57,85 +48,79 @@ public enum StructureType { } public StructureBuilderDialog(JFrame frame, String title, boolean modal, VideoType videoType, - StructureType structureType, Manager manager, UserInteractionHandler userInteractionHandler, - Function filenameLibraryBuilder) { + StructureType structureType, Manager manager, UserInteractionHandler userInteractionHandler, + Function filenameLibraryBuilder) { super(frame, title, modal); this.videoType = videoType; this.structureType = structureType; - this.manager = manager; this.userInteractionHandler = userInteractionHandler; this.libraryBuilder = filenameLibraryBuilder; - initializeUi(); - generateVideoFiles(); + initializeUI(); + generateVideoFiles(manager); } - 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"); - - this.tagPanel = new JPanel(new MigLayout("flowy, wrap 5", "[150px][150px][150px]")).addTo(panel, "grow, wrap"); - { - if (videoType == VideoType.EPISODE) { - // add tv show tags - buildLabelTable(SerieStructureTag.values()); - } else if (videoType == VideoType.MOVIE) { - // add movie tags - buildLabelTable(MovieStructureTag.values()); - } - if (structureType == StructureType.FOLDER) { - buildLabelTable(FolderStructureTag.values()); - } - } - new JLabel(Messages.getString("StructureBuilderDialog.Structure")).addTo(panel); - this.txtStructure = new JTextField().withColumns(100).addTo(panel, "span, wrap"); - this.txtStructure.getDocument().addDocumentListener(this); - - new JLabel(Messages.getString("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 -> { - setVisible(false); - txtStructure.setText(oldStructure); - dispose(); // this is needed to dispose the dialog and return the control to the window - }) - .withActionCommand("Cancel")); + contentPane + .layout(new MigLayout("insets 10, nogrid")) + .addComponent("wrap", new JLabel(getText("StructureBuilderDialog.AvailableTagsClickToAdd"))) + .addComponent("grow, wrap", + tagPanel = new JPanel(new MigLayout("flowy, wrap 5", "[150px][150px][150px]"))) + .addComponent(new JLabel(getText("StructureBuilderDialog.Structure"))) + .addComponent("span, wrap", + txtStructure = new JTextField() + .columns(100) + .documentListener(this)) + .addComponent(new JLabel(getText("StructureBuilderDialog.Preview"))) + .addComponent(lblPreview = new JLabel()) + .addComponent(BorderLayout.SOUTH, new JPanel(new FlowLayout(FlowLayout.RIGHT)) + .addComponent(new JButton(getText("App.OK")) + .defaultButtonFor(rootPane) + .actionListener(_ -> { + setVisible(false); + dispose(); // this is needed to dispose the dialog and return the control to the window + }) + .actionCommand("OK")) + .addComponent(new JButton(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"))); + + switch (videoType) { + case EPISODE -> buildLabelTable(SerieStructureTag.values()); + case MOVIE -> buildLabelTable(MovieStructureTag.values()); + } + if (structureType == StructureType.FOLDER) { + buildLabelTable(FolderStructureTag.values()); + } } - private void generateVideoFiles() { + private void generateVideoFiles(Manager manager) { 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, false); + case MOVIE -> movieRelease = (MovieRelease) releaseFactory.createRelease( + Path.of("Final.Destination.5.2011.720p.Bluray.x264-TWiZTED.mkv"), + userInteractionHandler, false); } } 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()) - .addTo(tagPanel) - .withMouseListener(new InsertTag()); + new JLabel(structureTag.label) + .withToolTipText(structureTag.description) + .addTo(tagPanel) + .mouseListener(new InsertTag()); } public String showDialog(String structure) { @@ -192,7 +177,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..6aec818a 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,12 +1,15 @@ 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 static org.lodder.subtools.multisubdownloader.Messages.*; +import javax.swing.*; +import javax.swing.table.*; +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; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; import org.lodder.subtools.multisubdownloader.gui.dialog.MultiSubDialog; @@ -14,29 +17,43 @@ 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 private static final long serialVersionUID = -1331536352530988442L; - private final Cancelable searchAction; private final GUI window; - private SearchProgressTableModel tableModel; - private JProgressBar progressBar; + private final SearchProgressTableModel tableModel; + private final JProgressBar progressBar; private boolean completed; public SearchProgressDialog(GUI window, Cancelable searchAction) { - super(window, Messages.getString("SearchProgressDialog.Title"), false); - this.searchAction = searchAction; + super(window, getText("SearchProgressDialog.Title"), false); this.window = window; this.completed = false; - initialize_ui(); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + searchAction.cancel(true); + } + }); + setBounds(100, 100, 601, 300); + + JTable table = new JTable(this.tableModel = new SearchProgressTableModel()); + TableColumn column1 = table.getColumnModel().getColumn(0); + column1.minWidth = 120; + column1.maxWidth = 150; + TableColumn column2 = table.getColumnModel().getColumn(1); + column2.minWidth = 50; + column2.maxWidth = 50; + + contentPane + .layout(new MigLayout("", "[grow,fill][]", "[][][]")) + .addComponent("cell 0 0 2 1", new JScrollPane(table).viewportView(table)) + .addComponent("cell 0 1 2 1,grow", progressBar = new JProgressBar(0, 100).indeterminate(true)) + .addComponent("cell 1 2,alignx left", + new JButton(getText("SearchProgressDialog.Stop")) + .actionListener(_ -> searchAction.cancel(true))); setDialogLocation(window); repaint(); } @@ -44,8 +61,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,36 +98,6 @@ public void onStatus(String message) { this.window.setStatusMessage(message); } - private void initialize_ui() { - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - searchAction.cancel(true); - } - }); - setBounds(100, 100, 601, 300); - getContentPane().setLayout(new MigLayout("", "[grow,fill][]", "[][][]")); - - this.tableModel = new SearchProgressTableModel(); - JTable table = new JTable(tableModel); - - table.getColumnModel().getColumn(0).setMinWidth(120); - table.getColumnModel().getColumn(0).setMaxWidth(150); - table.getColumnModel().getColumn(0).setMinWidth(50); - table.getColumnModel().getColumn(1).setMaxWidth(50); - - JScrollPane tablePane = new JScrollPane(table); - tablePane.setViewportView(table); - getContentPane().add(tablePane, "cell 0 0 2 1"); - - progressBar = new JProgressBar(0, 100); - 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)); - getContentPane().add(btnStop, "cell 1 2,alignx left"); - } private void setVisible() { if (this.completed || this.isVisible()) { 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..6df8435d 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 @@ -1,12 +1,12 @@ package org.lodder.subtools.multisubdownloader.gui.dialog.progress.search; +import static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.table.*; import java.io.Serial; import java.util.HashMap; import java.util.Map; -import org.lodder.subtools.multisubdownloader.Messages; - public class SearchProgressTableModel extends DefaultTableModel { @Serial @@ -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")}); + getText("SearchProgressTableModel.Source"), + getText("SearchProgressTableModel.Queue"), + 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..c9fd716f 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; + @Serial private static final long serialVersionUID = 1L; + @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; + @Serial private static final long serialVersionUID = 1L; + @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..4cc841a3 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,43 +1,30 @@ 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) { - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; @Override protected void addImpl(Component comp, Object constraints, int index) { @@ -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()); } @@ -111,9 +98,9 @@ public interface BuilderOtherIntf { @Setter @Accessors(chain = true, fluent = true) public static class Builder implements - BuilderPanelNewLineIntf, - BuilderSeparatorIntf, - BuilderOtherIntf { + BuilderPanelNewLineIntf, + BuilderSeparatorIntf, + BuilderOtherIntf { private final JCheckBox checkbox; private boolean panelOnNewLine; private LayoutManager panelLayout = new MigLayout("insets 0, novisualpadding, fillx"); @@ -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..0c3d1f9a 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,9 @@ private int requireValidIndex(int index) { } return index; } + + @Override + 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..5a4debb0 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 @@ -3,11 +3,19 @@ import java.util.LinkedList; import java.util.List; -public enum StatusMessenger implements Messenger { - instance; +public class StatusMessenger implements Messenger { + private static final StatusMessenger instance = new StatusMessenger(); private final List statusMessengers = new LinkedList<>(); + private StatusMessenger() { + // private constructor to prevent instantiation + } + + public static StatusMessenger getInstance() { + return instance; + } + public void addListener(Messenger sm) { synchronized (statusMessengers) { statusMessengers.add(sm); @@ -23,7 +31,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..e22f5519 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,30 +1,31 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; +import javax.swing.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() - .orElse(-1); + .filter(i -> this.getColumnName(i).equals(customColumnName.columnName)).findFirst() + .orElse(-1); } public void setColumnVisibility(SearchColumnName searchColumnName, boolean visible) { if (visible) { - unhideColumn(searchColumnName); + showColumn(searchColumnName); } else { hideColumn(searchColumnName); } @@ -33,28 +34,27 @@ public void setColumnVisibility(SearchColumnName searchColumnName, boolean visib public void hideColumn(SearchColumnName searchColumnName) { int columnId = getColumnIdByName(searchColumnName); if (columnId > -1) { - columnSettings.put(searchColumnName, new int[] { - getColumnModel().getColumn(columnId).getMaxWidth(), - getColumnModel().getColumn(columnId).getMinWidth(), - getColumnModel().getColumn(columnId).getPreferredWidth() }); - getColumnModel().getColumn(columnId).setMaxWidth(0); - getColumnModel().getColumn(columnId).setMinWidth(0); - getColumnModel().getColumn(columnId).setPreferredWidth(0); + TableColumn column = columnModel.getColumn(columnId); + columnSettings.put(searchColumnName, new int[]{ column.maxWidth, column.minWidth, column.preferredWidth }); + column.maxWidth = 0; + column.minWidth = 0; + column.preferredWidth = 0; } } - public void unhideColumn(SearchColumnName searchColumnName) { + public void showColumn(SearchColumnName searchColumnName) { int columnId = getColumnIdByName(searchColumnName); if (columnId > -1) { + TableColumn column = getColumnModel().getColumn(columnId); if (columnSettings.containsKey(searchColumnName)) { - getColumnModel().getColumn(columnId).setMaxWidth(columnSettings.get(searchColumnName)[0]); - getColumnModel().getColumn(columnId).setMinWidth(columnSettings.get(searchColumnName)[1]); - getColumnModel().getColumn(columnId).setPreferredWidth( - columnSettings.get(searchColumnName)[2]); + int[] columnSetting = columnSettings.get(searchColumnName); + column.maxWidth = columnSetting[0]; + column.minWidth = columnSetting[1]; + column.preferredWidth = columnSetting[2]; } else { - getColumnModel().getColumn(columnId).setMaxWidth(MAX_WIDTH); - getColumnModel().getColumn(columnId).setMinWidth(MIN_WIDTH); - getColumnModel().getColumn(columnId).setPreferredWidth(PREFERRED_WIDTH); + column.maxWidth = MAX_WIDTH; + column.minWidth = MIN_WIDTH; + column.preferredWidth = PREFERRED_WIDTH; } } } @@ -62,8 +62,8 @@ public void unhideColumn(SearchColumnName searchColumnName) { public boolean isHideColumn(SearchColumnName searchColumnName) { int columnId = getColumnIdByName(searchColumnName); if (columnId > -1) { - return getColumnModel().getColumn(columnId).getMinWidth() == 0 - && getColumnModel().getColumn(columnId).getPreferredWidth() == 0; + TableColumn column = getColumnModel().getColumn(columnId); + return column.minWidth == 0 && column.preferredWidth == 0; } return true; } @@ -79,5 +79,4 @@ public String getToolTipText(MouseEvent e) { } return null; } - } 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..19f7487f 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); + public static SubtitleTableModel createDefaultSubtitleTableModel() { + 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..00f54eed 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,15 @@ import javax.swing.event.*; import java.awt.*; import java.io.Serial; -import java.util.Arrays; -import java.util.Optional; +import java.util.Objects; 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 +23,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 +66,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 +108,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 +140,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 = Objects.equals(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 +165,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..485b3c8f 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 @@ -14,10 +14,11 @@ import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.sublibrary.util.BooleanConsumer; -public abstract class MyTextFieldCommon> extends JTextField implements - MyTextFieldToStringMapperIntf, - MyTextFieldToObjectMapperIntf, - MyTextFieldOthersIntf { +public abstract sealed class MyTextFieldCommon> extends JTextField implements + MyTextFieldToStringMapperIntf, + MyTextFieldToObjectMapperIntf, + MyTextFieldOthersIntf + permits MyTextFieldInteger, MyTextFieldPath, MyTextFieldString { @Serial private static final long serialVersionUID = -393882042554264226L; @@ -28,8 +29,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 +79,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 +134,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 +164,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/MyTextFieldInteger.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java index bc5f58ac..d393aba1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java @@ -6,7 +6,7 @@ import org.apache.commons.lang3.StringUtils; -public class MyTextFieldInteger extends MyTextFieldCommon { +public final class MyTextFieldInteger extends MyTextFieldCommon { @Serial private static final long serialVersionUID = -8526638589445703452L; @@ -28,10 +28,10 @@ private MyTextFieldInteger() { } - public static MyTextFieldOthersIntf builder() { + public static MyTextFieldOthersIntf builder() { return new MyTextFieldInteger() - .withToStringMapper(TO_STRING_MAPPER) - .withToObjectMapper(TO_OBJECT_MAPPER) - .withValueVerifier(INT_VERIFIER); + .withToStringMapper(TO_STRING_MAPPER) + .withToObjectMapper(TO_OBJECT_MAPPER) + .withValueVerifier(INT_VERIFIER); } } 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/MyTextFieldPath.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java index c77d252f..68ecd394 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java @@ -8,11 +8,12 @@ import org.apache.commons.lang3.StringUtils; -public class MyTextFieldPath extends MyTextFieldCommon { +public final class MyTextFieldPath extends MyTextFieldCommon { @Serial private static final long serialVersionUID = -8526638589445703452L; - private static final Function TO_STRING_MAPPER = path -> path == null ? null : path.toAbsolutePath().toString(); + private static final Function TO_STRING_MAPPER = + path -> path == null ? null : path.toAbsolutePath().toString(); private static final Function TO_OBJECT_MAPPER = s -> s == null ? null : Path.of(s); public static final Predicate ABSOLUTE_PATH_VERIFIER = text -> { try { @@ -26,10 +27,10 @@ private MyTextFieldPath() { } - public static MyTextFieldOthersIntf builder() { + public static MyTextFieldOthersIntf builder() { return new MyTextFieldPath() - .withToStringMapper(TO_STRING_MAPPER) - .withToObjectMapper(TO_OBJECT_MAPPER) - .withValueVerifier(ABSOLUTE_PATH_VERIFIER); + .withToStringMapper(TO_STRING_MAPPER) + .withToObjectMapper(TO_OBJECT_MAPPER) + .withValueVerifier(ABSOLUTE_PATH_VERIFIER); } } 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..07830a38 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 @@ -4,26 +4,26 @@ import java.util.function.Function; import java.util.function.Predicate; -public class MyTextFieldString extends MyTextFieldCommon { +public final class MyTextFieldString extends MyTextFieldCommon { @Serial private static final long serialVersionUID = -8526638589445703452L; private static final Function 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); + .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..6826108d 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,31 +1,28 @@ 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 { +public abstract sealed class InputPanel extends JPanel permits SearchFileInputPanel, SearchTextInputPanel { @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..a9ac6219 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,39 @@ 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 ch.qos.logback.classic.Logger; 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 Logger ROOT = (Logger) org.slf4j.LoggerFactory.getLogger(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..0bf87091 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,22 +1,14 @@ 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 { +public final class SearchFileInputPanel extends InputPanel { @Serial private static final long serialVersionUID = 6522020963519514345L; @@ -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..5ae16b31 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 { +public final class SearchTextInputPanel extends InputPanel { @Serial private static final long serialVersionUID = 7030171360517948253L; - private MyComboBox cbxVideoType; - protected JTextField txtInputSeason; - protected JTextField txtInputEpisode; - protected JTextField txtQualityVersion; + + private JComboBox cbxVideoType; private JTextField txtInputVideoName; + private JTextField txtInputSeason; + private JTextField txtInputEpisode; + private JTextField txtQualityVersion; 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(t -> getText(t.msgCode)); + 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..b016554c 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,25 @@ import static java.util.function.Predicate.*; -import java.util.Arrays; +import javax.swing.*; +import javax.swing.table.*; +import java.awt.*; +import java.io.Serial; 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; + @Serial private static final long serialVersionUID = 1L; + private final SettingsControl settingsCtrl; private final ScrollTable unusedPatternsTable; private final ScrollTable usedPatternsTable; @@ -41,20 +29,23 @@ 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(); } private static class ScrollTable extends Container { - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; private final JScrollPane scrollPane; private final JTable table; @@ -73,8 +64,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 +75,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 +136,7 @@ public List getItems() { @Override public Component[] getComponents() { - return new Component[] { scrollPane, table }; + return new Component[]{ scrollPane, table }; } @Override @@ -188,13 +179,13 @@ protected void moveRuleRowUp() { } public void loadPreferenceSettings() { - Source.values().stream().filter(not(settingsCtrl.getSettings().getOptionsDefaultSelectionQualityList()::contains)) - .forEach(unusedPatternsTable::addItem); - settingsCtrl.getSettings().getOptionsDefaultSelectionQualityList().forEach(usedPatternsTable::addItem); + Source.values().stream().filter(not(settingsCtrl.settings.optionsDefaultSelectionQualityList::contains)) + .forEach(unusedPatternsTable::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..041f4e99 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(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..e5c1c323 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,102 +29,105 @@ 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(); public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, Manager manager, - UserInteractionHandler userInteractionHandler) { + UserInteractionHandler userInteractionHandler) { 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(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + languageScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.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(); } } @@ -156,15 +151,15 @@ private void addLanguage(Language lang, String langCode) { private Function getLibraryStructureBuilder() { return structure -> FilenameLibraryBuilder.builder() - .structure(structure) - .replaceSpace(chkReplaceSpace.isSelected()) - .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()) - .includeLanguageCode(chkIncludeLanguageCode.isSelected()) - .languageTags(languageMapping.toSettingsMap()) - .useTvdbName(false) - .tvdbAdapter(null) - .rename(true) - .build(); + .structure(structure) + .replaceSpace(chkReplaceSpace.isSelected()) + .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedValue()) + .includeLanguageCode(chkIncludeLanguageCode.isSelected()) + .languageTags(languageMapping.toSettingsMap()) + .useTvdbName(false) + .tvdbAdapter(null) + .rename(true) + .build(); } @Override @@ -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.filenameStructure); + chkReplaceSpace.setSelected(librarySettings.filenameReplaceSpace); + cbxReplaceSpaceChar.setSelectedItem(librarySettings.filenameReplacingSpaceChar); + chkIncludeLanguageCode.setSelected(librarySettings.includeLanguageCode); + 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.filenameStructure = txtFileStructure.getText(); + librarySettings.filenameReplaceSpace = chkReplaceSpace.isSelected(); + librarySettings.filenameReplacingSpaceChar = 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,18 +227,22 @@ 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() - .forEach(this::updateBorder); + languageComponentsMap.values() + .stream() + .map(langComp -> langComp.cmbLanguage.getSelectedValue()) + .distinct() + .forEach(this::updateBorder); } else { languageComponentsMap.values() - .forEach(langComps -> langComps.cmbLanguage.setBorder(getDefaultBorder(langComps))); + .forEach(langComps -> langComps.cmbLanguage.setBorder(getDefaultBorder(langComps))); } } @@ -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..be2656ed 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,81 +19,77 @@ 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) { + UserInteractionHandler userInteractionHandler) { super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titelPanel = TitlePanel.title(Messages.getString("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); - } + 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(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(); } private Function getLibraryStructureBuilder() { return structure -> PathLibraryBuilder.builder() - .structure(structure) - .replaceSpace(chkReplaceSpace.isSelected()) - .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()) - .useTvdbName(false) - .tvdbAdapter(null) - .libraryFolder(txtLibraryFolder.getObject()) - .move(true) - .build(); + .structure(structure) + .replaceSpace(chkReplaceSpace.isSelected()) + .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedValue()) + .useTvdbName(false) + .tvdbAdapter(null) + .libraryFolder(txtLibraryFolder.getObject()) + .move(true) + .build(); } @Override @@ -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.folder); + txtFolderStructure.setText(librarySettings.folderStructure); + chkRemoveEmptyFolder.setSelected(librarySettings.removeEmptyFolders); + chkReplaceSpace.setSelected(librarySettings.folderReplaceSpace); + cbxReplaceSpaceChar.setSelectedItem(librarySettings.folderReplacingSpaceChar); } public void savePreferenceSettings() { - librarySettings - .setLibraryFolder(txtLibraryFolder.getObject()) - .setLibraryFolderStructure(txtFolderStructure.getText()) - .setLibraryRemoveEmptyFolders(chkRemoveEmptyFolder.isSelected()) - .setLibraryFolderReplaceSpace(chkReplaceSpace.isSelected()) - // if (pnlStructureFolder.isReplaceSpaceSelected()) { - .setLibraryFolderReplacingSpaceChar(cbxReplaceSpaceChar.getSelectedItem()); - // } + librarySettings.folder = txtLibraryFolder.getObject(); + librarySettings.folderStructure = txtFolderStructure.getText(); + librarySettings.removeEmptyFolders = chkRemoveEmptyFolder.isSelected(); + librarySettings.folderReplaceSpace = chkReplaceSpace.isSelected(); + librarySettings.folderReplacingSpaceChar = 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 deleted file mode 100644 index a96f3d32..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructurePanel.java +++ /dev/null @@ -1,62 +0,0 @@ -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 java.awt.event.ActionListener; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.experimental.ExtensionMethod; - -@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")); - - this.cbxReplaceSpaceChar = new MyComboBox<>(new String[] { "-", ".", "_" }); - - this.chkReplaceSpace = new JCheckBox(Messages.getString("PreferenceDialog.ReplaceSpaceWith")) - .addCheckedChangeListener(cbxReplaceSpaceChar::setEnabled); - } - - @SuppressWarnings("unchecked") - public T addBuildStructureAction(ActionListener buildStructureAction) { - btnBuildStructure.addActionListener(buildStructureAction); - return (T) this; - } - - public String getReplaceSpaceChar() { - return this.getCbxReplaceSpaceChar().getSelectedItem(); - } - - public void setReplaceSpaceChar(String s) { - this.getCbxReplaceSpaceChar().setSelectedItem(s); - } - - public boolean isReplaceSpaceSelected() { - return this.getChkReplaceSpace().isSelected(); - } - - public void setReplaceSpaceSelected(boolean b) { - this.getChkReplaceSpace().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..7466e11b 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")) - .margin(0).padding(0).paddingLeft(20).addTo(this, "span, growx"); + 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); - - 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"))) - .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); + this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); + + 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(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.backupSubtitle); + txtBackupSubtitlePath.setObject(librarySettings.backupSubtitlePath); + chkBackupUseSourceFileName.setSelected(librarySettings.backupUseWebsiteFileName); } public void savePreferenceSettings() { - librarySettings - .setLibraryBackupSubtitle(chkBackupSubtitle.isSelected()) - .setLibraryBackupSubtitlePath(txtBackupSubtitlePath.getObject()) - .setLibraryBackupUseWebsiteFileName(chkBackupUseSourceFileName.isSelected()); + librarySettings.backupSubtitle = chkBackupSubtitle.isSelected(); + librarySettings.backupSubtitlePath = txtBackupSubtitlePath.getObject(); + librarySettings.backupUseWebsiteFileName = 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..ec2070b7 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, - UserInteractionHandler userInteractionHandler) { + 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")) - .margin(0).padding(0).paddingLeft(20).addTo(this, "span, growx"); + 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) - .addTo(performActionPanel, "wrap"); + 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) + this.pnlStructureFolder = + new StructureFolderPanel(librarySettings, videoType, manager, userInteractionHandler) .addTo(performActionPanel, "hidemode 3, wrap, span, growx"); - this.pnlStructureFile = new StructureFilePanel(librarySettings, videoType, manager, userInteractionHandler) + 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.action); + chkUseTVDBNaming.setSelected(librarySettings.useTVDBNaming); + cbxLibraryOtherFileAction.setSelectedItem(librarySettings.otherFileAction); 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.action = this.cbxLibraryAction.getSelectedValue(); + librarySettings.useTVDBNaming = this.chkUseTVDBNaming.isSelected(); + librarySettings.otherFileAction = 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..f3ddb1e2 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; @@ -32,14 +32,21 @@ public ReleaseFactory(Settings settings, Manager manager) { } public Release createRelease(Path file, UserInteractionHandler userInteractionHandler) { + return createRelease(file, userInteractionHandler, true); + } + + public Release createRelease(Path file, UserInteractionHandler userInteractionHandler, + boolean validate) { 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(); + if (validate) { + releaseControl.process(); + } + 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..50855a3b 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,27 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting; +import static manifold.ext.props.rt.api.PropOption.*; + +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; -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,28 +29,30 @@ 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 + Map defaultWeightsNew = new HashMap<>(defaultWeights); // clone - replaceReservedKeywords(release, defaultWeights); + replaceReservedKeywords(release, defaultWeightsNew); /* get a list of tags */ - List tags = ReleaseParser.getQualityKeyWords(release.getQuality()); - if (StringUtils.isNotBlank(release.getReleaseGroup())) { - tags.add(release.getReleaseGroup().toLowerCase()); + List tags = new ArrayList<>(ReleaseParser.getQualityKeyWords(release.quality)); + if (StringUtils.isNotBlank(release.releaseGroup)) { + tags.add(release.releaseGroup.toLowerCase()); } - /* only store tags for which we have a weight defined */ - tags.retainAll(defaultWeights.keySet()); - /* store weights for this release */ - for (String tag : tags) { - int weight = defaultWeights.get(tag); - this.maxScore += weight; - this.weights.put(tag, weight); - } + tags.forEach(tag -> + defaultWeightsNew.entrySet().stream() + /* only store tags for which we have a weight defined */ + .filter(entry -> Pattern.compile(entry.getKey()).matcher(tag).find()) + .map(Map.Entry::getValue) + .findFirst() + .ifPresent(weight -> { + this.maxScore += weight; + this.weights.put(tag, weight); + })); } private void replaceReservedKeywords(Release release, Map weights) { 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..69b9e302 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 @@ -3,22 +3,22 @@ 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.MovieStructureTag; 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; +import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; 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 { +public final class FilenameLibraryBuilder extends LibraryBuilder { private final String structure; private final boolean replaceSpace; @@ -27,8 +27,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; @@ -39,17 +40,17 @@ private FilenameLibraryBuilder(String structure, boolean replaceSpace, char repl } public static FilenameLibraryBuilder fromSettings(LibrarySettings librarySettings, Manager manager, - UserInteractionHandler userInteractionHandler) { + UserInteractionHandler userInteractionHandler) { return FilenameLibraryBuilder.builder() - .structure(librarySettings.getLibraryFolderStructure()) - .replaceSpace(librarySettings.isLibraryFolderReplaceSpace()) - .replacingSpaceChar(librarySettings.getLibraryFolderReplacingSpaceChar()) - .includeLanguageCode(librarySettings.isLibraryIncludeLanguageCode()) - .languageTags(librarySettings.getLangCodeMap()) - .useTvdbName(librarySettings.isLibraryUseTVDBNaming()) - .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) - .rename(librarySettings.hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)) - .build(); + .structure(librarySettings.folderStructure) + .replaceSpace(librarySettings.folderReplaceSpace) + .replacingSpaceChar(librarySettings.folderReplacingSpaceChar) + .includeLanguageCode(librarySettings.includeLanguageCode) + .languageTags(librarySettings.langCodeMap) + .useTvdbName(librarySettings.useTVDBNaming) + .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) + .rename(librarySettings.hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)) + .build(); } public static FilenameLibraryBuilderStructureIntf builder() { @@ -94,16 +95,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,64 +115,82 @@ 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); + if (rename && StringUtils.isNotBlank(structure)) { + String filename = switch (release) { + case TvRelease tvRelease -> { + String fName = structure; + // order is important! + fName = replace(fName, SerieStructureTag.SHOW_NAME, getShowName(tvRelease.name)); + fName = + replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_LONG, tvRelease.episodeNumbers, + true); + fName = + replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_SHORT, tvRelease.episodeNumbers, + false); + fName = replace(fName, SerieStructureTag.SEASON_LONG, formattedNumber(tvRelease.season, true)); + fName = replace(fName, SerieStructureTag.SEASON_SHORT, formattedNumber(tvRelease.season, false)); + fName = replace(fName, SerieStructureTag.EPISODE_LONG, + formattedNumber(tvRelease.firstEpisodeNumber, true)); + fName = replace(fName, SerieStructureTag.EPISODE_SHORT, + formattedNumber(tvRelease.firstEpisodeNumber, false)); + fName = replace(fName, SerieStructureTag.TITLE, tvRelease.title); + fName = replace(fName, SerieStructureTag.QUALITY, release.quality); + fName = replace(fName, SerieStructureTag.RELEASE_GROUP, release.releaseGroup); + + fName += "." + release.extension; + yield fName; + } + case MovieRelease movieRelease -> { + String fName = structure; + // order is important! + fName = replace(fName, MovieStructureTag.MOVIE_TITLE, getShowName(movieRelease.name)); + fName = replace(fName, MovieStructureTag.YEAR, formattedNumber(movieRelease.year, false)); + fName = replace(fName, MovieStructureTag.QUALITY, release.quality); + fName = replace(fName, MovieStructureTag.RELEASE_GROUP, release.releaseGroup); + + fName += "." + release.extension; + yield fName; + } + }; + + 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; + String subFileName = filename; if (version != null) { - filename = filename.substring(0, filename.indexOf(extension)) + "-v" + version + "." + release.getExtension(); + subFileName = subFileName.substring(0, subFileName.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); + subFileName = changeExtension(subFileName, !"".equals(langCode) ? ".$langCode.srt" : ".srt"); } else { - filename = changeExtension(filename, ".srt"); + subFileName = changeExtension(subFileName, ".srt"); } - - filename = StringUtil.removeIllegalWindowsChars(filename); + subFileName = subFileName.removeIllegalWindowsChars(); if (replaceSpace) { - filename = filename.replace(' ', replacingSpaceChar); + subFileName = subFileName.replace(' ', replacingSpaceChar); } - return filename; + return subFileName; } /** 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..7c455bbd 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,19 +4,15 @@ 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 { +public abstract sealed class LibraryBuilder permits FilenameLibraryBuilder, PathLibraryBuilder { private final boolean useTvdb; private final TheTvdbAdapter tvdbAdapter; @@ -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); + .map(episode -> formattedNumber(episode, leadingZero)) + .collect(Collectors.joining(separator)); + 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..dfb3960a 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,12 +15,8 @@ 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 { +public final class PathLibraryBuilder extends LibraryBuilder { private final String structure; private final boolean replaceSpace; @@ -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,16 +34,17 @@ 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()) - .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) - .libraryFolder(librarySettings.getLibraryFolder()) - .move(librarySettings.hasAnyLibraryAction(LibraryActionType.MOVE, LibraryActionType.MOVEANDRENAME)) - .build(); + .structure(librarySettings.folderStructure) + .replaceSpace(librarySettings.folderReplaceSpace) + .replacingSpaceChar(librarySettings.folderReplacingSpaceChar) + .useTvdbName(librarySettings.useTVDBNaming) + .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) + .libraryFolder(librarySettings.folder) + .move(librarySettings.hasAnyLibraryAction(LibraryActionType.MOVE, LibraryActionType.MOVEANDRENAME)) + .build(); } public static PathLibraryBuilderStructureIntf builder() { @@ -86,15 +85,11 @@ public interface PathLibraryBuilderBuildIntf { @Setter @Accessors(chain = true, fluent = true) - public static class PathLibraryBuilderBuilder implements - PathLibraryBuilderStructureIntf, - PathLibraryBuilderReplaceSpaceIntf, - PathLibraryBuilderReplaceSpaceCharIntf, - PathLibraryBuilderUseTvdbNameIntf, - PathLibraryBuilderTvdbAdapterIntf, - PathLibraryBuilderLibraryFolderIntf, - PathLibraryBuilderMoveIntf, - PathLibraryBuilderBuildIntf { + public static class PathLibraryBuilderBuilder + implements PathLibraryBuilderStructureIntf, PathLibraryBuilderReplaceSpaceIntf, + PathLibraryBuilderReplaceSpaceCharIntf, PathLibraryBuilderUseTvdbNameIntf, + PathLibraryBuilderTvdbAdapterIntf, PathLibraryBuilderLibraryFolderIntf, PathLibraryBuilderMoveIntf, + PathLibraryBuilderBuildIntf { private String structure; private boolean replaceSpace; @@ -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.RELEASE_GROUP, tvRelease.releaseGroup); 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..d0410a4d 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 @@ -1,11 +1,11 @@ package org.lodder.subtools.multisubdownloader.serviceproviders; -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.cli.CliOption; import org.lodder.subtools.multisubdownloader.framework.Container; -import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; @@ -18,19 +18,15 @@ 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) { this.app = app; /* Resolve the SubtitleProviderStore from the IoC Container */ - final SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); + final SubtitleProviderStore subtitleProviderStore = app.makeSubtitleProviderStore(); /* Create the SubtitleProvider */ subtitleProvider = createProvider(userInteractionHandler); @@ -43,34 +39,34 @@ public void register(Container app, UserInteractionHandler userInteractionHandle } private SubtitleProvider createProvider(UserInteractionHandler userInteractionHandler) { - Settings settings = (Settings) this.app.make("Settings"); - Preferences preferences = (Preferences) this.app.make("Preferences"); - Manager manager = (Manager) this.app.make("Manager"); + Settings settings = app.makeSettings(); + Manager manager = app.makeManager(); 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, + app.makePreferences().getBoolean(CliOption.SPEEDY.value, false), + manager, userInteractionHandler); } } // TODO is this still needed? - private void registerListener(SubtitleProviderStore subtitleProviderStore, UserInteractionHandler userInteractionHandler) { - /* Resolve the EventEmitter from the IoC Container */ - Emitter emitter = (Emitter) app.make("EventEmitter"); + private void registerListener(SubtitleProviderStore subtitleProviderStore, + UserInteractionHandler userInteractionHandler) { /* Listen for settings-change */ - emitter.listen("providers.settings.change", event -> { + app.makeEventEmitter().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..e577cafa 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,26 +1,21 @@ 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; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; -import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.Local; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; -import org.lodder.subtools.sublibrary.Manager; public class LocalServiceProvider implements ServiceProvider { - protected Container app; - protected SubtitleProvider subtitleProvider; private UserInteractionHandler userInteractionHandler; - - @Override - public int getPriority() { - /* We define a priority lower than SubtitleServiceProvider */ - return 1; - } + private Container app; + private SubtitleProvider subtitleProvider; + /* We define a priority lower than SubtitleServiceProvider */ + @val @override int priority = 1; @Override public void register(Container app, UserInteractionHandler userInteractionHandler) { @@ -28,7 +23,7 @@ public void register(Container app, UserInteractionHandler userInteractionHandle this.userInteractionHandler = userInteractionHandler; /* Resolve the SubtitleProviderStore from the IoC Container */ - final SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); + final SubtitleProviderStore subtitleProviderStore = app.makeSubtitleProviderStore(); /* Create the SubtitleProvider */ subtitleProvider = createProvider(); @@ -41,17 +36,12 @@ public void register(Container app, UserInteractionHandler userInteractionHandle } private SubtitleProvider createProvider() { - Settings settings = (Settings) this.app.make("Settings"); - Manager manager = (Manager) app.make("Manager"); - return new Local(settings, manager, userInteractionHandler); + return new Local(app.makeSettings(), app.makeManager(), userInteractionHandler); } private void registerListener(final SubtitleProviderStore subtitleProviderStore) { - /* Resolve the EventEmitter from the IoC Container */ - Emitter emitter = (Emitter) app.make("EventEmitter"); - /* Listen for settings-change */ - emitter.listen("providers.settings.change", event -> { + app.makeEventEmitter().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/OpenSubtitlesServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java index 535722a6..f0e2809a 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,33 +1,29 @@ 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; -import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JOpenSubAdapter; -import org.lodder.subtools.sublibrary.Manager; 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) { this.app = app; /* Resolve the SubtitleProviderStore from the IoC Container */ - SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); + SubtitleProviderStore subtitleProviderStore = app.makeSubtitleProviderStore(); /* Create the SubtitleProvider */ subtitleProvider = createProvider(userInteractionHandler); @@ -40,27 +36,25 @@ public void register(Container app, UserInteractionHandler userInteractionHandle } private SubtitleProvider createProvider(UserInteractionHandler userInteractionHandler) { - Settings settings = (Settings) this.app.make("Settings"); - Manager manager = (Manager) this.app.make("Manager"); + Settings settings = app.makeSettings(); 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); + return new JOpenSubAdapter(loginEnabled, username, password, app.makeManager(), userInteractionHandler); } - private void registerListener(SubtitleProviderStore subtitleProviderStore, UserInteractionHandler userInteractionHandler) { - /* Resolve the EventEmitter from the IoC Container */ - Emitter emitter = (Emitter) app.make("EventEmitter"); + private void registerListener(SubtitleProviderStore subtitleProviderStore, + UserInteractionHandler userInteractionHandler) { /* Listen for settings-change */ - emitter.listen("providers.settings.change", event -> { + app.makeEventEmitter().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/PodnapisiServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java index d64e1261..bc7a1c48 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,29 +1,19 @@ 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; -import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; -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) { - /* Resolve the SubtitleProviderStore from the IoC Container */ - SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); - - /* Create the SubtitleProvider */ - Manager manager = (Manager) app.make("Manager"); - SubtitleProvider podnapisiAdapter = new JPodnapisiAdapter(manager, userInteractionHandler); - /* Add the SubtitleProvider to the store */ - subtitleProviderStore.addProvider(podnapisiAdapter); + app.makeSubtitleProviderStore().addProvider(new JPodnapisiAdapter(app.makeManager(), 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..4a03775a 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,34 +1,23 @@ 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; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; -import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JSubsceneAdapter; -import org.lodder.subtools.sublibrary.Manager; 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) { - /* Resolve the SubtitleProviderStore from the IoC Container */ - SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); - - /* Create the SubtitleProvider */ - Manager manager = (Manager) app.make("Manager"); - JSubsceneAdapter subsceneAdapter = new JSubsceneAdapter(manager, userInteractionHandler); - /* Add the SubtitleProvider to the store */ - subtitleProviderStore.addProvider(subsceneAdapter); + app.makeSubtitleProviderStore().addProvider(new JSubsceneAdapter(app.makeManager(), 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..3a86b3a3 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,30 +1,20 @@ 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; -import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; -import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JTVsubtitlesAdapter; -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) { - /* Resolve the SubtitleProviderStore from the IoC Container */ - SubtitleProviderStore subtitleProviderStore = (SubtitleProviderStore) app.make("SubtitleProviderStore"); - - /* Create the SubtitleProvider */ - Manager manager = (Manager) app.make("Manager"); - SubtitleProvider tvsubtitlesAdapter = new JTVsubtitlesAdapter(manager, userInteractionHandler); - /* Add the SubtitleProvider to the store */ - subtitleProviderStore.addProvider(tvsubtitlesAdapter); + app.makeSubtitleProviderStore().addProvider(new JTVsubtitlesAdapter(app.makeManager(), 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..790618c8 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,402 +29,401 @@ 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 { // SETTINGS SETTINGS_VERSION(createSettingInt() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getSettingsVersion) - .valueSetter(Settings::setSettingsVersion) - .defaultValue(0)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getSettingsVersion) + .valueSetter(Settings::setSettingsVersion) + .defaultValue(0)), LAST_OUTPUT_DIR(createSettingPath() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(settings -> MemoryFolderChooser.getInstance().getMemory()) - .valueSetter(Settings::setLastOutputDir) - .defaultValue(Path.of(""))), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(settings -> MemoryFolderChooser.getInstance().getMemory()) + .valueSetter(Settings::setLastOutputDir) + .defaultValue(Path.of(""))), GENERAL_DEFAULT_INCOMING_FOLDER(createSettingPath() - .rootElementFunction(SettingsControl::getSettings) - .collectionGetter(Settings::getDefaultIncomingFolders)), + .rootElementFunction(SettingsControl::getSettings) + .collectionGetter(Settings::getDefaultIncomingFolders)), LOCAL_SUBTITLES_SOURCES_FOLDERS(createSettingPath() - .rootElementFunction(SettingsControl::getSettings) - .collectionGetter(Settings::getLocalSourcesFolders)), + .rootElementFunction(SettingsControl::getSettings) + .collectionGetter(Settings::getLocalSourcesFolders)), EXCLUDE_ITEM(createSetting(PathOrRegex.class) - .toStringMapper(PathOrRegex::getValue) - .toObjectMapper(PathOrRegex::new) - .rootElementFunction(SettingsControl::getSettings) - .collectionGetter(Settings::getExcludeList)), + .toStringMapper(PathOrRegex::getValue) + .toObjectMapper(PathOrRegex::new) + .rootElementFunction(SettingsControl::getSettings) + .collectionGetter(Settings::getExcludeList)), DEFAULT_SELECTION_QUALITY(createSettingEnum(VideoPatterns.Source.class) - .rootElementFunction(SettingsControl::getSettings) - .collectionGetter(Settings::getOptionsDefaultSelectionQualityList)), + .rootElementFunction(SettingsControl::getSettings) + .collectionGetter(Settings::getOptionsDefaultSelectionQualityList)), DEFAULT_SELECTION_QUALITY_ENABLED(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionsDefaultSelection) - .valueSetter(Settings::setOptionsDefaultSelection) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionsDefaultSelection) + .valueSetter(Settings::setOptionsDefaultSelection) + .defaultValue(false)), OPTIONS_LANGUAGE(createSettingEnum(Language.class) - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getLanguage) - .valueSetter(Settings::setLanguage) - .defaultValue(Language.ENGLISH)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getLanguage) + .valueSetter(Settings::setLanguage) + .defaultValue(Language.ENGLISH)), OPTIONS_ALWAYS_CONFIRM(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionsAlwaysConfirm) - .valueSetter(Settings::setOptionsAlwaysConfirm) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionsAlwaysConfirm) + .valueSetter(Settings::setOptionsAlwaysConfirm) + .defaultValue(false)), OPTIONS_CONFIRM_MAPPING(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionsConfirmProviderMapping) - .valueSetter(Settings::setOptionsConfirmProviderMapping) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionsConfirmProviderMapping) + .valueSetter(Settings::setOptionsConfirmProviderMapping) + .defaultValue(true)), OPTIONS_MIN_AUTOMATIC_SELECTION(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionsMinAutomaticSelection) - .valueSetter(Settings::setOptionsMinAutomaticSelection) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionsMinAutomaticSelection) + .valueSetter(Settings::setOptionsMinAutomaticSelection) + .defaultValue(false)), OPTIONS_MIN_AUTOMATIC_SELECTION_VALUE(createSettingInt() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getOptionsMinAutomaticSelectionValue) - .valueSetter(Settings::setOptionsMinAutomaticSelectionValue) - .defaultValue(0)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getOptionsMinAutomaticSelectionValue) + .valueSetter(Settings::setOptionsMinAutomaticSelectionValue) + .defaultValue(0)), OPTION_SUBTITLE_EXACT_MATCH(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionSubtitleExactMatch) - .valueSetter(Settings::setOptionSubtitleExactMatch) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionSubtitleExactMatch) + .valueSetter(Settings::setOptionSubtitleExactMatch) + .defaultValue(true)), OPTION_SUBTITLE_KEYWORD_MATCH(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionSubtitleKeywordMatch) - .valueSetter(Settings::setOptionSubtitleKeywordMatch) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionSubtitleKeywordMatch) + .valueSetter(Settings::setOptionSubtitleKeywordMatch) + .defaultValue(true)), OPTION_SUBTITLE_EXCLUDE_HEARING_IMPAIRED(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionSubtitleExcludeHearingImpaired) - .valueSetter(Settings::setOptionSubtitleExcludeHearingImpaired) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionSubtitleExcludeHearingImpaired) + .valueSetter(Settings::setOptionSubtitleExcludeHearingImpaired) + .defaultValue(true)), OPTIONS_SHOW_ONLY_FOUND(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionsShowOnlyFound) - .valueSetter(Settings::setOptionsShowOnlyFound) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionsShowOnlyFound) + .valueSetter(Settings::setOptionsShowOnlyFound) + .defaultValue(true)), OPTIONS_STOP_ON_SEARCH_ERROR(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionsStopOnSearchError) - .valueSetter(Settings::setOptionsStopOnSearchError) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionsStopOnSearchError) + .valueSetter(Settings::setOptionsStopOnSearchError) + .defaultValue(false)), OPTION_RECURSIVE(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isOptionRecursive) - .valueSetter(Settings::setOptionRecursive) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isOptionRecursive) + .valueSetter(Settings::setOptionRecursive) + .defaultValue(false)), PROCESS_EPISODE_SOURCE(createSettingEnum(SettingsProcessEpisodeSource.class) - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getProcessEpisodeSource) - .valueSetter(Settings::setProcessEpisodeSource) - .defaultValue(SettingsProcessEpisodeSource.TVDB)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getProcessEpisodeSource) + .valueSetter(Settings::setProcessEpisodeSource) + .defaultValue(SettingsProcessEpisodeSource.TVDB)), UPDATE_CHECK_PERIOD(createSettingEnum(UpdateCheckPeriod.class) - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getUpdateCheckPeriod) - .valueSetter(Settings::setUpdateCheckPeriod) - .defaultValue(UpdateCheckPeriod.WEEKLY)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getUpdateCheckPeriod) + .valueSetter(Settings::setUpdateCheckPeriod) + .defaultValue(UpdateCheckPeriod.WEEKLY)), USE_NIGHTLY(createSettingEnum(UpdateType.class) - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getUpdateType) - .valueSetter(Settings::setUpdateType) - .defaultValue(UpdateType.STABLE)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getUpdateType) + .valueSetter(Settings::setUpdateType) + .defaultValue(UpdateType.STABLE)), SUBTITLE_LANGUAGE(createSettingEnum(Language.class) - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getSubtitleLanguage) - .valueSetter(Settings::setSubtitleLanguage) - .defaultValue(Language.DUTCH)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getSubtitleLanguage) + .valueSetter(Settings::setSubtitleLanguage) + .defaultValue(Language.DUTCH)), // SCREEN SETTINGS SCREEN_HIDE_EPISODE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) - .valueGetter(ScreenSettings::isHideEpisode) - .valueSetter(ScreenSettings::setHideEpisode) - .defaultValue(true)), + .rootElementFunction(sCtr -> sCtr.settings.screenSettings) + .valueGetter(ScreenSettings::isHideEpisode) + .valueSetter(ScreenSettings::setHideEpisode) + .defaultValue(true)), SCREEN_HIDE_FILENAME(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) - .valueGetter(ScreenSettings::isHideFilename) - .valueSetter(ScreenSettings::setHideFilename) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.screenSettings) + .valueGetter(ScreenSettings::isHideFilename) + .valueSetter(ScreenSettings::setHideFilename) + .defaultValue(false)), SCREEN_HIDE_SEASON(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) - .valueGetter(ScreenSettings::isHideSeason) - .valueSetter(ScreenSettings::setHideSeason) - .defaultValue(true)), + .rootElementFunction(sCtr -> sCtr.settings.screenSettings) + .valueGetter(ScreenSettings::isHideSeason) + .valueSetter(ScreenSettings::setHideSeason) + .defaultValue(true)), SCREEN_HIDE_TITLE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) - .valueGetter(ScreenSettings::isHideTitle) - .valueSetter(ScreenSettings::setHideTitle) - .defaultValue(true)), + .rootElementFunction(sCtr -> sCtr.settings.screenSettings) + .valueGetter(ScreenSettings::isHideTitle) + .valueSetter(ScreenSettings::setHideTitle) + .defaultValue(true)), SCREEN_HIDE_TYPE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) - .valueGetter(ScreenSettings::isHideType) - .valueSetter(ScreenSettings::setHideType) - .defaultValue(true)), + .rootElementFunction(sCtr -> sCtr.settings.screenSettings) + .valueGetter(ScreenSettings::isHideType) + .valueSetter(ScreenSettings::setHideType) + .defaultValue(true)), SCREEN_HIDE_W_I_P(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getScreenSettings()) - .valueGetter(ScreenSettings::isHideWIP) - .valueSetter(ScreenSettings::setHideWIP) - .defaultValue(true)), + .rootElementFunction(sCtr -> sCtr.settings.screenSettings) + .valueGetter(ScreenSettings::isHideWIP) + .valueSetter(ScreenSettings::setHideWIP) + .defaultValue(true)), // PROXY SETTINGS GENERAL_PROXY_ENABLED(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isGeneralProxyEnabled) - .valueSetter(Settings::setGeneralProxyEnabled) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isGeneralProxyEnabled) + .valueSetter(Settings::setGeneralProxyEnabled) + .defaultValue(false)), GENERAL_PROXY_HOST(createSettingString() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getGeneralProxyHost) - .valueSetter(Settings::setGeneralProxyHost) - .defaultValue("")), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getGeneralProxyHost) + .valueSetter(Settings::setGeneralProxyHost) + .defaultValue("")), GENERAL_PROXY_PORT(createSettingInt() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getGeneralProxyPort) - .valueSetter(Settings::setGeneralProxyPort) - .defaultValue(80)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getGeneralProxyPort) + .valueSetter(Settings::setGeneralProxyPort) + .defaultValue(80)), // LIBRARY SERIE EPISODE_LIBRARY_BACKUP_SUBTITLE_PATH(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryBackupSubtitlePath) - .valueSetter(LibrarySettings::setLibraryBackupSubtitlePath) - .defaultValue(null)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getBackupSubtitlePath) + .valueSetter(LibrarySettings::setBackupSubtitlePath) + .defaultValue(null)), EPISODE_LIBRARY_BACKUP_SUBTITLE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryBackupSubtitle) - .valueSetter(LibrarySettings::setLibraryBackupSubtitle) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isBackupSubtitle) + .valueSetter(LibrarySettings::setBackupSubtitle) + .defaultValue(false)), EPISODE_LIBRARY_BACKUP_USE_WEBSITE_FILE_NAME(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryBackupUseWebsiteFileName) - .valueSetter(LibrarySettings::setLibraryBackupUseWebsiteFileName) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isBackupUseWebsiteFileName) + .valueSetter(LibrarySettings::setBackupUseWebsiteFileName) + .defaultValue(false)), EPISODE_LIBRARY_ACTION(createSettingEnum(LibraryActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryAction) - .valueSetter(LibrarySettings::setLibraryAction) - .defaultValue(LibraryActionType.NOTHING)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getAction) + .valueSetter(LibrarySettings::setAction) + .defaultValue(LibraryActionType.NOTHING)), EPISODE_LIBRARY_USE_T_V_D_B_NAMING(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryUseTVDBNaming) - .valueSetter(LibrarySettings::setLibraryUseTVDBNaming) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isUseTVDBNaming) + .valueSetter(LibrarySettings::setUseTVDBNaming) + .defaultValue(false)), EPISODE_LIBRARY_OTHER_FILE_ACTION(createSettingEnum(LibraryOtherFileActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryOtherFileAction) - .valueSetter(LibrarySettings::setLibraryOtherFileAction) - .defaultValue(LibraryOtherFileActionType.NOTHING)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getOtherFileAction) + .valueSetter(LibrarySettings::setOtherFileAction) + .defaultValue(LibraryOtherFileActionType.NOTHING)), EPISODE_LIBRARY_FOLDER(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFolder) - .valueSetter(LibrarySettings::setLibraryFolder) - .defaultValue(null)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getFolder) + .valueSetter(LibrarySettings::setFolder) + .defaultValue(null)), EPISODE_LIBRARY_FOLDER_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFolderStructure) - .valueSetter(LibrarySettings::setLibraryFolderStructure) - .defaultValue("")), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getFolderStructure) + .valueSetter(LibrarySettings::setFolderStructure) + .defaultValue("")), EPISODE_LIBRARY_REMOVE_EMPTY_FOLDERS(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryRemoveEmptyFolders) - .valueSetter(LibrarySettings::setLibraryRemoveEmptyFolders) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isRemoveEmptyFolders) + .valueSetter(LibrarySettings::setRemoveEmptyFolders) + .defaultValue(false)), EPISODE_LIBRARY_FILENAME_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFilenameStructure) - .valueSetter(LibrarySettings::setLibraryFilenameStructure) - .defaultValue("")), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getFilenameStructure) + .valueSetter(LibrarySettings::setFilenameStructure) + .defaultValue("")), EPISODE_LIBRARY_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryFilenameReplaceSpace) - .valueSetter(LibrarySettings::setLibraryFilenameReplaceSpace) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isFilenameReplaceSpace) + .valueSetter(LibrarySettings::setFilenameReplaceSpace) + .defaultValue(false)), EPISODE_LIBRARY_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFilenameReplacingSpaceChar) - .valueSetter(LibrarySettings::setLibraryFilenameReplacingSpaceChar) - .defaultValue('_')), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getFilenameReplacingSpaceChar) + .valueSetter(LibrarySettings::setFilenameReplacingSpaceChar) + .defaultValue('_')), EPISODE_LIBRARY_FOLDER_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryFolderReplaceSpace) - .valueSetter(LibrarySettings::setLibraryFolderReplaceSpace) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isFolderReplaceSpace) + .valueSetter(LibrarySettings::setFolderReplaceSpace) + .defaultValue(false)), EPISODE_LIBRARY_FOLDER_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFolderReplacingSpaceChar) - .valueSetter(LibrarySettings::setLibraryFolderReplacingSpaceChar) - .defaultValue('_')), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::getFolderReplacingSpaceChar) + .valueSetter(LibrarySettings::setFolderReplacingSpaceChar) + .defaultValue('_')), EPISODE_LIBRARY_INCLUDE_LANGUAGE_CODE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryIncludeLanguageCode) - .valueSetter(LibrarySettings::setLibraryIncludeLanguageCode) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .valueGetter(LibrarySettings::isIncludeLanguageCode) + .valueSetter(LibrarySettings::setIncludeLanguageCode) + .defaultValue(false)), EPISODE_LIBRARY_LANG_CODE_MAPPING(createSettingMap() - .toStringMapperKey(Language::name) - .toObjectMapperKey(Language::valueOf) - .toStringMapperValue(Function.identity()) - .toObjectMapperValue(Function.identity()) - .rootElementFunction(sCtr -> sCtr.getSettings().getEpisodeLibrarySettings()) - .mapGetter(LibrarySettings::getLangCodeMap)), + .toStringMapperKey(Language::name) + .toObjectMapperKey(Language::valueOf) + .toStringMapperValue(Function.identity()) + .toObjectMapperValue(Function.identity()) + .rootElementFunction(sCtr -> sCtr.settings.episodeLibrarySettings) + .mapGetter(LibrarySettings::getLangCodeMap)), // LIBRARY MOVIE MOVIE_LIBRARY_BACKUP_SUBTITLE_PATH(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryBackupSubtitlePath) - .valueSetter(LibrarySettings::setLibraryBackupSubtitlePath) - .defaultValue(null)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getBackupSubtitlePath) + .valueSetter(LibrarySettings::setBackupSubtitlePath) + .defaultValue(null)), MOVIE_LIBRARY_BACKUP_SUBTITLE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryBackupSubtitle) - .valueSetter(LibrarySettings::setLibraryBackupSubtitle) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isBackupSubtitle) + .valueSetter(LibrarySettings::setBackupSubtitle) + .defaultValue(false)), MOVIE_LIBRARY_BACKUP_USE_WEBSITE_FILE_NAME(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryBackupUseWebsiteFileName) - .valueSetter(LibrarySettings::setLibraryBackupUseWebsiteFileName) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isBackupUseWebsiteFileName) + .valueSetter(LibrarySettings::setBackupUseWebsiteFileName) + .defaultValue(false)), MOVIE_LIBRARY_ACTION(createSettingEnum(LibraryActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryAction) - .valueSetter(LibrarySettings::setLibraryAction) - .defaultValue(LibraryActionType.NOTHING)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getAction) + .valueSetter(LibrarySettings::setAction) + .defaultValue(LibraryActionType.NOTHING)), MOVIE_LIBRARY_USE_T_V_D_B_NAMING(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryUseTVDBNaming) - .valueSetter(LibrarySettings::setLibraryUseTVDBNaming) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isUseTVDBNaming) + .valueSetter(LibrarySettings::setUseTVDBNaming) + .defaultValue(false)), MOVIE_LIBRARY_OTHER_FILE_ACTION(createSettingEnum(LibraryOtherFileActionType.class) - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryOtherFileAction) - .valueSetter(LibrarySettings::setLibraryOtherFileAction) - .defaultValue(LibraryOtherFileActionType.NOTHING)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getOtherFileAction) + .valueSetter(LibrarySettings::setOtherFileAction) + .defaultValue(LibraryOtherFileActionType.NOTHING)), MOVIE_LIBRARY_FOLDER(createSettingPath() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFolder) - .valueSetter(LibrarySettings::setLibraryFolder) - .defaultValue(null)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getFolder) + .valueSetter(LibrarySettings::setFolder) + .defaultValue(null)), MOVIE_LIBRARY_FOLDER_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFolderStructure) - .valueSetter(LibrarySettings::setLibraryFolderStructure) - .defaultValue("")), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getFolderStructure) + .valueSetter(LibrarySettings::setFolderStructure) + .defaultValue("")), MOVIE_LIBRARY_REMOVE_EMPTY_FOLDERS(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryRemoveEmptyFolders) - .valueSetter(LibrarySettings::setLibraryRemoveEmptyFolders) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isRemoveEmptyFolders) + .valueSetter(LibrarySettings::setRemoveEmptyFolders) + .defaultValue(false)), MOVIE_LIBRARY_FILENAME_STRUCTURE(createSettingString() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFilenameStructure) - .valueSetter(LibrarySettings::setLibraryFilenameStructure) - .defaultValue("")), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getFilenameStructure) + .valueSetter(LibrarySettings::setFilenameStructure) + .defaultValue("")), MOVIE_LIBRARY_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryFilenameReplaceSpace) - .valueSetter(LibrarySettings::setLibraryFilenameReplaceSpace) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isFilenameReplaceSpace) + .valueSetter(LibrarySettings::setFilenameReplaceSpace) + .defaultValue(false)), MOVIE_LIBRARY_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFilenameReplacingSpaceChar) - .valueSetter(LibrarySettings::setLibraryFilenameReplacingSpaceChar) - .defaultValue('_')), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getFilenameReplacingSpaceChar) + .valueSetter(LibrarySettings::setFilenameReplacingSpaceChar) + .defaultValue('_')), MOVIE_LIBRARY_FOLDER_REPLACE_SPACE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryFolderReplaceSpace) - .valueSetter(LibrarySettings::setLibraryFolderReplaceSpace) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isFolderReplaceSpace) + .valueSetter(LibrarySettings::setFolderReplaceSpace) + .defaultValue(false)), MOVIE_LIBRARY_FOLDER_REPLACING_SIGN(createSettingCharacter() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::getLibraryFolderReplacingSpaceChar) - .valueSetter(LibrarySettings::setLibraryFolderReplacingSpaceChar) - .defaultValue('_')), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::getFolderReplacingSpaceChar) + .valueSetter(LibrarySettings::setFolderReplacingSpaceChar) + .defaultValue('_')), MOVIE_LIBRARY_INCLUDE_LANGUAGE_CODE(createSettingBoolean() - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .valueGetter(LibrarySettings::isLibraryIncludeLanguageCode) - .valueSetter(LibrarySettings::setLibraryIncludeLanguageCode) - .defaultValue(false)), + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .valueGetter(LibrarySettings::isIncludeLanguageCode) + .valueSetter(LibrarySettings::setIncludeLanguageCode) + .defaultValue(false)), MOVIE_LIBRARY_LANG_CODE_MAPPING(createSettingMap() - .toStringMapperKey(Language::name) - .toObjectMapperKey(Language::valueOf) - .toStringMapperValue(Function.identity()) - .toObjectMapperValue(Function.identity()) - .rootElementFunction(sCtr -> sCtr.getSettings().getMovieLibrarySettings()) - .mapGetter(LibrarySettings::getLangCodeMap)), + .toStringMapperKey(Language::name) + .toObjectMapperKey(Language::valueOf) + .toStringMapperValue(Function.identity()) + .toObjectMapperValue(Function.identity()) + .rootElementFunction(sCtr -> sCtr.settings.movieLibrarySettings) + .mapGetter(LibrarySettings::getLangCodeMap)), // SERIE SOURCE SETTINGS LOGIN_ADDIC7ED_ENABLED(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isLoginAddic7edEnabled) - .valueSetter(Settings::setLoginAddic7edEnabled) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isLoginAddic7edEnabled) + .valueSetter(Settings::setLoginAddic7edEnabled) + .defaultValue(false)), LOGIN_ADDIC7ED_USERNAME(createSettingString() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getLoginAddic7edUsername) - .valueSetter(Settings::setLoginAddic7edUsername) - .defaultValue("")), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getLoginAddic7edUsername) + .valueSetter(Settings::setLoginAddic7edUsername) + .defaultValue("")), LOGIN_ADDIC7ED_PASSWORD(createSettingString() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getLoginAddic7edPassword) - .valueSetter(Settings::setLoginAddic7edPassword) - .defaultValue("")), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getLoginAddic7edPassword) + .valueSetter(Settings::setLoginAddic7edPassword) + .defaultValue("")), LOGIN_OPEN_SUBTITLES_ENABLED(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isLoginOpenSubtitlesEnabled) - .valueSetter(Settings::setLoginOpenSubtitlesEnabled) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isLoginOpenSubtitlesEnabled) + .valueSetter(Settings::setLoginOpenSubtitlesEnabled) + .defaultValue(false)), LOGIN_OPEN_SUBTITLES_USERNAME(createSettingString() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getLoginOpenSubtitlesUsername) - .valueSetter(Settings::setLoginOpenSubtitlesUsername) - .defaultValue("")), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getLoginOpenSubtitlesUsername) + .valueSetter(Settings::setLoginOpenSubtitlesUsername) + .defaultValue("")), LOGIN_OPEN_SUBTITLES_PASSWORD(createSettingString() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::getLoginOpenSubtitlesPassword) - .valueSetter(Settings::setLoginOpenSubtitlesPassword) - .defaultValue("")), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::getLoginOpenSubtitlesPassword) + .valueSetter(Settings::setLoginOpenSubtitlesPassword) + .defaultValue("")), SERIE_SOURCE_ADDIC7ED(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourceAddic7ed) - .valueSetter(Settings::setSerieSourceAddic7ed) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourceAddic7ed) + .valueSetter(Settings::setSerieSourceAddic7ed) + .defaultValue(true)), SERIE_SOURCE_ADDIC7ED_PROXY(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourceAddic7edProxy) - .valueSetter(Settings::setSerieSourceAddic7edProxy) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourceAddic7edProxy) + .valueSetter(Settings::setSerieSourceAddic7edProxy) + .defaultValue(true)), SERIE_SOURCE_LOCAL(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourceLocal) - .valueSetter(Settings::setSerieSourceLocal) - .defaultValue(false)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourceLocal) + .valueSetter(Settings::setSerieSourceLocal) + .defaultValue(false)), SERIE_SOURCE_OPENSUBTITLES(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourceOpensubtitles) - .valueSetter(Settings::setSerieSourceOpensubtitles) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourceOpensubtitles) + .valueSetter(Settings::setSerieSourceOpensubtitles) + .defaultValue(true)), SERIE_SOURCE_PODNAPISI(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourcePodnapisi) - .valueSetter(Settings::setSerieSourcePodnapisi) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourcePodnapisi) + .valueSetter(Settings::setSerieSourcePodnapisi) + .defaultValue(true)), SERIE_SOURCE_TV_SUBTITLES(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourceTvSubtitles) - .valueSetter(Settings::setSerieSourceTvSubtitles) - .defaultValue(true)), + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourceTvSubtitles) + .valueSetter(Settings::setSerieSourceTvSubtitles) + .defaultValue(true)), SERIE_SOURCE_SUBSCENE(createSettingBoolean() - .rootElementFunction(SettingsControl::getSettings) - .valueGetter(Settings::isSerieSourceSubscene) - .valueSetter(Settings::setSerieSourceSubscene) - .defaultValue(true)); + .rootElementFunction(SettingsControl::getSettings) + .valueGetter(Settings::isSerieSourceSubscene) + .valueSetter(Settings::setSerieSourceSubscene) + .defaultValue(true)); private final BiConsumer storeValueFunction; private final BiConsumer loadValueFunction; @@ -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)); } @@ -460,38 +459,38 @@ private interface SettingIntf { private static SettingTypedRootElementFunctionIntf createSettingString() { return createSetting(String.class) - .toStringMapper(Function.identity()) - .toObjectMapper(Function.identity()); + .toStringMapper(Function.identity()) + .toObjectMapper(Function.identity()); } private static SettingTypedRootElementFunctionIntf createSettingCharacter() { return createSetting(Character.class) - .toStringMapper(String::valueOf) - .toObjectMapper(s -> s.charAt(0)); + .toStringMapper(String::valueOf) + .toObjectMapper(s -> s.charAt(0)); } private static SettingTypedRootElementFunctionIntf createSettingInt() { return createSetting(Integer.class) - .preferencesSetter(Preferences::putInt) - .preferencesGetter(Preferences::getInt); + .preferencesSetter(Preferences::putInt) + .preferencesGetter(Preferences::getInt); } private static SettingTypedRootElementFunctionIntf createSettingBoolean() { return createSetting(Boolean.class) - .preferencesSetter(Preferences::putBoolean) - .preferencesGetter(Preferences::getBoolean); + .preferencesSetter(Preferences::putBoolean) + .preferencesGetter(Preferences::getBoolean); } private static SettingTypedRootElementFunctionIntf createSettingPath() { return createSetting(Path.class) - .toStringMapper(FileUtils::toAbsolutePathAsString) - .toObjectMapper(Path::of); + .toStringMapper(PathExt::toAbsolutePathAsString) + .toObjectMapper(Path::of); } private static > SettingTypedRootElementFunctionIntf createSettingEnum(Class type) { return createSetting(type) - .toStringMapper(Enum::name) - .toObjectMapper(s -> Enum.valueOf(type, s)); + .toStringMapper(Enum::name) + .toObjectMapper(s -> Enum.valueOf(type, s)); } private static SettingTypedToStringMapperIntf createSetting(Class type) { @@ -509,7 +508,8 @@ private interface SettingTypedToObjectMapperIntf { } private interface SettingTypedPreferenceGetterIntf { - SettingTypedRootElementFunctionIntf preferencesGetter(TriFunction preferencesGetter); + SettingTypedRootElementFunctionIntf preferencesGetter( + TriFunction preferencesGetter); } private interface SettingTypedRootElementFunctionIntf { @@ -542,15 +542,15 @@ private enum SettingType { @Setter @Accessors(chain = true, fluent = true) private static class SettingTyped extends SettingCommon> - implements - SettingTypedToStringMapperIntf, - SettingTypedToObjectMapperIntf, - SettingTypedPreferenceGetterIntf, - SettingTypedRootElementFunctionIntf, - SettingTypedValueGetterIntf, - SettingTypedValueSetterIntf, - SettingTypedDefaultValueIntf, - SettingBuildIntf { + implements + SettingTypedToStringMapperIntf, + SettingTypedToObjectMapperIntf, + SettingTypedPreferenceGetterIntf, + SettingTypedRootElementFunctionIntf, + SettingTypedValueGetterIntf, + SettingTypedValueSetterIntf, + SettingTypedDefaultValueIntf, + SettingBuildIntf { private SettingType settingType = SettingType.SINGLE_VALUE; @@ -597,10 +597,10 @@ public SettingTyped toStringMapper(Function toStringMapper) { public SettingTyped toObjectMapper(Function toObjectMapper) { this.toObjectMapper = toObjectMapper; this.preferencesGetter = - (preferences, key, defaultValue) -> { - String value = preferences.get(key, null); - return value != null ? toObjectMapper.apply(value) : getDefaultValue(); - }; + (preferences, key, defaultValue) -> { + String value = preferences.get(key, null); + return value != null ? toObjectMapper.apply(value) : getDefaultValue(); + }; return this; } @@ -619,19 +619,21 @@ 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 && text.isEmpty())) { preferencesSetter.accept(preferences, key, value); } }); - super.loadValueFunction((settingsControl, preferences) -> valueSetter.accept(getRootElement(settingsControl), + super.loadValueFunction( + (settingsControl, preferences) -> valueSetter.accept(getRootElement(settingsControl), preferencesGetter.apply(preferences, key, getDefaultValue()))); } case COLLECTION -> { super.storeValueFunction((settingsControl, preferences) -> { AtomicInteger i = new AtomicInteger(-1); valueConsumer.accept(getRootElement(settingsControl), - value -> preferences.put(key + i.incrementAndGet(), toStringMapper.apply(value))); + value -> preferences.put(key + i.incrementAndGet(), toStringMapper.apply(value))); if (i.get() > -1) { preferences.putInt(key + "Size", i.get() + 1); } @@ -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); @@ -683,14 +686,14 @@ private interface SettingMapTypedMapGetterIntf { @Setter @Accessors(chain = true, fluent = true) private static class SettingMapTyped - implements SettingIntf, - SettingMapTypedToStringMapperKeyIntf, - SettingMapTypedToObjectMapperKeyIntf, - SettingMapTypedToStringMapperValueIntf, - SettingMapTypedToObjectMapperValueIntf, - SettingMapTypedRootElementFunctionIntf, - SettingMapTypedMapGetterIntf, - SettingBuildIntf { + implements SettingIntf, + SettingMapTypedToStringMapperKeyIntf, + SettingMapTypedToObjectMapperKeyIntf, + SettingMapTypedToStringMapperValueIntf, + SettingMapTypedToObjectMapperValueIntf, + SettingMapTypedRootElementFunctionIntf, + SettingMapTypedMapGetterIntf, + SettingBuildIntf { private Function toStringMapperKey; private Function toObjectMapperKey; @@ -745,22 +748,22 @@ public SettingIntf build(String key) { this.storeValueFunction = (settingsControl, preferences) -> { AtomicInteger i = new AtomicInteger(-1); valueConsumer.accept(getRootElement(settingsControl), - (k, v) -> { - int idx = i.incrementAndGet(); - preferences.put(getKeyString(key, idx), toStringMapperKey.apply(k)); - preferences.put(getValueString(key, idx), toStringMapperValue.apply(v)); - }); + (k, v) -> { + int idx = i.incrementAndGet(); + preferences.put(getKeyString(key, idx), toStringMapperKey.apply(k)); + preferences.put(getValueString(key, idx), toStringMapperValue.apply(v)); + }); if (i.get() > -1) { preferences.putInt(key + "Size", i.get() + 1); } }; this.loadValueFunction = (settingsControl, preferences) -> { int numberOfItems = preferences.getInt(key + "Size", 0); - IntStream.range(0, numberOfItems).forEach(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), ""))); - }); + 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..37553335 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,17 @@ 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"; + public static final String DATABASE_VERSION_KEY = "DATABSE_VERSION"; + 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 +77,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 +98,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 +108,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 +122,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 +154,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 +166,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 +181,27 @@ public void migrateSettingsV0ToV1() { // } EPISODE_LIBRARY_FOLDER_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFolderStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFolderStructure())); + settings.episodeLibrarySettings.folderStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.folderStructure); EPISODE_LIBRARY_FOLDER_STRUCTURE.store(this, preferences); EPISODE_LIBRARY_FILENAME_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFilenameStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFilenameStructure())); + settings.episodeLibrarySettings.filenameStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.filenameStructure); EPISODE_LIBRARY_FILENAME_STRUCTURE.store(this, preferences); MOVIE_LIBRARY_FOLDER_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFolderStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFolderStructure())); + settings.episodeLibrarySettings.folderStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.folderStructure); MOVIE_LIBRARY_FOLDER_STRUCTURE.store(this, preferences); MOVIE_LIBRARY_FILENAME_STRUCTURE.load(this, preferences); - settings.getEpisodeLibrarySettings() - .setLibraryFilenameStructure(migrateLibraryStructureV0(settings.getEpisodeLibrarySettings().getLibraryFilenameStructure())); + settings.episodeLibrarySettings.filenameStructure = + migrateLibraryStructureV0(settings.episodeLibrarySettings.filenameStructure); 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 +210,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.otherFileAction = + 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.otherFileAction = + 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.action = + 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.action = + 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 +254,7 @@ public void migrateSettingsV2ToV3() { // }); // preferences.remove("DictionarySize"); - settings.setSettingsVersion(3); + settings.settingsVersion = 3; SETTINGS_VERSION.store(this, preferences); } @@ -269,44 +271,46 @@ 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()) - .forEach(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix) - .forEach(serieMappingPair -> { - manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(serieMappingPair.getKey()) - .remove(); - })); - settings.setSettingsVersion(5); + MappingType.ADDIC7ED_PROXY.selectionForKeyPrefixList + .forEach(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix) + .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()) - .forEach(i -> preferences.remove("DefaultSelectionQuality" + i)); + .forEach(i -> preferences.remove("DefaultSelectionQuality" + i)); } } DEFAULT_SELECTION_QUALITY.store(this, preferences); - settings.setSettingsVersion(6); + settings.settingsVersion = 6; SETTINGS_VERSION.store(this, preferences); } @@ -315,47 +319,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.filenameStructure)) { + settings.movieLibrarySettings.filenameStructure = "%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.folderStructure)) { + settings.movieLibrarySettings.folderStructure = "%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.filenameStructure)) { + settings.movieLibrarySettings.filenameStructure = "%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 +383,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(DATABASE_VERSION_KEY).valueSupplier(() -> 0).get(); if (version == 0) { migrateDatabaseV0ToV1(); } @@ -388,12 +396,15 @@ private void migrateDatabase() { private void migrateDatabaseV0ToV1() { manager.valueBuilder().cacheType(CacheType.DISK).keyFilter(k -> k.startsWith("TVDB-SerieMapping-")).remove(); manager.valueBuilder().cacheType(CacheType.DISK).keyFilter(k -> k.startsWith("TVDB-SerieId-")).remove(); - manager.valueBuilder().cacheType(CacheType.DISK).key("DATABSE_VERSION").value(1).store(); + manager.valueBuilder().cacheType(CacheType.DISK).key(DATABASE_VERSION_KEY).value(1).store(); } 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,17 +413,16 @@ 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); }).toList(); editedEntries.forEach(entry -> { manager.valueBuilder().cacheType(CacheType.DISK).key(entry.getKey()).remove(); manager.valueBuilder().cacheType(CacheType.DISK).key(entry.getKey()).value(entry.getValue()).store(); }); - manager.valueBuilder().cacheType(CacheType.DISK).key("DATABSE_VERSION").value(2).store(); + manager.valueBuilder().cacheType(CacheType.DISK).key(DATABASE_VERSION_KEY).value(2).store(); } } 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..95b0335f 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,49 +1,42 @@ 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 filenameStructure = ""; + @var String folderStructure = ""; + @var Path folder; + @var boolean filenameReplaceSpace; + @var boolean folderReplaceSpace; + @var boolean includeLanguageCode; + @var boolean removeEmptyFolders; + @var boolean useTVDBNaming; + @var LibraryActionType action = LibraryActionType.NOTHING; + @var LibraryOtherFileActionType otherFileAction = LibraryOtherFileActionType.NOTHING; + @var Character filenameReplacingSpaceChar; + @var Character folderReplacingSpaceChar; + @var boolean backupSubtitle; + @var boolean backupUseWebsiteFileName; + @var Path backupSubtitlePath; + @var Map langCodeMap = new LinkedHashMap<>(); public boolean hasLibraryAction(LibraryActionType libraryAction) { - return this.libraryAction == libraryAction; + return this.action == libraryAction; } public boolean hasAnyLibraryAction(LibraryActionType... libraryActions) { - return Arrays.stream(libraryActions).anyMatch(this::hasLibraryAction); + return libraryActions.stream().anyMatch(this::hasLibraryAction); } public boolean hasLibraryOtherFileAction(LibraryOtherFileActionType libraryOtherFileAction) { - return this.libraryOtherFileAction == libraryOtherFileAction; + return this.otherFileAction == 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..474772fc 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,7 @@ package org.lodder.subtools.multisubdownloader.settings.model; +import java.awt.*; +import java.io.Serial; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.InvalidPathException; @@ -8,19 +10,15 @@ import java.util.function.Predicate; import java.util.regex.Pattern; -import org.lodder.subtools.sublibrary.util.NamedPattern; - -import java.awt.Image; - -import lombok.Getter; +import manifold.ext.props.rt.api.val; +import manifold.ext.rt.api.Self; public class PathOrRegex implements Serializable { - private static final long serialVersionUID = 1L; - @Getter - private final String value; - @Getter - private final transient Image image; + @Serial private static final long serialVersionUID = 1L; + + @val String value; + @val transient Image image; private final transient Predicate isExcludedPathPredicate; public PathOrRegex(Path path) { @@ -40,9 +38,9 @@ public PathOrRegex(String value) { regex = true; } if (regex) { - this.image = PathMatchType.REGEX.getImage(); - NamedPattern np = NamedPattern.compile(value.replace("*", ".*") + ".*$", Pattern.CASE_INSENSITIVE); - this.isExcludedPathPredicate = p -> np.matcher(p.getFileName().toString()).find(); + this.image = PathMatchType.REGEX.image; + Pattern pattern = Pattern.compile(value.replace("*", ".*") + ".*$", Pattern.CASE_INSENSITIVE); + this.isExcludedPathPredicate = p -> pattern.matcher(p.getFileName().toString()).find(); } else { this.image = getImage(path); this.isExcludedPathPredicate = path::equals; @@ -50,7 +48,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) { @@ -68,7 +66,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { - return obj instanceof PathOrRegex other && Objects.equals(value, other.getValue()); + public boolean equals(@Self Object obj) { + 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..c106884b 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,89 @@ 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))); - VideoPatterns.AudioEncoding.values().stream() - .forEach(encoding -> encoding.getValues().stream().forEach(keyword -> sortWeightsTemp.put(keyword, 2))); + VideoPatterns.Source.values().forEach(source -> sortWeightsTemp.put(source.regex, 2)); + VideoPatterns.AudioEncoding.values().forEach(encoding -> sortWeightsTemp.put(encoding.regex, 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..4fe62ee8 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,14 @@ import java.util.function.Predicate; import java.util.regex.Pattern; -import org.lodder.subtools.sublibrary.util.NamedPattern; - -import lombok.Getter; +import manifold.ext.props.rt.api.get; @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,8 +19,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); - yield file -> np.matcher(file.getFileName().toString()).find(); + Pattern pattern = Pattern.compile(description.replace("*", ".*") + ".*$", Pattern.CASE_INSENSITIVE); + yield file -> pattern.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..bb628a45 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,21 @@ 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.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor(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%", "StructureBuilderDialog.MovieName"), + QUALITY("%QUALITY%", "StructureBuilderDialog.QualityOfMovie"), + YEAR("%YEAR%", "StructureBuilderDialog.MovieYear"), + RELEASE_GROUP("%RELEASE GROUP%", "StructureBuilderDialog.ReleaseGroup"); - private final String label; - private final String description; + @val @override String label; + @val @override String description; + MovieStructureTag(String label, String descriptionMessage) { + this.label = label; + this.description = Messages.getText(descriptionMessage); + } } 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..716a0224 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,29 +1,28 @@ 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"), SEASON_SHORT("%S%", "StructureBuilderDialog.NumberOfSeasonsWithoutLeading"), QUALITY("%QUALITY%", "StructureBuilderDialog.QualityOfRelease"), - DESCRIPTION("%DESCRIPTION%", "StructureBuilderDialog.Description"); + RELEASE_GROUP("%RELEASE GROUP%", "StructureBuilderDialog.ReleaseGroup"); - 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..d0ee5f1d 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,15 +49,10 @@ 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() - .flatMap(local -> getAllSubtitlesFiles(local, filter).stream()) - .toList(); + return settings.localSourcesFolders.stream() + .flatMap(local -> getAllSubtitlesFiles(local, filter).stream()) + .toList(); } @Override @@ -67,36 +60,33 @@ public Set searchSubtitles(TvRelease tvRelease, Language language) { Set listFoundSubtitles = new HashSet<>(); ReleaseParser vfp = new ReleaseParser(); - String filter; - if (tvRelease.getOriginalName().length() > 0) { - filter = tvRelease.getOriginalName().replaceAll("[^A-Za-z]", "").trim(); - } else { - filter = tvRelease.getName().replaceAll("[^A-Za-z]", "").trim(); - } + String name = !tvRelease.originalName.isEmpty() ? tvRelease.originalName : tvRelease.name; + String filter = 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()) - .fileName(fileSub.getFileNameAsString()) - .language(language) - .quality(ReleaseParser.getQualityKeyword(fileSub.getFileNameAsString())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(fileSub.getFileNameAsString(), true)) - .uploader(fileSub.toAbsolutePath().toString()) - .hearingImpaired(false)); + Subtitle.downloadSource(fileSub) + .subtitleSource(subtitleSource) + .fileName(fileSub.fileNameAsString) + .language(language) + .quality(ReleaseParser.getQualityKeyword(fileSub.fileNameAsString)) + .subtitleMatchType(SubtitleMatchType.EVERYTHING) + .releaseGroup(ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true)) + .uploader(fileSub.toAbsolutePath().toString()) + .hearingImpaired(false)); } } } @@ -117,30 +107,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.fileNameAsString) + .language(language) // TODO previously: language(""). This was not correct? + .quality(ReleaseParser.getQualityKeyword(fileSub.fileNameAsString)) + .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()) { @@ -157,9 +148,12 @@ public Set searchSubtitles(MovieRelease movieRelease, Language languag 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())) - .toList(); + .filter(file -> file.hasExtension("srt")) + .filter(file -> file.getFileNameAsString() + .replaceAll("[^A-Za-z]", "") + .toLowerCase() + .contains(filter.toLowerCase())) + .toList(); } catch (IOException e) { LOGGER.error(e.getMessage(), e); return List.of(); @@ -168,7 +162,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..529c342c 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,28 +41,23 @@ 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() - .cacheType(CacheType.DISK) - .keyFilter((String k) -> k.startsWith(getProviderName() + "-")) - .clear(); + manager.clearExpiredCacheBuilder() + .cacheType(CacheType.DISK) + .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..83bc2901 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,74 @@ 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 sealed class AbstractAdapter + implements Adapter, SubtitleProvider + permits JAddic7edAdapter, JAddic7edViaProxyAdapter, JOpenSubAdapter, JPodnapisiAdapter, JSubsceneAdapter, + JTVsubtitlesAdapter { + + @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 +97,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..d5623703 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,6 +1,6 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.adapters; -import static org.lodder.subtools.sublibrary.util.OptionalExtension.*; +import static org.lodder.subtools.multisubdownloader.Messages.*; import java.io.IOException; import java.io.Serializable; @@ -16,8 +16,8 @@ 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; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesHasher; @@ -30,14 +30,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 +43,7 @@ public interface Adapter exte Logger LOGGER = LoggerFactory.getLogger(Adapter.class); default UserInteractionSettingsIntf getUserInteractionSettings() { - return getUserInteractionHandler().getSettings(); + return getUserInteractionHandler().settings; } UserInteractionHandler getUserInteractionHandler(); @@ -55,16 +51,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 +68,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 +96,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 +113,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,27 +121,29 @@ 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()); + Function customNameFunction) throws X { + return getProviderSerieId(nameFunction.apply(tvRelease), customNameFunction.apply(tvRelease), + tvRelease.displayName, tvRelease.season, tvRelease.getTvdbIdOptional()); } default Optional getProviderSerieId(String serieName, String displayName, int season, - OptionalInt tvdbIdOptional) throws X { + OptionalInt tvdbIdOptional) throws X { 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, - useSeasonForSerieId() ? season : -1))).orElseThrow(); + 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 return tvdbIdValueBuilder.get().returnType(SerieMapping.class).getOptional(); @@ -152,25 +152,21 @@ default Optional getProviderSerieId(String serieName, String serie return Optional.empty(); } int seasonToUse = useSeasonForSerieId() ? season : 0; - ValueBuilderIsPresentIntf serieNameValueBuilder = getManager().valueBuilder() - .cacheType(CacheType.DISK) - .key("%s-serieName-name:%s-%s".formatted(getProviderName(), serieName.toLowerCase(), seasonToUse)); + ValueBuilderIsPresentIntf serieNameValueBuilder = manager.valueBuilder() + .cacheType(CacheType.DISK) + .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,56 +175,55 @@ 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)) - .storeTempNullValue() - .timeToLive(OptionalExtension - .map(serieNameValueBuilder.getTemporaryTimeToLive(), v -> v * 2) - .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) - .storeAsTempValue(); + serieNameValueBuilder.value(new SerieMapping(serieName, null, null, seasonToUse)) + .storeTempNullValue() + .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() - .cacheType(CacheType.MEMORY) - .key("%s-serieName-prev-results:%s-%s".formatted(getProviderName(), displayName.toLowerCase(), seasonToUse)); + ValueBuilderIsPresentIntf previousResultsValueBuilder = manager.valueBuilder() + .cacheType(CacheType.MEMORY) + .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() ? + getText("SelectDialog.SelectSerieNameForNameWithSeason", displayName, seasonToUse) : + getText("SelectDialog.SelectSerieNameForName", 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)) - .storeTempNullValue() - .timeToLive(OptionalExtension - .map(serieNameValueBuilder.getTemporaryTimeToLive(), v -> v * 2) - .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) - .storeAsTempValue(); + serieNameValueBuilder.value(new SerieMapping(serieNameToSearchFor, null, null, seasonToUse)) + .storeTempNullValue() + .timeToLive(serieNameValueBuilder.getTemporaryTimeToLive() + .map(v -> v * 2) + .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) + .storeAsTempValue(); previousResultsValueBuilder.collectionValue(providerSerieIds).store(); } 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..e97fff5d 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,31 +26,30 @@ 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 { +public final 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) { + UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); 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 +59,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,51 +79,53 @@ 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())) - .language(sub.getLanguage()) - .quality(ReleaseParser.getQualityKeyword(sub.getTitle() + " " + sub.getVersion())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(sub.getTitle() + " " + sub.getVersion(), - (sub.getTitle() + " " + sub.getVersion()).endsWith(".srt"))) - .uploader(sub.getUploader()) - .hearingImpaired(false)) - .collect(Collectors.toSet()); + .filter(sub -> language == sub.getLanguage()) + .map(sub -> Subtitle.downloadSource(sub.getUrl()) + .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.getTitle().endsWith(".srt"))) + .uploader(sub.getUploader()) + .hearingImpaired(false)) + .collect(Collectors.toSet()); } @Override - 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.getName().replaceAll("[^A-Za-z]", "")))) - .toList(); + 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(); } @Override @@ -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..ae4b7a32 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 { +public final 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,52 +72,53 @@ 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)) + 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(getProviderName(), tvdbId)); + 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)) - .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) - .handleHttpCode(ReturnCode.NOT_FOUND, () -> { - LOGGER.info("API %s - Could not find serie name [%s]".formatted(getProviderName(), serieName)); - return List.of(); - }) - .execute(); + serieIds = new ExecuteCall<>(() -> getApi().getProviderSerieName(serieName)).message( + "getProviderSerieName: [$serieName]") + .retryWhenHttpCode(ReturnCode.RATE_LIMIT_REACHED) + .handleHttpCode(ReturnCode.NOT_FOUND, () -> { + 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]", "")))) - .toList(); + .sorted(Comparator.comparing(n -> !serieName.replaceAll("[^A-Za-z]", "") + .equalsIgnoreCase(n.name.replaceAll("[^A-Za-z]", "")))) + .toList(); } @Override @@ -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..93a55559 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,26 +26,24 @@ 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 { +public final 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) { + UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); if (osApi == null) { osApi = new LazySupplier<>(() -> { @@ -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,110 +65,93 @@ private OpenSubtitlesApi getApi() { } @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.OPENSUBTITLES; - } - - @Override - public String getProviderName() { - return getSubtitleSource().name(); + public List searchMovieSubtitlesWithHash(String hash, Language language) + throws OpenSubtitlesException { + return getApi().searchSubtitles().movieHash(hash).language(language).searchSubtitles().getData(); } @Override - 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(); + throws OpenSubtitlesException { + 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()) - .flatMap(attributes -> attributes.getFiles().stream().map(file -> createSubtitle(file, attributes))) - .collect(Collectors.toSet()); + 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))) - .collect(Collectors.toSet()); + 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()) - .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()); + 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.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]", ""))) - .thenComparing(OpensubtitleSerieId::getYear, Comparator.reverseOrder())) - .toList(); + throws OpenSubtitlesException { + 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(); } @Override @@ -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..991fc98e 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 final 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()) - .language(language) - .quality(ReleaseParser.getQualityKeyword(ossd.getReleaseString())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(ossd.getReleaseString(), - StringUtils.endsWith(ossd.getReleaseString(), ".srt"))) - .uploader(ossd.getUploaderName()) - .hearingImpaired(ossd.isHearingImpaired())) - .collect(Collectors.toSet()); + .filter(ossd -> StringUtils.isNotBlank(ossd.releaseString)) + .map(ossd -> Subtitle.downloadSource(ossd.url) + .subtitleSource(subtitleSource) + .fileName(ossd.releaseString) + .language(language) + .quality(ReleaseParser.getQualityKeyword(ossd.releaseString)) + .subtitleMatchType(SubtitleMatchType.EVERYTHING) + .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..40a08e78 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,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 JSubsceneAdapter extends AbstractAdapter { +public final 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 +50,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 +60,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 +79,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,40 +111,43 @@ public List getSortedProviderSerieIds(OptionalInt tvdbIdOptiona default -> 4; }; Pattern yearPattern = Pattern.compile("(\\d\\d\\d\\d)"); - 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) - .thenComparing(serieId -> { - Matcher matcher = yearPattern.matcher(serieId.getName()); - return matcher.find() ? Integer.parseInt(matcher.group()) : 0; - }, Comparator.reverseOrder()) - .thenComparing(SubSceneSerieId::getSeason, Comparator.reverseOrder())) - .distinct() - .toList(); + 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.season == 0) + .thenComparing(serieId -> { + Matcher matcher = yearPattern.matcher(serieId.name); + return matcher.find() ? Integer.parseInt(matcher.group()) : 0; + }, Comparator.reverseOrder()) + .thenComparing(SubSceneSerieId::getSeason, Comparator.reverseOrder())) + .distinct() + .toList(); } @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)))) - .map(sub -> Subtitle.downloadSource(sub.getUrlSupplier()) - .subtitleSource(getSubtitleSource()) - .fileName(StringUtil.removeIllegalFilenameChars(sub.getName())) - .language(sub.getLanguage()) - .quality(ReleaseParser.getQualityKeyword(sub.getName())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(sub.getName(), false)) - .uploader(sub.getUploader()) - .hearingImpaired(sub.isHearingImpaired())) - .collect(Collectors.toSet()); + .filter(sub -> language == sub.getLanguage()) + .filter(sub -> sub.getName() + .contains(getSeasonEpisodeString(tvRelease.season, tvRelease.firstEpisodeNumber))) + .map(sub -> Subtitle.downloadSource(sub.getUrlSupplier()) + .subtitleSource(subtitleSource) + .fileName(sub.getName().removeIllegalFilenameChars()) + .language(sub.getLanguage()) + .quality(ReleaseParser.getQualityKeyword(sub.getName())) + .subtitleMatchType(SubtitleMatchType.EVERYTHING) + .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 +157,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..8c5c2df2 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 final 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,58 +76,61 @@ 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()) - .language(language) - .quality(ReleaseParser.getQualityKeyword(sub.getFilename() + " " + sub.getRip())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleasegroup(sub.getFilename(), StringUtils.endsWith(sub.getFilename(), ".srt"))) - .uploader(sub.getAuthor()) - .hearingImpaired(false)) - .collect(Collectors.toSet()); + .map(sub -> Subtitle.downloadSource(sub.url) + .subtitleSource(subtitleSource) + .fileName(sub.filename) + .language(language) + .quality(ReleaseParser.getQualityKeyword(sub.filename + " " + sub.rip)) + .subtitleMatchType(SubtitleMatchType.EVERYTHING) + .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]", ""))) - .thenComparing((ProviderSerieId providerSerieId) -> { - Matcher matcher = yearPatter.matcher(providerSerieId.getName()); - if (matcher.find()) { - return Integer.parseInt(matcher.group(2)); - } - return 0; - }, Comparator.reverseOrder())) - .toList(); + 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.name); + if (matcher.find()) { + return Integer.parseInt(matcher.group(2)); + } + return 0; + }, Comparator.reverseOrder())) + .toList(); } @Override @@ -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..bb79f2bf 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,12 +53,12 @@ public JAddic7edApi(String username, String password, boolean speedy, Manager ma public void login(String username, String password) throws Addic7edException { try { - getManager().postBuilder() - .url(DOMAIN + "/dologin.php") - .addData("username", username) - .addData("password", password) - .addData("remember", "false") - .post(); + manager.postBuilder() + .url(DOMAIN + "/dologin.php") + .addData("username", username) + .addData("password", password) + .addData("remember", "false") + .post(); } catch (ManagerException e) { throw new Addic7edException(e); } @@ -71,16 +69,17 @@ 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); + StringUtils.containsIgnoreCase(formattedSerieName, serieNameFormatted); }).toList(); return !providerSerieIdsFormatted.isEmpty() ? providerSerieIdsFormatted : providerSerieIds; } catch (Exception e) { @@ -99,120 +98,106 @@ 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() - .memoryCache() - .key("%s-subtitles-%s-%s-%s-%s".formatted(getSubtitleSource().name(), addic7edSerieMapping.getProviderId(), 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(); + 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(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.providerName.replace(" ", "_"), UTF_8), season, + episode, languageIds.size() == 1 ? languageIds.first.id : LanguageId.ALL.id); + + Document doc = getContent(url); + String title = null; + + 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 title = null; - Elements elTitel = doc.get().getElementsByClass("titulo"); - if (elTitel.size() == 1) { - Matcher matcher = TITLE_PATTERN.matcher(elTitel.get(0).html()); - if (matcher.matches()) { - title = matcher.group(1); + Elements blocks = doc.select(".tabel95[width='100%']"); + + List lSubtitles = new ArrayList<>(); + for (Element block : blocks) { + 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.first.text().trim()); + if (!m.matches()) { + break; + } else { + version = m.group(1).trim(); + uploader = block.selectFirst("a[href*=user/]").text(); + hearingImpaired = !block.select("img[title~=Hearing]").isEmpty(); } } - String uploader, version, lang, download; - boolean hearingImpaired; - Elements blocks = doc.get().select(".tabel95[width='100%']"); - - List lSubtitles = new ArrayList<>(); - for (Element block : blocks) { - uploader = ""; - version = null; - lang = null; - download = null; - 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()); - if (!m.matches()) { - break; - } else { - version = m.group(1).trim(); - uploader = block.selectFirst("a[href*=user/]").text(); - hearingImpaired = !block.select("img[title~=Hearing]").isEmpty(); + if (version != null) { + Elements tds = block.select("tr:contains(Completed)"); + Elements reqTds = tds.select("td").not("td[rowspan=2]"); + for (Element td : reqTds) { + if (td.hasClass("language")) { + lang = td.html().substring(0, td.html().indexOf("<")); } - } - if (version != null) { - Elements tds = block.select("tr:contains(Completed)"); - Elements reqTds = tds.select("td").not("td[rowspan=2]"); - for (Element td : reqTds) { - if (td.hasClass("language")) { - lang = td.html().substring(0, td.html().indexOf("<")); - } - - // incomplete not wanted - if ((lang != null && td.toString().toLowerCase().contains("completed")) - && td.html().toLowerCase().contains("% completed")) { - lang = null; - } + // incomplete not wanted + 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"); - } - if (downloadElements.size() == 2) { - download = DOMAIN + downloadElements.get(1).attr("href"); - } + Elements downloadElements = td.getElementsByClass("buttonDownload"); + if (lang != null && !downloadElements.isEmpty()) { + if (downloadElements.size() == 1) { + download = DOMAIN + downloadElements.first.attr("href"); + } else if (downloadElements.size() == 2) { + download = DOMAIN + downloadElements.get(1).attr("href"); } - if (lang != null && download != null && title != null) { - Addic7edSubtitleDescriptor sub = - new Addic7edSubtitleDescriptor() - .setUploader(uploader) - .setTitle(title.trim()) - .setVersion(version.trim()) - .setUrl(download) - .setLanguage(Language.fromValueOptional(lang.trim()).orElse(null)) - .setHearingImpaired(hearingImpaired); - if (!isDuplicate(lSubtitles, sub)) { - lSubtitles.add(sub); - } - lang = null; - download = null; + } + if (lang != null && download != null && title != null) { + Addic7edSubtitleDescriptor sub = + new Addic7edSubtitleDescriptor().setUploader(uploader) + .setTitle(title.trim()) + .setVersion(version.trim()) + .setUrl(download) + .setLanguage(Language.fromValueOptional(lang.trim()).orElse(null)) + .setHearingImpaired(hearingImpaired); + if (!isDuplicate(lSubtitles, sub)) { + lSubtitles.add(sub); } + lang = null; + download = null; } } } - return lSubtitles; - }).getCollection(); + } + return lSubtitles; + }) + .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 +212,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..ff66ff16 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,26 +1,26 @@ 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 -public class DownloadSubtitle extends OpenSubtitlesExecuter { +public final class DownloadSubtitle extends OpenSubtitlesExecuter { private final ApiClient apiClient; private int fileId; 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..5bc809c2 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() - .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) - .retryWait(5) - .getAsJsonArray(); - return shows.stream() - .filter(show -> "tv".equals(show.getString("kind"))) - .map(show -> new OpensubtitleSerieId(show.getString("name"), show.getInt("id"), show.getString("year"))) - .toList(); + 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(exc -> exc instanceof HttpClientException e && e.responseCode == 429) + .retryWait(5) + .getAsJsonArray() + .stream() + .filter(show -> "tv".equals(show.getString("kind"))) + .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..560602ad 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,10 +1,9 @@ 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 { +public abstract sealed class OpenSubtitlesExecuter permits DownloadSubtitle, SearchSubtitles { protected T execute(ThrowingSupplier callable) throws ApiException { try { 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..09978325 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,16 +22,11 @@ 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 @RequiredArgsConstructor -public class SearchSubtitles extends OpenSubtitlesExecuter { +public final class SearchSubtitles extends OpenSubtitlesExecuter { private final Manager manager; private final ApiClient apiClient; @@ -77,28 +76,34 @@ 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)) - .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)); - } catch (Exception e) { - throw new OpenSubtitlesException(e); - } - }) - .get(); + .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)) + .valueSupplier(() -> { + try { + 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); + } + }) + .get(); } 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..a148e83a 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,85 +27,85 @@ 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 - ? Optional.of(new ProviderSerieId(showName, showName)) - : Optional.empty(); + 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) - throws PodnapisiException { + 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) - throws PodnapisiException { + 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) - throws PodnapisiException { - return getManager().valueBuilder() - .memoryCache() - .key("%s-subtitles-%s-%s-%s-%s".formatted(getSubtitleSource().name(), providerSerieId.getProviderId(), 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)); - if (PODNAPISI_LANGS.containsKey(language)) { - url.append("&sJ=").append(PODNAPISI_LANGS.get(language)); - } - if (year != null) { - url.append("&sY=").append(year); - } - if (season > 0) { - url.append("&sTS=").append(season).append("&sT=1"); // series - } else { - url.append("&sT=0"); // movies - } - if (episode > 0) { - url.append("&sTE=").append(episode); - } - url.append("&sXML=1"); - - return getXml(url.toString()).select("subtitle").stream().map(this::parsePodnapisiSubtitle).toList(); - } catch (Exception e) { - throw new PodnapisiException(e); + private List getSubtitles(SerieMapping providerSerieId, Integer year, int season, + int episode, Language language) throws PodnapisiException { + return manager.valueBuilder() + .memoryCache() + .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.providerId.trim().toLowerCase(), StandardCharsets.UTF_8)); + if (PODNAPISI_LANGS.containsKey(language)) { + url.append("&sJ=").append(PODNAPISI_LANGS.get(language)); + } + if (year != null) { + url.append("&sY=").append(year); + } + if (season > 0) { + url.append("&sTS=").append(season).append("&sT=1"); // series + } else { + url.append("&sT=0"); // movies + } + if (episode > 0) { + url.append("&sTE=").append(episode); } - }) - .getCollection(); + url.append("&sXML=1"); + + return getXml(url.toString()).selectAllByTag("subtitle") + .stream() + .map(this::parsePodnapisiSubtitle) + .toList(); + } catch (Exception e) { + throw new PodnapisiException(e); + } + }) + .getCollection(); } - 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 - && httpClientException.getResponseCode() < 600) - .retryWait(5).getAsJsoupDocument(); + return manager.getPageContentBuilder() + .url(url) + .userAgent(userAgent) + .cacheType(CacheType.MEMORY) + .retries(1) + .retryPredicate(e -> e instanceof HttpClientException httpClientException && + httpClientException.responseCode >= 500 && httpClientException.responseCode < 600) + .retryWait(5) + .getAsJsoupDocument(); } catch (Exception e) { throw new PodnapisiException(e); } @@ -110,83 +114,84 @@ 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()) - .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; + .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()) + .year(getText.apply(elem.selectFirst("year"))) + .imdb(getText.apply(elem.selectFirst("imdb"))) + .omdb(getText.apply(elem.selectFirst("omdb"))) + .build(); } 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 - .unmodifiableMap(new EnumMap<>(Language.class) { - @Serial - private static final long serialVersionUID = 2950169212654074275L; - - { - put(Language.SLOVENIAN, "1"); - put(Language.ENGLISH, "2"); - put(Language.NORWEGIAN, "3"); - put(Language.KOREAN, "4"); - put(Language.GERMAN, "5"); - put(Language.ICELANDIC, "6"); - put(Language.CZECH, "7"); - put(Language.FRENCH, "8"); - put(Language.ITALIAN, "9"); - put(Language.BOSNIAN, "10"); - put(Language.JAPANESE, "11"); - put(Language.ARABIC, "12"); - put(Language.ROMANIAN, "13"); - put(Language.SPANISH, "14"); // es-ar Spanish (Argentina) - put(Language.HUNGARIAN, "15"); - put(Language.GREEK, "16"); - put(Language.CHINESE_SIMPLIFIED, "17"); - put(Language.LITHUANIAN, "19"); - put(Language.ESTONIAN, "20"); - put(Language.LATVIAN, "21"); - put(Language.HEBREW, "22"); - put(Language.DUTCH, "23"); - put(Language.DANISH, "24"); - put(Language.SWEDISH, "25"); - put(Language.POLISH, "26"); - put(Language.RUSSIAN, "27"); - put(Language.SPANISH, "28"); - put(Language.ALBANIAN, "29"); - put(Language.TURKISH, "30"); - put(Language.FINNISH, "31"); - put(Language.PORTUGUESE, "32"); - put(Language.BULGARIAN, "33"); - put(Language.MACEDONIAN, "35"); - put(Language.SLOVAK, "37"); - put(Language.CROATIAN, "38"); - put(Language.CHINESE_SIMPLIFIED, "40"); - put(Language.HINDI, "42"); - put(Language.THAI, "44"); - put(Language.UKRAINIAN, "46"); - put(Language.SERBIAN, "47"); - put(Language.PORTUGUESE, "48"); // Portuguese (Brazil) - put(Language.IRISH, "49"); - put(Language.BELARUSIAN, "50"); - put(Language.VIETNAMESE, "51"); - put(Language.PERSIAN, "52"); - put(Language.CATALAN, "53"); - put(Language.INDONESIAN, "54"); - - } - }); + private static final Map PODNAPISI_LANGS = + Collections.unmodifiableMap(new EnumMap<>(Language.class) { + @Serial private static final long serialVersionUID = 2950169212654074275L; + + { + put(Language.SLOVENIAN, "1"); + put(Language.ENGLISH, "2"); + put(Language.NORWEGIAN, "3"); + put(Language.KOREAN, "4"); + put(Language.GERMAN, "5"); + put(Language.ICELANDIC, "6"); + put(Language.CZECH, "7"); + put(Language.FRENCH, "8"); + put(Language.ITALIAN, "9"); + put(Language.BOSNIAN, "10"); + put(Language.JAPANESE, "11"); + put(Language.ARABIC, "12"); + put(Language.ROMANIAN, "13"); + put(Language.SPANISH, "14"); // es-ar Spanish (Argentina) + put(Language.HUNGARIAN, "15"); + put(Language.GREEK, "16"); + put(Language.CHINESE_SIMPLIFIED, "17"); + put(Language.LITHUANIAN, "19"); + put(Language.ESTONIAN, "20"); + put(Language.LATVIAN, "21"); + put(Language.HEBREW, "22"); + put(Language.DUTCH, "23"); + put(Language.DANISH, "24"); + put(Language.SWEDISH, "25"); + put(Language.POLISH, "26"); + put(Language.RUSSIAN, "27"); + put(Language.SPANISH, "28"); + put(Language.ALBANIAN, "29"); + put(Language.TURKISH, "30"); + put(Language.FINNISH, "31"); + put(Language.PORTUGUESE, "32"); + put(Language.BULGARIAN, "33"); + put(Language.MACEDONIAN, "35"); + put(Language.SLOVAK, "37"); + put(Language.CROATIAN, "38"); + put(Language.CHINESE_SIMPLIFIED, "40"); + put(Language.HINDI, "42"); + put(Language.THAI, "44"); + put(Language.UKRAINIAN, "46"); + put(Language.SERBIAN, "47"); + put(Language.PORTUGUESE, "48"); // Portuguese (Brazil) + put(Language.IRISH, "49"); + put(Language.BELARUSIAN, "50"); + put(Language.VIETNAMESE, "51"); + put(Language.PERSIAN, "52"); + put(Language.CATALAN, "53"); + put(Language.INDONESIAN, "54"); + + } + }); } 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..0863f03e 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.responseCode == 409 || httpClientException.responseCode == 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,79 @@ 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()); - int season = 0; - if (matcher.matches()) { - 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)); + 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); + } + 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)) - .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)) - .toList(); - } catch (Exception e) { - throw new SubsceneException(e); - } - }).getCollection(); + 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.providerId) + .selectAllByCss("td.a1") + .stream() +// .map(Element::parent) + .map(el -> (Element) el.parent()) + .map(row -> new SubsceneSubtitleDescriptor().setLanguage( + Language.fromValueOptional(row.selectAllByCss(".a1 span.l").text().trim()) + .orElse(null)) + .setUrlSupplier(() -> getDownloadUrl( + DOMAIN + row.selectAllByCss(".a1 > a").attr("href").trim())) + .setName(row.selectAllByCss(".a1 span:not(.l)").text().trim()) + .setHearingImpaired(row.selectFirstByCss(".a41") != null) + .setUploader(row.selectFirstByCss(".a5 > a").text().trim()) + .setComment(row.selectFirstByCss(".a6 > div").text().trim())) + .filter(subDescriptor -> subDescriptor.getSeasonEpisode() != null && + subDescriptor.getSeasonEpisode().episodes.stream() + .anyMatch(ep -> ep == episode)) + .toList(); + } catch (Exception e) { + throw new SubsceneException(e); + } + }) + .getCollection(); } private String getDownloadUrl(String seriePageUrl) throws SubsceneException { try { - return DOMAIN + getJsoupDocument(seriePageUrl).selectFirst("#downloadButton").attr("href"); + String href = getJsoupDocument(seriePageUrl).selectFirstById("downloadButton").attr("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,99 +169,94 @@ private void setIncludeHearingImpairedWithCookie(boolean includeHearingImpaired) } private void addCookie(String cookieName, String cookieValue) { - getManager().storeCookies("subscene.com", Map.of(cookieName, cookieValue)); - } - - @Override - public SubtitleSource getSubtitleSource() { - return SubtitleSource.SUBSCENE; + manager.storeCookies("subscene.com", Map.of(cookieName, cookieValue)); } - 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 @@ -284,15 +292,15 @@ private enum OrdinalNumber { 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"), + THIRTY_FIRST(31, "Thirty-First"), + THIRTY_SECOND(32, "Thirty-Second"), + THIRTY_THIRD(33, "Thirty-Third"), + THIRTY_FOURTH(34, "Thirty-Fourth"), + THIRTY_FIFTH(35, "Thirty-Fifth"), + THIRTY_SIXTH(36, "Thirty-Sixth"), + THIRTY_SEVENTH(37, "Thirty-Seventh"), + THIRTY_EIGHTH(38, "Thirty-Eighth"), + THIRTY_NINTH(39, "Thirty-Ninth"), FORTIETH(40, "Fortieth"), FORTY_FIRST(41, "Forty-First"), FORTY_SECOND(42, "Forty-Second"), @@ -359,8 +367,9 @@ private enum OrdinalNumber { private final String value; public static Optional optionalFromValue(String value) { - return Stream.of(OrdinalNumber.values()).filter(ordinalNumber -> StringUtils.equalsIgnoreCase(value, ordinalNumber.getValue())) - .findAny(); + 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..8d45c6a2 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.size() != 1) { + continue; + } + for (Element item : subtitlePageTableDoc.getFirst().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").attr("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..f04e32a7 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,6 +1,9 @@ package org.lodder.subtools.multisubdownloader.util; +import javax.swing.*; +import java.awt.*; import java.io.IOException; +import java.io.Serial; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; @@ -10,8 +13,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 +29,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,69 +41,63 @@ 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())) - .ifPresent(path -> { - try { - switch (listType) { - case PREFERENCES -> ExportImportPreferences.exportSettings(path, settingsControl); - case SERIE_MAPPING -> ExportImportSerieMapping.exportSettings(path, manager); - default -> throw new IllegalArgumentException("Unexpected value: " + listType); - } - } catch (Exception e) { - userInteractionHandler.showMessage(Messages.getString("ImportExport.ErrorWhileExporting"), - Messages.getString("ImportExport.ErrorWhileExporting"), MessageSeverity.ERROR); + 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) { + case PREFERENCES -> ExportImportPreferences.exportSettings(path, settingsControl); + case SERIE_MAPPING -> ExportImportSerieMapping.exportSettings(path, manager); + default -> throw new IllegalArgumentException("Unexpected value: " + listType); } - }); + } catch (Exception e) { + userInteractionHandler.showMessage(Messages.getText("ImportExport.ErrorWhileExporting"), + Messages.getText("ImportExport.ErrorWhileExporting"), MessageSeverity.ERROR); + } + }); } - @ExtensionMethod({ StreamExtension.class }) @UtilityClass public static class ExportImportPreferences { @@ -116,8 +105,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,48 +119,62 @@ 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()) - .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()))) - .toList(); + 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()))) + .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()) - .map(MappingType::getSelectionForKeyPrefixList) - .flatMap(Arrays::stream) - .forEach(selectionForKeyPrefix -> manager.clearExpiredCacheBuilder() - .cacheType(CacheType.DISK) - .keyFilter((String k) -> k.startsWith(selectionForKeyPrefix.keyPrefix())) - .clear()); + MappingType.values().stream() + .map(MappingType::getSelectionForKeyPrefixList) + .flatMap(Arrays::stream) + .forEach(selectionForKeyPrefix -> manager.clearExpiredCacheBuilder() + .cacheType(CacheType.DISK) + .keyFilter((String k) -> k.startsWith(selectionForKeyPrefix.keyPrefix())) + .clear()); } - Arrays.stream(serieMappings).forEach(serieMapping -> manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(serieMapping.key) - .value(serieMapping.serieMapping) - .store()); + serieMappings.forEach(serieMapping -> manager.valueBuilder() + .cacheType(CacheType.DISK) + .key(serieMapping.key) + .value(serieMapping.serieMapping) + .store()); }); } + private static Optional getImportStyle(UserInteractionHandler userInteractionHandler) { + return userInteractionHandler.choice(Arrays.asList(ImportStyle.values()), + Messages.getText("ImportExport.OverwriteOrAdd"), + Messages.getText("ImportExport.OverwriteOrAddTitle"), + option -> switch (option) { + case OVERWRITE -> Messages.getText("ImportExport.Overwrite"); + case APPEND -> Messages.getText("ImportExport.Add"); + }); + } + @AllArgsConstructor @Data private static class SeriemappingWithKey implements Serializable { - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; private String key; private SerieMapping serieMapping; } @@ -181,7 +184,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()); @@ -190,20 +193,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"); - }); - } - private enum ImportStyle { OVERWRITE, APPEND } @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..1cea32d7 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 @@ -4,10 +4,13 @@ import java.io.InputStream; import java.util.Properties; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; + 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")) { @@ -27,7 +30,14 @@ private static PropertiesReader getPropertiesReader() { return propertiesReaderInstance; } - public static String getProperty(String propertyName) { - return PropertiesReader.getPropertiesReader().properties.getProperty(propertyName); + public static String getProperty(PomProperty property) { + return PropertiesReader.getPropertiesReader().properties.getProperty(property.value); + } + + @AllArgsConstructor + public enum PomProperty { + BUILD_TIMESTAMP("build.timestamp"); + + @val String value; } } 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..9fa1eb4a 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); @@ -87,10 +95,10 @@ public void addProvider(SubtitleProvider provider) { } public void addRelease(Release release) { - this.queue.forEach((key, value) -> queue.get(key).add(release)); + this.queue.forEach((key, _) -> 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..59aa093d 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,16 +1,15 @@ 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; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +import org.lodder.subtools.sublibrary.model.TvRelease; class SubtitleFilteringTest { @@ -31,10 +30,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 +57,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 +82,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,19 +107,19 @@ 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; } private Release createRelease(String filename, String releasegroup) { - Release release = mock(Release.class); + Release release = mock(TvRelease.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 +127,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 +153,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 +165,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 +175,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..01adc265 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 @@ -8,11 +8,12 @@ import org.junit.jupiter.api.Test; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +import org.lodder.subtools.sublibrary.model.TvRelease; 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 +45,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); + Release release = mock(TvRelease.class); + 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..24bc96f6 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 @@ -8,15 +8,16 @@ import org.junit.jupiter.api.Test; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.TvRelease; class SortWeightTest { @Test 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"); + Release release = mock(TvRelease.class); + when(release.quality).thenReturn("DVDRip XviD"); + when(release.releaseGroup).thenReturn("MEDiEVAL"); HashMap definedWeights = new HashMap<>(); definedWeights.put("dvdrip", 2); @@ -25,17 +26,16 @@ void test_it_generates_weights_for_release() throws Exception { definedWeights.put("%GROUP%", 5); SortWeight sortWeight = new SortWeight(release, definedWeights); - Map weights = sortWeight.getWeights(); + Map weights = sortWeight.weights; /* check if we have the 3 weights */ - assertThat(weights).hasSize(3).containsKeys("dvdrip", "xvid", "medieval"); - - /* check if the weights are correct */ - assertThat(weights.get("dvdrip")).isEqualTo(2); - assertThat(weights.get("xvid")).isEqualTo(1); - assertThat(weights.get("medieval")).isEqualTo(5); + assertThat(weights) + .hasSize(3) + .containsEntry("dvdrip", 2) + .containsEntry("xvid", 1) + .containsEntry("medieval", 5); /* check if the maxScore is correct */ - assertThat(sortWeight.getMaxScore()).isEqualTo(8); + assertThat(sortWeight.maxScore).isEqualTo(8); } } 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..255f5dee 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 @@ -8,20 +8,21 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.TvRelease; class GroupReplacerTest { protected GroupReplacer replacer; @BeforeEach - public void setUp() throws Exception { + public void setUp() { replacer = new GroupReplacer(); } @Test - void test_it_replaces_the_keyword_group_to_a_releasename() throws Exception { - Release release = mock(Release.class); - when(release.getReleaseGroup()).thenReturn("Acme"); + void test_it_replaces_the_keyword_group_to_a_releasename() { + Release release = mock(TvRelease.class); + when(release.releaseGroup).thenReturn("Acme"); HashMap definedWeights = new HashMap<>(); definedWeights.put("%GROUP%", 5); @@ -36,9 +37,9 @@ 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"); + void testEmptyWeights() { + Release release = mock(TvRelease.class); + when(release.releaseGroup).thenReturn("Acme"); HashMap definedWeights = new HashMap<>(); diff --git a/SubLibrary/pom.xml b/SubLibrary/pom.xml index cadd27b4..174c4411 100644 --- a/SubLibrary/pom.xml +++ b/SubLibrary/pom.xml @@ -1,162 +1,181 @@ - - org.lodder.subtools - subtools - 1.5.1-SNAPSHOT - + + org.lodder.subtools + subtools + 1.5.1-SNAPSHOT + - 4.0.0 - sublibrary - SubLibrary + 4.0.0 + sublibrary + SubLibrary - - - org.openapitools - openapi-generator-core - - - javax.annotation - javax.annotation-api - - - io.swagger - swagger-annotations - - - com.squareup.okhttp3 - logging-interceptor - - - com.google.code.gson - gson - - - io.gsonfire - gson-fire - - - com.fasterxml.jackson.core - jackson-databind - - - com.pivovarit - throwing-function - - - name.falgout.jeffrey - throwing-interfaces - - - name.falgout.jeffrey - throwing-streams - - - com.optimaize.languagedetector - language-detector - - - org.apache.commons - commons-lang3 - - - org.jsoup - jsoup - - - org.hsqldb - hsqldb - - - ch.qos.logback - logback-classic - - - javax.ws.rs - javax.ws.rs-api - - - com.uwetrottmann.thetvdb-java - thetvdb-java - - - org.projectlombok - lombok - provided - - - - org.codehaus.plexus - plexus-interactivity-api - - - - com.massisframework - j-text-utils - - - org.json - json - - - net.jodah - typetools - + + + org.openapitools + openapi-generator-core + + + io.swagger.core.v3 + swagger-annotations + + + com.squareup.okhttp3 + logging-interceptor + + + com.google.code.gson + gson + + + io.gsonfire + gson-fire + + + com.fasterxml.jackson.core + jackson-databind + + + com.pivovarit + throwing-function + + + name.falgout.jeffrey + throwing-interfaces + + + name.falgout.jeffrey + throwing-streams + + + com.optimaize.languagedetector + language-detector + + + org.apache.commons + commons-lang3 + + + org.jsoup + jsoup + + + org.jspecify + jspecify + + + org.hsqldb + hsqldb + + + ch.qos.logback + logback-classic + + + jakarta.ws.rs + jakarta.ws.rs-api + + + com.uwetrottmann.thetvdb-java + thetvdb-java + + + systems.manifold + manifold-ext-rt + + + systems.manifold + manifold-props-rt + + + systems.manifold + manifold-tuple-rt + + + org.projectlombok + lombok + provided + + + + org.codehaus.plexus + plexus-interactivity-api + + + + com.massisframework + j-text-utils + + + org.json + json + + + net.jodah + typetools + - - org.mockito - mockito-core - test - - - org.assertj - assertj-core - test - - - org.junit.jupiter - junit-jupiter-api - test - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - 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.mockito + mockito-core + test + + + org.assertj + assertj-core + test + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + 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} + + + systems.manifold + manifold-tuple + ${manifold.version} + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + + java,class + + + + + + \ No newline at end of file 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 67% 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..5f3deffb 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,44 +15,55 @@ 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, ""); } + public record FilenameAndExtension(String filename, String extension) {} + + public static FilenameAndExtension splitExtension(@This Path path) { + return new FilenameAndExtension(StringUtils.substringBeforeLast(path.getFileName().toString(), "."), + StringUtils.substringAfterLast(path.getFileName().toString(), ".")); + } + 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(); } /** * Moves a file or a complete directory tree. *

- * This method moves the given {@link Path} to the specified destination. Depending on whether - * the path is a directory or a regular file, the behavior of the method is as follows: + * This method moves the given {@link Path} to the specified destination. Depending on whether the path is a + * directory or a regular file, the behavior of the method is as follows: *

    *
  • If the {@link Path} is a directory, it will recursively move all files and subdirectories * within the directory hierarchy, starting from and including the given path, to the @@ -64,25 +75,22 @@ public static String toAbsolutePathAsString(Path path) { * existing file or directory, using the {@link StandardCopyOption} enum. These options are * applied to all files and directories being moved. * - * @param source - * the path to be moved - * @param destinationDir - * the destination directory - * @param copyOptions - * optional move options to apply while moving the path + * @param source the path to be moved + * @param destinationDir the destination directory + * @param copyOptions optional move options to apply while moving the path * @return the destination - * @throws IOException - * if an I/O error occurs while moving the 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); } /** * Moves a file or a complete directory tree. *

    - * This method moves the given {@link Path} to the specified destination. Depending on whether - * the path is a directory or a regular file, the behavior of the method is as follows: + * This method moves the given {@link Path} to the specified destination. Depending on whether the path is a + * directory or a regular file, the behavior of the method is as follows: *

      *
    • If the {@link Path} is a directory, it will recursively move all files and subdirectories * within the directory hierarchy, starting from and including the given path, to the @@ -95,20 +103,16 @@ public static Path moveToDir(Path source, Path destinationDir, StandardCopyOptio * existing file or directory, using the {@link StandardCopyOption} enum. These options are * applied to all files and directories being moved. * - * @param source - * the path to be moved - * @param destinationDir - * the destination directory - * @param newFileName - * the new file name - * @param copyOptions - * optional move options to apply while moving the path + * @param source the path to be moved + * @param destinationDir the destination directory + * @param newFileName the new file name + * @param copyOptions optional move options to apply while moving the path * @return the destination - * @throws IOException - * if an I/O error occurs while moving the path + * @throws IOException if an I/O error occurs while moving the path */ - public static Path moveToDirAndRename(Path source, Path destinationDir, String newFileName, StandardCopyOption... copyOptions) - throws IOException { + public static Path moveToDirAndRename(@This Path source, Path destinationDir, String newFileName, + StandardCopyOption... copyOptions) + throws IOException { Files.createDirectories(destinationDir); if (Files.isRegularFile(source)) { Files.move(source, destinationDir.resolve(newFileName), copyOptions); @@ -123,7 +127,8 @@ public static Path moveToDirAndRename(Path source, Path destinationDir, String n return destinationDir.resolve(newFileName); } - private static Path moveNonEmptyDirectory(Path sourceDir, Path targetDir, StandardCopyOption... copyOptions) throws IOException { + private static Path moveNonEmptyDirectory(Path sourceDir, Path targetDir, StandardCopyOption... copyOptions) + throws IOException { if (Files.isDirectory(sourceDir)) { return moveNonEmptyDirectoryRecursively(sourceDir, targetDir, copyOptions); } else { @@ -131,9 +136,10 @@ private static Path moveNonEmptyDirectory(Path sourceDir, Path targetDir, Standa } } - private static Path moveNonEmptyDirectoryRecursively(Path source, Path target, StandardCopyOption... copyOptions) throws IOException { + private static Path moveNonEmptyDirectoryRecursively(Path source, Path target, StandardCopyOption... copyOptions) + throws IOException { foreachSubfile(source, s -> s.asThrowingStream(IOException.class) - .forEach(child -> moveNonEmptyDirectory(child, target.resolve(source.getFileName()), copyOptions))); + .forEach(child -> moveNonEmptyDirectory(child, target.resolve(source.getFileName()), copyOptions))); Files.delete(source); return target; } @@ -141,29 +147,27 @@ private static Path moveNonEmptyDirectoryRecursively(Path source, Path target, S /** * Deletes a {@link Path}. *

      - * If the {@link Path} exists, this method will delete it without any possibility of recovery. - * This method behaves as follows: + * If the {@link Path} exists, this method will delete it without any possibility of recovery. This method behaves + * as follows: *

        *
      • If the {@link Path} is a directory, it will recursively delete all files and * subdirectories within the directory, starting from and including the given path.
      • *
      • If the {@link Path} is a regular file, it will be deleted.
      • *
      * - * @param path - * the path to delete - * @throws IOException - * if an I/O error occurs while deleting the path + * @param path the path to delete + * @throws IOException 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()); } /** * Copies a file or a complete directory tree. *

      - * This method copies the given {@link Path} to the specified destination. Depending on whether - * the path is a directory or a regular file, the behavior of the method is as follows: + * This method copies the given {@link Path} to the specified destination. Depending on whether the path is a + * directory or a regular file, the behavior of the method is as follows: *

        *
      • If the {@link Path} is a directory, it will recursively copy all files and subdirectories * within the directory hierarchy, starting from and including the given path, to the @@ -175,25 +179,22 @@ public static void delete(Path path) throws IOException { * existing file or directory, using the {@link StandardCopyOption} enum. These options are * applied to all files and directories being copied. * - * @param source - * the path to be copied - * @param destinationDir - * the destination directory - * @param copyOptions - * optional copy options to apply while copying the path + * @param source the path to be copied + * @param destinationDir the destination directory + * @param copyOptions optional copy options to apply while copying the path * @return the location of the copied path - * @throws IOException - * if an I/O error occurs while deleting the path + * @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); } /** * Copies a file or a complete directory tree. *

        - * This method copies the given {@link Path} to the specified destination. Depending on whether - * the path is a directory or a regular file, the behavior of the method is as follows: + * This method copies the given {@link Path} to the specified destination. Depending on whether the path is a + * directory or a regular file, the behavior of the method is as follows: *

          *
        • If the {@link Path} is a directory, it will recursively copy all files and subdirectories * within the directory hierarchy, starting from and including the given path, to the @@ -206,20 +207,16 @@ public static Path copyToDir(Path source, Path destinationDir, StandardCopyOptio * existing file or directory, using the {@link StandardCopyOption} enum. These options are * applied to all files and directories being copied. * - * @param source - * the path to be copied - * @param destinationDir - * the destination directory - * @param newFileName - * the new file name - * @param copyOptions - * optional copy options to apply while copying the path + * @param source the path to be copied + * @param destinationDir the destination directory + * @param newFileName the new file name + * @param copyOptions optional copy options to apply while copying the path * @return the location of the copied path - * @throws IOException - * if an I/O error occurs while deleting the path + * @throws IOException if an I/O error occurs while deleting the path */ - public static Path copyToDirAndRename(Path source, Path destinationDir, String newFileName, StandardCopyOption... copyOptions) - throws IOException { + public static Path copyToDirAndRename(@This Path source, Path destinationDir, String newFileName, + StandardCopyOption... copyOptions) + throws IOException { if (Files.isRegularFile(source)) { Files.createDirectories(destinationDir); Path destinationFile = destinationDir.resolve(newFileName); @@ -233,36 +230,38 @@ 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)); } @@ -312,13 +311,13 @@ public static boolean isGZipCompressed(byte[] bytes) { return false; } else { return bytes[0] == (byte) GZIPInputStream.GZIP_MAGIC - && bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8); + && bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8); } } public static byte[] decompressGZip(byte[] data) throws IOException { try (ByteArrayInputStream binput = new ByteArrayInputStream(data); - GZIPInputStream gzinput = new GZIPInputStream(binput)) { + GZIPInputStream gzinput = new GZIPInputStream(binput)) { return gzinput.readAllBytes(); } } diff --git a/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java b/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java new file mode 100644 index 00000000..c5b34fed --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java @@ -0,0 +1,12 @@ +package extensions.java.util.Collection; + +import manifold.ext.rt.api.Extension; + +@Extension +public class CollectionExt { + +// @Intercept +// public static int size(@This @Nullable Collection collection) { +// return collection == null ? 0 : collection.size(); +// } +} 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..8ce2be34 --- /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..a6503fdc --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java @@ -0,0 +1,239 @@ +package extensions.org.jsoup.nodes.Element; + +import extensions.org.jsoup.select.Elements.UnmodifiableElements; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Intercept; +import manifold.ext.rt.api.Jailbreak; +import manifold.ext.rt.api.This; +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.jspecify.annotations.Nullable; +import org.lodder.subtools.sublibrary.exception.WebpageException; + +@Extension +public class ElementExt { + + // --------------- \\ + // Get all Element \\ + // --------------- \\ + + public static Elements selectAllByClass(@This @Nullable Element element, String className) { + return element == null ? UnmodifiableElements.EMPTY : element.getElementsByClass(requireNotEmpty(className)); + } + + public static Elements selectAllByTag(@This @Nullable Element element, String tagName) { + return element == null ? UnmodifiableElements.EMPTY : element.getElementsByTag(requireNotEmpty(tagName)); + } + + public static Elements selectAllByAttribute(@This @Nullable Element element, String attribute) { + return element == null ? UnmodifiableElements.EMPTY : + element.getElementsByAttribute(requireNotEmpty(attribute)); + } + + public static Elements selectAllByCss(@This @Nullable Element element, String cssQuery) { + return element == null ? UnmodifiableElements.EMPTY : element.getElements(requireNotEmpty(cssQuery)); + } + + public static Elements selectAll(@This @Nullable Element element, Evaluator evaluator) { + return element == null ? UnmodifiableElements.EMPTY : Collector.collect(evaluator, element); + } + + // ----------------- \\ + // Get First Element \\ + // ----------------- \\ + + public static @Nullable Element selectFirstByClass(@This @Nullable Element element, String className) { + return element == null ? null : element.selectFirst(new Evaluator.Class(requireNotEmpty(className))); + } + + public static @Nullable Element selectFirstByTag(@This @Nullable Element element, String tagName) { + return element == null ? null : element.selectFirst(new Evaluator.Tag(requireNotEmpty(tagName))); + } + + public static @Nullable Element selectFirstByAttribute(@This @Nullable Element element, String attribute) { + return element == null ? null : element.selectFirst(new Evaluator.Attribute(requireNotEmpty(attribute))); + } + + public static @Nullable Element selectFirstByCss(@This @Nullable Element element, String cssQuery) { + return element == null ? null : element.selectFirst(QueryParser.parse(requireNotEmpty(cssQuery))); + } + + public static @Nullable Element selectFirstById(@This @Nullable Element element, String id) { + return element == null ? null : element.selectFirst(new Evaluator.Id(requireNotEmpty(id))); + } + + public static @Nullable Element selectFirstBy(@This @Nullable Element element, Evaluator evaluator) { + return element == null ? null : Collector.findFirst(evaluator, element); + } + + // ---------------- \\ + // Get n-th Element \\ + // ---------------- \\ + + public static @Nullable Element selectNthByClass(@This @Nullable Element element, String className, int index) { + return element == null ? null : selectNth(element, new Evaluator.Class(requireNotEmpty(className)), index); + } + + public static @Nullable Element selectNthByTag(@This @Nullable Element element, String tagName, int index) { + return element == null ? null : selectNth(element, new Evaluator.Tag(requireNotEmpty(tagName)), index); + } + + public static @Nullable Element selectNthByAttribute(@This @Nullable Element element, String attribute, + int index) { + return element == null ? null : + selectNth(element, new Evaluator.Attribute(requireNotEmpty(attribute)), index); + } + + public static @Nullable Element selectNthByCss(@This @Nullable Element element, String cssQuery, int index) { + return element == null ? null : selectNth(element, QueryParser.parse(requireNotEmpty(cssQuery)), index); + } + + public static @Nullable Element selectNth(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable 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(@This @Nullable Element element, String cssQuery) { + return element == null ? new Elements() : element.select(cssQuery); + } + + // --------------- \\ + // Element methods \\ + // --------------- \\ + + + @Intercept + public static String text(@This @Nullable Element element) { + return element == null ? "" : element.text(); + } + + @Intercept + public static @Nullable Element parent(@This @Nullable Element element) { + return element == null ? null : element.parent(); + } + + // --------------- \\ + // 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..19e6a541 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java @@ -0,0 +1,37 @@ +package extensions.org.jsoup.nodes.Element; + +import static org.jsoup.select.NodeFilter.FilterResult.*; + +import lombok.RequiredArgsConstructor; +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; +import org.jspecify.annotations.Nullable; + +@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..7f560e37 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Node/NodeExt.java @@ -0,0 +1,110 @@ +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.Intercept; +import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingFunction; +import org.jsoup.nodes.Node; +import org.jspecify.annotations.Nullable; + +@Extension +public class NodeExt { + + // ------------ \\ + // Node methods \\ + // ------------ \\ + + @Intercept + public static @Nullable Node parent(@This @Nullable Node node) { + return node == null ? null : node.parent(); + } + + @Intercept + public static String attr(@This @Nullable Node node, String attr) { + return node == null ? null : node.attr(attr); + } + + public static T useAttrOrElse(@This @Nullable Node node, String attr, Function mapper, + Supplier supplier) { + return node != null && node.hasAttr(attr) ? mapper.apply(node.attr(attr)) : supplier.get(); + } + + public static Optional getAttrOptional(@This @Nullable Node node, String attr) { + return Optional.ofNullable(node.attr(attr)); + } + + // ------------- \\ + // Other methods \\ + // ------------- \\ + + public static @Nullable Node filter(@This @Nullable Node node, Predicate predicate) { + return node != null && predicate.test(node) ? node : null; + } + + public static boolean matches(@This @Nullable Node node, Predicate predicate) { + return node != null && predicate.test(node); + } + + public static <@Nullable T> @Nullable T map(@This @Nullable Node node, Function mapper) { + return node != null ? mapper.apply(node) : null; + } + + public static Optional mapOptional(@This @Nullable Node node, Function mapper) { + return node != null ? Optional.ofNullable(mapper.apply(node)) : Optional.empty(); + } + + public static T mapOrElse(@This @Nullable Node node, Function mapper, T obj) { + return node != null ? mapper.apply(node) : obj; + } + + public static T mapOrElseGet(@This @Nullable Node node, Function mapper, + Supplier elseSupplier) { + return node != null ? mapper.apply(node) : elseSupplier.get(); + } + + public static <@Nullable T, X extends Exception> @Nullable T mapThrowing(@This @Nullable Node node, + ThrowingFunction mapper) + throws X { + return node != null ? mapper.apply(node) : null; + } + + public static Node orElseGet(@This @Nullable Node node, Supplier elementSupplier) { + return node != null ? node : elementSupplier.get(); + } + + public static Node orElseThrow(@This @Nullable Node node, + Supplier exceptionSupplier) throws X { + if (node == null) { + throw exceptionSupplier.get(); + } + return node; + } + + public static void ifPresent(@This @Nullable Node node, Consumer consumer) { + if (node != null) { + consumer.accept(node); + } + } + + public static boolean isFound(@This @Nullable Node node) { + return node != null; + } + + public static boolean isNotNull(@This @Nullable Node node) { + return node != null; + } + + public static boolean isNotFound(@This @Nullable Node node) { + return node == null; + } + + public static boolean isNull(@This @Nullable 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..2d753322 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/ElementsExt.java @@ -0,0 +1,29 @@ +package extensions.org.jsoup.select.Elements; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Intercept; +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 { + + @Intercept + public static String text(@This @Nullable Elements elements) { + return elements == null ? "" : elements.text(); + } + + @Intercept + public static @Nullable String attr(@This @Nullable Elements elements, String attribute) { + return elements == null ? null : elements.attr(attribute); + } + + @Intercept + public static @Nullable Element first(@This @Nullable Elements elements) { + return elements == null ? null : elements.first(); + } +} 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..794e0175 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/w3c/dom/Document/DocumentExt.java @@ -0,0 +1,31 @@ +package extensions.org.w3c.dom.Document; + +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Intercept; +import manifold.ext.rt.api.This; +import org.jsoup.helper.Validate; +import org.jspecify.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +@Extension +public class DocumentExt { + + // ----------------- \\ + // Get First Element \\ + // ----------------- \\ + + @Intercept + public static @Nullable NodeList getElementsByTagName(@This @Nullable 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..064379af 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java @@ -5,43 +5,33 @@ 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 final String BUNDLE_NAME = "resourcebundle.Message"; + private static ResourceBundle resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ROOT); + static @var Language language; private Messages() { } - public static String getString(String key) { + public static String getText(String key, Object... replacements) { try { - return RESOURCE_BUNDLE.getString(key); + String text = resourceBundle.getString(key); + return replacements == null || replacements.isEmpty() ? text : text.formatted(replacements); } catch (MissingResourceException e) { - return '!' + key + '!'; - } - } - - public static String getString(String key, Object... replacements) { - try { - return RESOURCE_BUNDLE.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; + Locale locale = language == Language.ENGLISH ? Locale.ROOT : Locale.forLanguageTag(language.langCode); + resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME, locale); } public static List getAvailableLanguages() { - return List.of(Language.fromId("nl"), DEFAULT_LANGUAGE); + return List.of(Language.DUTCH, Language.ENGLISH); } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheKeyMatchEnum.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheKeyMatchEnum.java deleted file mode 100644 index 4680d4a6..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheKeyMatchEnum.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.lodder.subtools.sublibrary; - -public enum CacheKeyMatchEnum { - STARTING_WITH, ENDING_WITH, CONTAINING, EXACT -} \ No newline at end of file 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..3dd3d930 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java @@ -2,28 +2,37 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Properties; + +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public final class ConfigProperties { - private static ConfigProperties configProps = null; - private final java.util.Properties prop = new java.util.Properties(); + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigProperties.class); + + private static final ConfigProperties instance = new ConfigProperties(); + private final Properties prop = new Properties(); private ConfigProperties() { try (InputStream input = getClass().getResourceAsStream("/config.properties")) { prop.load(input); } catch (IOException ex) { - ex.printStackTrace(); + LOGGER.error("Error loading config properties", ex); } } - public synchronized static ConfigProperties getInstance() { - if (configProps == null) { - configProps = new ConfigProperties(); - } - return configProps; + public static String getProperty(Property property) { + return instance.prop.getProperty(property.value); } - public String getProperty(String key) { - return prop.getProperty(key); + @AllArgsConstructor + public enum Property { + NAME("name"), + VERSION("version"); + + @val String value; } } 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..b376f2f3 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java @@ -7,28 +7,27 @@ 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 { private static final Logger LOGGER = LoggerFactory.getLogger(DetectLanguage.class); - private static final LazyThrowingSupplier DETECTOR = - new LazyThrowingSupplier<>(() -> LanguageDetectorBuilder.create(NgramExtractors.standard()) - .shortTextAlgorithm(0) - .withProfiles(new LanguageProfileReader().readAllBuiltIn()) - .build()); + private static final LazyThrowingSupplier DETECTOR = new LazyThrowingSupplier<>( + () -> LanguageDetectorBuilder.create(NgramExtractors.standard()) + .shortTextAlgorithm(0) + .withProfiles(new LanguageProfileReader().readAllBuiltIn()) + .build()); private static final LazySupplier TEXT_OBJECT_FACTORY = - new LazySupplier<>(CommonTextObjectFactories::forDetectingOnLargeText); + new LazySupplier<>(CommonTextObjectFactories::forDetectingOnLargeText); private static final double MIN_PROBABILITY = 0.9; public static Language execute(Path file) { @@ -41,11 +40,15 @@ public static Language execute(Path file, Language defaultLang) { public static Optional executeOptional(Path file) { try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { - return DETECTOR.get().getProbabilities(TEXT_OBJECT_FACTORY.get().create().append(reader)).stream() - .filter(lang -> lang.getProbability() >= MIN_PROBABILITY).findFirst() - .map(lang -> lang.getLocale().getLanguage()).flatMap(Language::fromValueOptional); + return DETECTOR.get() + .getProbabilities(TEXT_OBJECT_FACTORY.get().create().append(reader)) + .stream() + .filter(lang -> lang.getProbability() >= MIN_PROBABILITY) + .findFirst() + .map(lang -> lang.getLocale().getLanguage()) + .flatMap(Language::fromValueOptional); } catch (IOException e) { - LOGGER.error("Could not detect language of file " + file); + LOGGER.error("Could not detect language of file {} ", file); return Optional.empty(); } } 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..258ba3ec 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,9 +185,10 @@ public interface PageContentBuilderGetIntf { @Setter @Accessors(chain = true, fluent = true) @RequiredArgsConstructor - public static class PageContentBuilder implements PageContentBuilderGetIntf, PageContentBuilderCacheTypeIntf, - PageContentBuilderUserAgentIntf, PageContentBuilderUrlIntf, PageContentBuilderRetryIntf, PageContentBuilderRetryConditionIntf, - PageContentBuilderRetryWaitIntf { + public static class PageContentBuilder + implements PageContentBuilderGetIntf, PageContentBuilderCacheTypeIntf, PageContentBuilderUserAgentIntf, + PageContentBuilderUrlIntf, PageContentBuilderRetryIntf, PageContentBuilderRetryConditionIntf, + PageContentBuilderRetryWaitIntf { private final HttpClient httpClient; private final InMemoryCache inMemoryCache; private String url; @@ -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,8 @@ 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.responseCode, e.responseMessage), e); } catch (IOException e) { if (retries-- > 0 && retryPredicate.test(e)) { return getContentWithoutCache(urlString, userAgent); @@ -318,7 +321,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; @@ -352,7 +356,7 @@ public interface ValueBuilderCacheTypeIntf { ValueBuilderKeyIntf cacheType(CacheType cacheType); - ValueBuilderKeyIntf memoryCache(); + ValueBuilderKeyIntf memoryCache(); ValueBuilderKeyIntf diskCache(); } @@ -376,8 +380,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 +395,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,27 +409,29 @@ 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(); } public interface ValueBuilderGetValueStoreTempValueTtlIntf - extends ValueBuilderGetValueIntf { + extends ValueBuilderGetValueIntf { ValueBuilderGetValueIntf timeToLive(long seconds); - ValueBuilderGetValueIntf timeToLiveFunction(Function timeToLiveFunction); + ValueBuilderGetValueIntf timeToLiveFunction(LongUnaryOperator timeToLiveFunction); } public interface ValueBuilderGetValueIntf extends ValueBuilderStoreIntf { @@ -432,15 +439,15 @@ public interface ValueBuilderGetValueIntf extends ValueB } public interface ValueBuilderGetOptionalStoreTempValueIntf - extends ValueBuilderGetOptionalIntf { + extends ValueBuilderGetOptionalIntf { ValueBuilderGetOptionalStoreTempValueTtlIntf storeTempNullValue(); } public interface ValueBuilderGetOptionalStoreTempValueTtlIntf - extends ValueBuilderGetOptionalIntf { + extends ValueBuilderGetOptionalIntf { ValueBuilderGetOptionalIntf timeToLive(long seconds); - ValueBuilderGetOptionalIntf timeToLiveFunction(Function timeToLiveFunction); + ValueBuilderGetOptionalIntf timeToLiveFunction(LongUnaryOperator timeToLiveFunction); } public interface ValueBuilderGetOptionalIntf extends ValueBuilderStoreIntf { @@ -449,14 +456,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,11 +481,11 @@ 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> - extends ValueBuilderStoreIntf { + extends ValueBuilderStoreIntf { C getCollection() throws X; } @@ -491,13 +500,14 @@ public interface ValueBuilderStoreIntf { @RequiredArgsConstructor @SuppressWarnings({ "rawtypes", "unchecked" }) public static class ValueBuilder, T, X extends Exception> - implements ValueBuilderGetOptionalIntf, ValueBuilderCacheTypeIntf, - ValueBuilderValueSupplierIntf, ValueBuilderKeyIntf, ValueBuilderGetCollectionIntf, ValueBuilderGetOptionalIntIntf, - ValueBuilderRetryIntf, ValueBuilderRetryConditionIntf, ValueBuilderRetryWaitIntf, - ValueBuilderIsPresentIntf, ValuesBuilderCacheTypeIntf, - ValueBuilderStoreIntf, ValueBuilderGetOptionalIntStoreTempValueIntf, ValueBuilderGetOptionalStoreTempValueIntf, - ValueBuilderGetOptionalIntStoreTempValueTtlIntf, ValueBuilderGetOptionalStoreTempValueTtlIntf, - ValueBuilderGetValueStoreTempValueIntf, ValueBuilderGetValueStoreTempValueTtlIntf, ValueBuilderGetValueIntf { + implements ValueBuilderGetOptionalIntf, ValueBuilderCacheTypeIntf, ValueBuilderValueSupplierIntf, + ValueBuilderKeyIntf, ValueBuilderGetCollectionIntf, ValueBuilderGetOptionalIntIntf, + ValueBuilderRetryIntf, ValueBuilderRetryConditionIntf, ValueBuilderRetryWaitIntf, + ValueBuilderIsPresentIntf, ValuesBuilderCacheTypeIntf, ValueBuilderStoreIntf, + ValueBuilderGetOptionalIntStoreTempValueIntf, ValueBuilderGetOptionalStoreTempValueIntf, + ValueBuilderGetOptionalIntStoreTempValueTtlIntf, ValueBuilderGetOptionalStoreTempValueTtlIntf, + ValueBuilderGetValueStoreTempValueIntf, ValueBuilderGetValueStoreTempValueTtlIntf, + ValueBuilderGetValueIntf { private final InMemoryCache inMemoryCache; private final DiskCache diskCache; private String key; @@ -516,15 +526,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 +570,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 +659,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 +723,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 +738,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 +831,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 +855,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 +920,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..3cb0dddb 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) final 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..719010d5 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 @@ -1,13 +1,14 @@ package org.lodder.subtools.sublibrary.cache; -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(); @@ -16,16 +17,8 @@ public interface CacheObject { String toString(Function valueToStringMapper); static CacheObject fromString(String string, Function valueToObjectMapper) { - Optional> cacheObject = ExpiringCacheObject.fromString(string, valueToObjectMapper); - if (cacheObject.isPresent()) { - return cacheObject.get(); - } - Optional> temporaryCacheObject = TemporaryCacheObject.fromString(string, valueToObjectMapper); - if (temporaryCacheObject.isPresent()) { - return temporaryCacheObject.get(); - } - throw new IllegalStateException("Could not parse value: " + string); + return ExpiringCacheObject.fromString(string, valueToObjectMapper) + .orElseGet(() -> TemporaryCacheObject.fromString(string, valueToObjectMapper) + .orElseThrow(() -> 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..bb96531d 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<>(); - - 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); + 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")); } - } - 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()) { - 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; + 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.age).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())) + 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().age)) .forEach(entry -> put(entry.getKey(), entry.getValue().iterator().next())); + } + } catch (SQLException e) { + LOGGER.error("Unable while insert objects in disk cache! (${e.getMessage()})", e); } - } 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 { - 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); + if (errorWhileReadingCacheFile) { + LOGGER.error("Deleting cache file to fix errors"); + connection.close(); + try { + 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"); } - connection = DriverManager.getConnection( - "jdbc:hsqldb:file:" + path + "/diskcache.hsqldb;hsqldb.write_delay=false;shutdown=true", "user", "pass"); + return connection; } - return connection; + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to load jdbcdriver for diskcache"); + } catch (SQLException e) { + throw new RuntimeException(e); } - } 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..e6fdd96c 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,39 +1,36 @@ 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"); - } - if (timerIntervalSeconds != null && timerIntervalSeconds < 1) { + } else if (timerIntervalSeconds != null && timerIntervalSeconds < 1) { throw new IllegalStateException("timerInterval should be a positive number"); - } - if (timeToLiveSeconds != null && timeToLiveSeconds < 1) { + } else if (timeToLiveSeconds != null && timeToLiveSeconds < 1) { throw new IllegalStateException("timeToLive should be a positive number"); - } - if (timeToLiveSeconds == null && timerIntervalSeconds != null) { + } else if (timeToLiveSeconds == null && timerIntervalSeconds != null) { throw new IllegalStateException("timeToLive should be specified when timerInterval is used"); - } - if (timeToLiveSeconds != null && timerIntervalSeconds != null && timeToLiveSeconds < timerIntervalSeconds) { + } else if (timeToLiveSeconds != null && timerIntervalSeconds != null && + timeToLiveSeconds < timerIntervalSeconds) { throw new IllegalStateException("timerInterval should be greater than timeToLive"); } if (timerIntervalSeconds != null) { - createCleanUpThread(timerIntervalSeconds * 1000); + createCleanUpThread(timerIntervalSeconds * 1000L); } - this.timeToLive = timeToLiveSeconds * 1000; + this.timeToLive = timeToLiveSeconds * 1000L; } public static InMemoryCacheBuilderKeyTypeIntf builder() { @@ -79,6 +76,7 @@ private void createCleanUpThread(long timerInterval) { try { Thread.sleep(timerInterval); } catch (InterruptedException ignored) { + //ignore } cleanup(); } @@ -93,9 +91,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/RegexUtils.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/RegexUtils.java new file mode 100644 index 00000000..8c7600ff --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/RegexUtils.java @@ -0,0 +1,132 @@ +package org.lodder.subtools.sublibrary.control; + +import java.util.List; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; +import org.lodder.subtools.sublibrary.control.Tags.Tag; + +/** + * Source + */ +@UtilityClass +public class RegexUtils { + + public static final List DELIMITERS = List.of('.', ' ', '-'); + public static final String DELIMITER = + "[" + Pattern.quote(DELIMITERS.stream().map(String::valueOf).collect(Collectors.joining(""))) + "]"; + + interface RegexStart extends RegexTag { + RegexTag startOfText(); + } + + interface RegexTag extends RegexRegex { + RegexRegex tag(Tag tag); + } + + interface RegexRegex { + RegexNext regex(String regex); + + RegexNext regexOptional(String regex); + + > RegexNext regex(Class enumClass, Function toStringMapper); + } + + interface RegexNext extends RegexTag, RegexEnd { + } + + interface RegexEnd extends RegexBuilderBuild { + RegexBuilderBuild endOfText(); + } + + interface RegexBuilderBuild { + String create(); + + List createWithDelimiter(); + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class Regex implements RegexStart, RegexTag, RegexRegex, RegexNext, RegexEnd, RegexBuilderBuild { + private Tag tag; + private String regex; + private String result = ""; + private boolean finalized = false; + + public static RegexStart builder() { + return new Regex(); + } + + @Override + public Regex startOfText() { + return regex("^"); + } + + @Override + public Regex endOfText() { + return regex("$"); + } + + @Override + public Regex tag(Tag tag) { + if (StringUtils.isNotBlank(this.regex)) { + next(); + } + this.tag = tag; + return this; + } + + @Override + public Regex regex(String regex) { + if (StringUtils.isNotBlank(this.regex)) { + next(); + } + this.regex = regex; + return this; + } + + @Override + public Regex regexOptional(String regex) { + return regex("(" + regex + ")?"); + } + + @Override + public > Regex regex(Class enumClass, Function toStringMapper) { + return regex(enumClass.getEnumConstants().stream().map(toStringMapper).collect(Collectors.joining("|"))); + } + + private void next() { + result += createCurrent(); + tag = null; + regex = null; + } + + private String createCurrent() { + return tag == null ? regex : "(?<${tag.value}>$regex)"; + } + + @Override + public String create() { + if (!finalized) { + next(); + } + finalized = true; + return result; + } + + @Override + public List createWithDelimiter() { + next(); + return List.of( + DELIMITER + result + DELIMITER, + DELIMITER + result + "$", + "^" + result + DELIMITER, + "^" + result + "$"); + } + } + +} 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..6fe2ed79 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 @@ -1,201 +1,455 @@ package org.lodder.subtools.sublibrary.control; +import static org.lodder.subtools.sublibrary.control.RegexUtils.*; +import static org.lodder.subtools.sublibrary.control.Tags.*; + import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.IntStream; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; +import org.apache.commons.lang3.StringUtils; +import org.lodder.subtools.sublibrary.control.Roman.RomanNumeral; +import org.lodder.subtools.sublibrary.control.VideoPatterns.AudioEncoding; +import org.lodder.subtools.sublibrary.control.VideoPatterns.Quality; +import org.lodder.subtools.sublibrary.control.VideoPatterns.RegexPattern; +import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; +import org.lodder.subtools.sublibrary.control.VideoPatterns.VideoEncoding; 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; import org.slf4j.LoggerFactory; +/** + * This class parses a file's name and its metadata to determine the release type (movie or TV series), creating a + * {@link Release} object containing the metadata + */ public class ReleaseParser { - private NamedMatcher namedMatcher; private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseParser.class); + /** + * Parses the provided file path to determine the release type (movie or TV show) and its details. + * + * @param file The file to parse. + * @return A Release object representing the parsed data. + * @throws ReleaseParseException if the file's name cannot be parsed. + */ 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 (NamedPattern np : VideoPatterns.COMPILED_PATTERNS) { - namedMatcher = np.matcher(fileParseName); - if (namedMatcher.find()) { - LOGGER.trace("Parsing match found using file name: {}", fileParseName); - return parsePatternResult(file, fileParseName); - } + if (file.getParent() != null) { + try { + return parsePatternResult(file, file.getParent().fileName.toString(), null); + } catch (Exception e) { + // Continue to next name } } - 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(); - 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): ""; - - if (namedGroups.contains("moviename")) { - String movieName; - if (namedGroups.contains("part")) { - String number = ""; - if (namedGroups.contains("romanepisode")) { - number = namedMatcher.group("romanepisode"); - } else if (namedGroups.contains("partnumber")) { - number = namedMatcher.group("partnumber"); - } - movieName = cleanUnwantedChars(namedMatcher.group("moviename") + " " + namedMatcher.group("part") + " " + number); - } else { - movieName = cleanUnwantedChars(namedMatcher.group("moviename")); + try { + var fileNameAndExtension = file.fileName.splitExtension(); + return parsePatternResult(file, fileNameAndExtension.filename, fileNameAndExtension.extension); + } catch (Exception e) { + throw new ReleaseParseException("Unknown format, can't be parsed: ${file.toAbsolutePath()}"); + } + } + + /** + * Parses the given file's name using a series of regex patterns to extract relevant metadata and constructs the + * corresponding {@link Release} object (either a {@link MovieRelease} or {@link TvRelease}). The method attempts to + * identify and parse key details such as the release type, season, episodes, quality, and release group from the + * filename. + * + * @param file The file path being parsed. + * @param fileParseName The file name (without extension) or the parent directory name. + * @param extension The extension of the file, or null for a parent directory. + * @return A {@link Release} object representing the parsed release information (either a movie or TV show). + * @throws ReleaseParseException If the file name cannot be parsed into a valid release format. + */ + private Release parsePatternResult(Path file, String fileParseName, String extension) throws ReleaseParseException { + ParserResults parserResults = new ParserResults(fileParseName); + + parseReleaseType(parserResults); + String quality = String.join(" ", getQualityKeyWordsAlreadyParsed(parserResults)); + + // When quality parts are found, the filename is split into multiple parts. Consider the last part as the + // release group + String releaseGroup = parserResults.parts.size() > 1 ? parserResults.parts.last : ""; + if (StringUtils.isNotBlank(releaseGroup)) { + parserResults.removeLastPart(); + } + + // Parse the number part, but don't remove it from the remaining parts, as it should be included in the title + // for movies. + parserResults.parseWithoutRemoving(part_number_Regex(NumberType.ARABIC), part_number_Regex(NumberType.ROMAN)); + + // Parse the season + episode numbers (using format SxxExx), and the title + parserResults.parse(seasonSxxExx_name_titleRegex(), name_season_episode_title_Regex(SeasonEpisodeType.SXXEXX)); + + if (parserResults.containsNone(SEASON, EPISODES_TEXT)) { + if (parserResults.containsNone(ARABIC_NUMBER, ROMAN_NUMBER)) { + // If the season and episode numbers are not found, and neither are the Arabic or Roman numbers, try to + // parse the season + episode numbers (using less 'safe' formats 'see' and 's_ee'), and the title + parserResults.parse(name_season_episode_title_Regex(SeasonEpisodeType.X_XX), + name_season_episode_title_Regex(SeasonEpisodeType.XXX), + season_episode_name_title_Regex(SeasonEpisodeType.X_XX), + season_episode_name_title_Regex(SeasonEpisodeType.XXX)); } - return MovieRelease.builder() - .name(movieName) - .file(file) - .year(year) - .description(description) - .releaseGroup(extractReleasegroup(file.getFileName().toString(), true)) - .quality(getQualityKeyword(fileParseName)) - .build(); - } - - if (namedGroups.contains("episodenumber1")) { - LOGGER.trace("parsePatternResult: episodenumber1: {}", namedMatcher.group("episodenumber1")); - // Multiple episodes, have episodenumber1, 2 .... - for (String group : namedGroups) { - Pattern pattern = Pattern.compile("episodenumber(\\d+)"); - Matcher match = pattern.matcher(group); - if (match.matches()) { - episodeNumbers.add(Integer.parseInt(namedMatcher.group(group))); + if (parserResults.containsNone(SEASON, EPISODES_TEXT, EPISODE)) { + // If still no episode numbers are found, try to parse the year + parserResults.parse(yearRegex()); + + // If the year is found, or the source is likely a movie release and not a TV show release, create a + // MovieRelease object + if (parserResults.contains(YEAR) || parserResults.getNamedMatch(SOURCE).stream() + .anyMatch(source -> source.likelyMovieRelease || !source.likelyTvRelease)) { + if (StringUtils.equals(parserResults.parts.first, fileParseName)) { + throw new ReleaseParseException("Could not parse " + fileParseName); + } + return MovieRelease.builder() + .name(cleanUnwantedChars(parserResults.parts.first)) + .file(file) + .year(parserResults.getNamedMatchValue(YEAR)) + .releaseGroup(releaseGroup) + .quality(StringUtils.toRootLowerCase(quality)) + .extension(extension) + .build(); } } - Collections.sort(episodeNumbers); - } else if (namedGroups.contains("episodenumberstart")) { - LOGGER.trace("parsePatternResult: episodenumberstart: {}", namedMatcher.group("episodenumberstart")); - // Multiple episodes, regex specifies start and end number - int start = Integer.parseInt(namedMatcher.group("episodenumberstart")); - int end = Integer.parseInt(namedMatcher.group("episodenumberend")); - if (start > end) { - int temp = start; - start = end; - end = temp; - } - IntStream.rangeClosed(start, end).forEach(episodeNumbers::add); - } else if (namedGroups.contains("episodenumber")) { - LOGGER.trace("parsePatternResult: episodenumber: {}", namedMatcher.group("episodenumber")); - episodeNumbers.add(Integer.parseInt(namedMatcher.group("episodenumber"))); - } else if (namedGroups.contains("year") || namedGroups.contains("month") || namedGroups.contains("day")) { - // TODO: need to implement - } else if (namedGroups.contains("romanepisode") && !namedGroups.contains("year")) { - episodeNumbers.add(Roman.decode(namedMatcher.group("romanepisode"))); - } - - if (namedGroups.contains("seriesname")) { - LOGGER.trace("parsePatternResult: seriesname: {}", namedMatcher.group("seriesname")); - seriesName = cleanUnwantedChars(namedMatcher.group("seriesname")); - if (namedGroups.contains("year")) { - seriesName = seriesName + " " + namedMatcher.group("year"); - } } - if (namedGroups.contains("seasonnumber")) { - LOGGER.trace("parsePatternResult: seasonnumber: {}", namedMatcher.group("seasonnumber")); - seasonNumber = Integer.parseInt(namedMatcher.group("seasonnumber")); - } else if (namedGroups.contains("part") && !namedGroups.contains("year")) { - seasonNumber = 1; - } else if (namedGroups.contains("year") && namedGroups.contains("month") && namedGroups.contains("day")) { - // need to implement - } else if (namedGroups.contains("season_episode")) { - LOGGER.trace("parsePatternResult: season_episode: {}", namedMatcher.group("season_episode")); - if (namedMatcher.group("season_episode").length() == 3) { - episodeNumbers.add(Integer.parseInt(namedMatcher.group("season_episode").substring(1, 3))); - seasonNumber = Integer.parseInt(namedMatcher.group("season_episode").substring(0, 1)); - } else if (namedMatcher.group("season_episode").length() == 4) { - episodeNumbers.add(Integer.parseInt(namedMatcher.group("season_episode").substring(2, 4))); - seasonNumber = Integer.parseInt(namedMatcher.group("season_episode").substring(0, 2)); + // the file is considered a tv show at this point. + + Integer season; + List episodes = new ArrayList<>(); + if (!parserResults.contains(SEASON) || (parserResults.containsNone(EPISODE, EPISODES_TEXT))) { + if (parserResults.containsNone(ARABIC_NUMBER, ROMAN_NUMBER)) { + throw new ReleaseParseException("Could not find a season and/or episodes" + fileParseName); } + // Parse the number parts. They are still present at this point, because they were not removed from the + // remaining parts earlier + parserResults.parse(part_number_Regex(NumberType.ARABIC), part_number_Regex(NumberType.ROMAN)); + // When using the part numbers, assume only one season exists for the TV show + season = 1; + episodes.add(parserResults.getNamedMatchValue(ARABIC_NUMBER, ROMAN_NUMBER)); } else { - // No season number specified, usually for Anime - // TODO: need to implement - throw new ReleaseParseException("Unable to parse the namedmatcher"); + season = parserResults.getNamedMatchValue(SEASON); + episodes.addAll(Objects.requireNonNull(parserResults.getNamedMatchValue(EPISODE, EPISODES_TEXT))); } + + // if no serie name was yet found, use the first remaining part as the serie name + String name = + parserResults.containsNone(NAME) ? parserResults.parts.first : parserResults.getNamedMatchValue(NAME); + // create a new parser to parse a potential year in the title (only at the end of the name) + parserResults.createWithNewText(name) + .parse(Regex.builder() + .startOfText() + .tag(NAME) + .regex(".*") + .regex(DELIMITER) + .regexOptional("\\(") + .regex(yearRegex().create()) + .regexOptional("\\)") + .endOfText()); + name = parserResults.containsNone(NAME) ? parserResults.parts.first : parserResults.getNamedMatchValue(NAME); + + if (StringUtils.equals(name, fileParseName)) { + throw new ReleaseParseException("Could not parse " + fileParseName); + } + if (season == null) { + throw new ReleaseParseException("Could not find a season and/or episodes" + fileParseName); + } + return TvRelease.builder() - .name(seriesName) - .season(seasonNumber) - .episodes(episodeNumbers) - .file(file) - .description(FileUtils.withoutExtension(description)) - .releaseGroup(extractReleasegroup(file.getFileName().toString(), true)) - .special(isSpecialEpisode(seasonNumber, episodeNumbers)) - .quality(getQualityKeyword(fileParseName)) - .build(); + .name(cleanUnwantedChars(name)) + .season(season) + .episodes(episodes) + .file(file) + .title(cleanUnwantedChars(parserResults.getNamedMatchValue(TITLE))) + .releaseGroup(releaseGroup) + .special(isSpecialEpisode(season, episodes)) + .quality(StringUtils.toRootLowerCase(quality)) + .extension(extension) + .build(); } - private String cleanUnwantedChars(String text) { - if (text.contains("cd1")) { - text = text.replace("cd1", " "); + @AllArgsConstructor + enum SeasonEpisodeType { + SXXEXX(Regex.builder() + .regex("s") + .tag(SEASON).regex("\\d{1,2}") + .tag(EPISODES_TEXT).regex("([xe]\\d{1,2})*")), + XXX(Regex.builder() + .tag(SEASON).regex("\\d") + .tag(EPISODES_TEXT).regex("\\d{2}")), + X_XX(Regex.builder() + .regex("\\(") + .tag(SEASON).regex("\\d{1,2}") + .regex("-") + .tag(EPISODE).regex("\\d{2}") + .regex("\\)")); + + @val RegexNext regex; + } + + @AllArgsConstructor + enum NumberType { + ARABIC(Regex.builder() + .tag(ARABIC_NUMBER).regex("\\d{1,2}")), + ROMAN(Regex.builder() + .tag(ROMAN_NUMBER) + .regex("[" + RomanNumeral.values().stream().map(RomanNumeral::name).collect(Collectors.joining()) + "]+")); + + @val RegexNext regex; + } + + /** + * Returns a regular expression pattern for matching years (e.g., 1990, 2023). + * + * @return A RegexNext object representing the year regex. + */ + private static RegexNext yearRegex() { + return Regex.builder().tag(YEAR).regex("19\\d{2}|20\\d{2}"); + } + + private static String name_season_episode_title_Regex(SeasonEpisodeType seasonEpisodeType) { + return Regex.builder() + .startOfText() + .tag(NAME) + .regex(".*") + .regex(DELIMITER) + .regex(seasonEpisodeType.regex.create()) + .regexOptional(DELIMITER + titleRegex().create()) + .endOfText() + .create(); + } + + private static String seasonSxxExx_name_titleRegex() { + return Regex.builder() + .startOfText() + .regexOptional("\\(") + .regex(SeasonEpisodeType.SXXEXX.regex.create()) + .regexOptional("\\)") + .regex(DELIMITER) + .tag(NAME).regex(".*") + .regex(DELIMITER) + .regex("-") + .regex(DELIMITER) + .tag(TITLE).regex(".*") + .endOfText() + .create(); + } + + private static String season_episode_name_title_Regex(SeasonEpisodeType seasonEpisodeType) { + return Regex.builder() + .startOfText() + .regex(seasonEpisodeType.regex.create()) + .tag(NAME).regex(".*") + .regexOptional(DELIMITER + titleRegex().create()) + .endOfText() + .create(); + } + + /** + * Helper method to generate a title regex. + * + * @return A RegexNext object for matching a title. + */ + private static RegexNext titleRegex() { + return Regex.builder().tag(TITLE).regex(".*"); + } + + private static RegexNext part_number_Regex(NumberType numberType) { + return Regex.builder().regex("(pt|part|ep)").regex(DELIMITER).regex(numberType.regex.create()); + } + + + private static void parseReleaseType(ParserResults parseResults) { + parseResults.parse(Regex.builder().tag(QUALITY).regex(Quality.class, Quality::getRegex)); + parseResults.parse(Regex.builder().tag(SOURCE).regex(Source.class, Source::getRegex)); + parseResults.parse(Regex.builder().tag(AUDIO_ENCODING).regex(AudioEncoding.class, AudioEncoding::getRegex)); + parseResults.parse(Regex.builder().tag(VIDEO_ENCODING).regex(VideoEncoding.class, VideoEncoding::getRegex)); + } + + static class ParserResults { + @val List parts = new ArrayList<>(); + private final NamedMatches namedMatches; + + public ParserResults(String text) { + this(text, new NamedMatches()); + } + + private ParserResults(String text, NamedMatches namedMatches) { + parts.add(text); + this.namedMatches = namedMatches; } - if (text.contains("cd2")) { - text = text.replace("cd2", " "); + + public ParserResults createWithNewText(String text) { + return new ParserResults(text, namedMatches); + } + + public boolean parse(RegexBuilderBuild... regexBuilders) { + return parse(true, regexBuilders); + } + + public boolean parseWithoutRemoving(RegexBuilderBuild... regexBuilders) { + return parse(false, regexBuilders); + } + + public boolean parse(String... regexes) { + return parse(true, regexes); + } + + public boolean parseWithoutRemoving(String... regexes) { + return parse(false, regexes); + } + + private boolean parse(boolean removeMatchedParts, RegexBuilderBuild... regexBuilders) { + boolean result = false; + Multimap matches = MultimapBuilder.hashKeys().arrayListValues().build(); + for (RegexBuilderBuild regexBuilder : regexBuilders) { + result &= + regexBuilder.createWithDelimiter().stream().map(v -> parsePrivate(matches, v, removeMatchedParts)) + .toList().contains(true); + } + matches.asMap().forEach(namedMatches::put); + return result; + } + + private boolean parse(boolean removeMatchedParts, String... regexes) { + Multimap matches = MultimapBuilder.hashKeys().arrayListValues().build(); + boolean result = + regexes.stream().map(regex -> parsePrivate(matches, regex, removeMatchedParts)).toList().contains(true); + matches.asMap().forEach(namedMatches::put); + return result; + } + + public boolean parsePrivate(Multimap matches, String regex, boolean removeMatchedParts) { + Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + for (String part : parts) { + Matcher matcher = pattern.matcher(part); + if (matcher.find()) { + Map namedGroupMap = matcher.namedGroups(); + namedGroupMap.entrySet().forEach(entry -> matches.put(entry.key, matcher.group(entry.value))); + String match = matcher.group(); + if (removeMatchedParts) { + List remainingParts = parts.stream() + .flatMap(p -> p.split(Pattern.quote(match)).stream().filter(StringUtils::isNotBlank)) + .toList(); + parts.clear(); + parts.addAll(remainingParts); + } + return true; + } + } + return false; + } + + public void removeLastPart() { + parts.removeLast(); + } + + public boolean contains(Tag tag) { + List namedMatch = getNamedMatch(tag); + return namedMatch != null && !namedMatch.isEmpty(); + } + + public boolean containsNone(Tag... tags) { + return tags.stream().noneMatch(this::contains); + } + + @SafeVarargs + public final T getNamedMatchValue(Tag... tags) { + for (Tag tag : tags) { + List namedMatch = getNamedMatch(tag, tag.mapper); + if (!namedMatch.isEmpty()) { + return namedMatch.first; + } + } + return null; + } + + public List getNamedMatch(Tag tag) { + return getNamedMatch(tag, tag.mapper); + } + + public List getNamedMatch(Tag tag, Function mapper) { + List values = namedMatches.get(tag); + return values == null ? List.of() : values.stream().map(mapper).distinct().toList(); } + } - text = text.replace(".", " "); // remove point bones.01x01 - text = text.replace("_", " "); // remove underscore bones_01x01 - text = text.replace(" -", " "); // remove space dash "ncis - 01x01" - text = text.replace(":", ""); // remove double point "CSI: NY" - text = text.replace("(", ""); // remove ( for castle (2009) - text = text.replace(")", ""); // remove ) for castle (2009) - text = text.replace("'", ""); - if (text.endsWith("-")) { // implemented if for "hawaii five-0" - text = text.replace("-", ""); // remove space dash "altiplano-cd1" + /** + * Cleans unwanted characters from a string (e.g., underscores, extra spaces). + * + * @param text The input text to clean. + * @return The cleaned text. + */ + private String cleanUnwantedChars(String text) { + if (text == null) { + return null; + } + String newText = text; + newText = newText.replace("cd1", " ").replace("cd2", " "); + newText = newText.replace(".", " "); // remove point bones.01x01 + newText = newText.replace("_", " "); // remove underscore bones_01x01 + newText = newText.replace(" -", " "); // remove space dash "ncis - 01x01" + newText = newText.replace(":", ""); // remove double point "CSI: NY" + newText = newText.replace("(", ""); // remove ( for castle (2009) + newText = newText.replace(")", ""); // remove ) for castle (2009) + newText = newText.replace("'", ""); + if (newText.startsWith("- ")) { + newText = newText.substring(2); + } + + if (newText.endsWith("-")) { // implemented if for "hawaii five-0" + newText = newText.replace("-", ""); // remove space dash "altiplano-cd1" } // remove multiple spaces between text Back to the Future[][]Part II - text = text.replaceAll(" +", " "); + newText = newText.replaceAll(" +", " "); - return text.trim(); + return newText.trim(); } public static String getQualityKeyword(String name) { - LOGGER.trace("getQualityKeyword: name: {}", name); - Matcher m = VideoPatterns.QUALITY_KEYWORDS_REGEX_PATTERN.matcher(name.trim().toLowerCase()); - StringBuilder builder = new StringBuilder(); - while (m.find()) { - builder.append(m.group(0).replace(".", " ")).append(" "); - } - LOGGER.trace("getQualityKeyWords: keyswords: {}", builder.toString().trim()); - return builder.toString().trim(); + return String.join(" ", getQualityKeyWords(name)); } public static List getQualityKeyWords(String name) { - LOGGER.trace("getQualityKeyWords: name: {}", name); - Matcher m = VideoPatterns.QUALITY_KEYWORDS_REGEX_PATTERN.matcher(name.trim().toLowerCase()); - List keywords = new ArrayList<>(); - while (m.find()) { - keywords.add(m.group(0)); - } - LOGGER.trace("getQualityKeyWords: keyswords: {}", keywords); - return keywords; + ParserResults parserResults = new ParserResults(name); + parseReleaseType(parserResults); + return getQualityKeyWordsAlreadyParsed(parserResults); + } + + private static List getQualityKeyWordsAlreadyParsed(ParserResults parserResults) { + return Stream.of( + parserResults.getNamedMatch(QUALITY), + parserResults.getNamedMatch(SOURCE), + parserResults.getNamedMatch(AUDIO_ENCODING), + parserResults.getNamedMatch(VIDEO_ENCODING)) + .flatMap(List::stream).map(RegexPattern::getValue).toList(); } - 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]+$"); + releaseGroupPattern = Pattern.compile("-(\\w+)\\.\\w+$"); } else { - releaseGroupPattern = Pattern.compile("-([\\w]+)$"); + releaseGroupPattern = Pattern.compile("-(\\w+)$"); } Matcher matcher = releaseGroupPattern.matcher(fileName); String releaseGroup = ""; @@ -203,14 +457,26 @@ 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; } public static boolean isSpecialEpisode(final int season, final List episodeNumbers) { - if (season == 0) { - return true; + return season == 0 || (episodeNumbers.size() == 1 && episodeNumbers.first == 0); + } + + /** + * Helper class for storing and retrieving named regular expression matches. + */ + private static class NamedMatches { + private final Map> map = new HashMap<>(); + + public void put(String key, Collection values) { + map.put(key, values.stream().distinct().toList()); + } + + public List get(Tag tag) { + return map.get(tag.value); } - return episodeNumbers.size() == 1 && episodeNumbers.get(0) == 0; } -} +} \ No newline at end of file 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..7fa40a20 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,21 +1,30 @@ package org.lodder.subtools.sublibrary.control; +import lombok.AllArgsConstructor; +import lombok.experimental.UtilityClass; +import manifold.ext.props.rt.api.val; + /** - * Source + * Source */ - +@UtilityClass public class Roman { + + @AllArgsConstructor + public enum RomanNumeral { + I(1), + V(5), + X(10), + L(50), + C(100), + D(500), + M(1000); + + @val int value; + } + private static int decodeSingle(char letter) { - return switch (letter) { - case 'M' -> 1000; - case 'D' -> 500; - case 'C' -> 100; - case 'L' -> 50; - case 'X' -> 10; - case 'V' -> 5; - case 'I' -> 1; - default -> 0; - }; + return RomanNumeral.valueOf(String.valueOf(letter).toUpperCase()).value; } public static int decode(String roman) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Tags.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Tags.java new file mode 100644 index 00000000..de317230 --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/control/Tags.java @@ -0,0 +1,46 @@ +package org.lodder.subtools.sublibrary.control; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.AllArgsConstructor; +import lombok.experimental.UtilityClass; +import manifold.ext.props.rt.api.val; +import org.lodder.subtools.sublibrary.control.VideoPatterns.AudioEncoding; +import org.lodder.subtools.sublibrary.control.VideoPatterns.Quality; +import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; +import org.lodder.subtools.sublibrary.control.VideoPatterns.VideoEncoding; + +@UtilityClass +public class Tags { + + @AllArgsConstructor + public static class Tag { + @val String value; + @val Function mapper; + } + + public static Tag NAME = new Tag<>("name", Function.identity()); + public static Tag TITLE = new Tag<>("title", Function.identity()); + public static Tag SEASON = new Tag<>("season", Integer::parseInt); + public static Tag> EPISODE = new Tag<>("episode", v -> List.of(Integer.parseInt(v))); + public static Tag> EPISODES_TEXT = + new Tag<>("episodestext", v -> { + List episodes = new ArrayList<>(); + Matcher matcher = Pattern.compile("\\d+").matcher(v); + while (matcher.find()) { + episodes.add(Integer.parseInt(matcher.group())); + } + return episodes; + }); + public static Tag ARABIC_NUMBER = new Tag<>("arabicnbr", Integer::parseInt); + public static Tag ROMAN_NUMBER = new Tag<>("romannbr", Roman::decode); + public static Tag YEAR = new Tag<>("year", Integer::parseInt); + public static Tag AUDIO_ENCODING = new Tag<>("audioenc", AudioEncoding::fromValue); + public static Tag VIDEO_ENCODING = new Tag<>("videoenc", VideoEncoding::fromValue); + public static Tag QUALITY = new Tag<>("quality", Quality::fromValue); + public static Tag SOURCE = new Tag<>("source", Source::fromValue); +} 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..0eb61203 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,126 +1,141 @@ package org.lodder.subtools.sublibrary.control; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; + import java.util.Set; import java.util.regex.Pattern; 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.override; +import manifold.ext.props.rt.api.val; @UtilityClass -@ExtensionMethod({ Arrays.class }) public class VideoPatterns { - public interface VideoPatternEnumIntf { - } - - @Getter - @RequiredArgsConstructor - public enum Quality implements VideoPatternEnumIntf { - Q1080P("1080p"), - Q1080I("1080i"), - Q720P("720p"), - Q480P("480p"); - - final String value; + public interface RegexPattern { + @val Pattern pattern; + @val String value; - public static Stream getValuesStream() { - return Quality.values().stream().map(Quality::getValue); + default String getRegex() { + return pattern.pattern(); } } - @Getter - public enum VideoEncoding implements VideoPatternEnumIntf { - X264("x264", "h264"), - X265("x265", "h265"); - - final String[] values; - - VideoEncoding(String... values) { - this.values = values; + public enum Quality implements RegexPattern { + Q8K("8k", "8k"), + Q4K("4k", "4k"), + Q1440P("1440p", "1440p"), + Q1080P("1080p", "1080p"), + Q1080I("1080i", "1080i"), + Q720P("720p", "720p"), + Q480P("480p", "480p"); + + @val @override String value; + @val @override Pattern pattern; + + Quality(String value, String quality) { + this.value = value; + this.pattern = Pattern.compile(quality, Pattern.CASE_INSENSITIVE); } - public static Stream getValuesStream() { - return VideoEncoding.values().stream().map(VideoEncoding::getValues).flatMap(Arrays::stream); + public static Quality fromValue(String value) { + return value == null ? null : Quality.values().stream() + .filter(v -> v.pattern.matcher(value).find()) + .findAny() + .orElse(null); } } - @Getter - public enum AudioEncoding implements VideoPatternEnumIntf { - DD5_1("dd5.1", "dd5-1"); + @AllArgsConstructor + public enum VideoEncoding implements RegexPattern { + X264("x264", "[xh]264"), + X265("x265", "[xh]265|hevc"); - final String[] values; + @val @override Pattern pattern; + @val @override String value; - AudioEncoding(String... values) { - this.values = values; + VideoEncoding(String value, String quality) { + this.value = value; + pattern = Pattern.compile(quality, Pattern.CASE_INSENSITIVE); } - public static Stream getValuesStream() { - return AudioEncoding.values().stream().map(AudioEncoding::getValues).flatMap(Arrays::stream); + public static VideoEncoding fromValue(String value) { + return value == null ? null : VideoEncoding.values().stream() + .filter(v -> v.pattern.matcher(value).find()) + .findAny() + .orElse(null); } } - @Getter - public enum Source implements VideoPatternEnumIntf { - HDTV(false, "hdtv"), - DVDRIP(false, "dvdrip"), - BLURAY(false, "bluray"), - BDRIP(false, "bdrip"), - BRRIP(false, "brrip"), - XVID(false, "xvid"), - PDTV(false, "pdtv"), - DIVX(false, "divx"), - WEBRIP(false, "webrip"), - RERIP(false, "rerip"), - WEBDL(false, "webdl", "web-dl", "web.dl"), - TS(true, "ts"), - DVD_SCREENER(true, "dvdscreener"), - R5(true, "r5"), - CAM(true, "cam"); - - 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))); - } + @AllArgsConstructor + public enum AudioEncoding implements RegexPattern { + DD5_1("dd5.1", "dd5[-.]1"); + + @val @override Pattern pattern; + @val @override String value; - final boolean manyDifferentSources; - final String[] values; + AudioEncoding(String value, String quality) { + this.value = value; + pattern = Pattern.compile(quality, Pattern.CASE_INSENSITIVE); + } - Source(boolean manyDifferentSources, String... values) { - this.manyDifferentSources = manyDifferentSources; - this.values = values; + public static AudioEncoding fromValue(String value) { + return value == null ? null : AudioEncoding.values().stream() + .filter(v -> v.pattern.matcher(value).find()) + .findAny() + .orElse(null); } + } - public static Stream getValuesStream() { - return Source.values().stream().map(Source::getValues).flatMap(Arrays::stream); + @AllArgsConstructor + public enum Source implements RegexPattern { + HDTV("hdtv", "hdtv", true, false), + DVDRIP("dvdrip", "dvdrip", false, false), + BLURAY("bluray", "bluray", false, false), + BDRIP("bdrip", "bdrip", false, false), + BRRIP("brrip", "brrip", false, false), + XVID("xvid", "xvid", false, false), + PDTV("pdtv", "pdtv", true, false), + DIVX("divx", "divx", false, false), + WEBRIP("webrip", "webrip", false, false), + RERIP("rerip", "rerip", false, false), + WEBDL("web-dl", "web[-.]?dl", false, false), + TS("ts", "ts", false, true), + DVD_SCREENER("dvdscreener", "dvdscreener", false, true), + R5("r5", "r5", false, true), + CAM("cam", "cam", false, true); + + @val @override Pattern pattern; + @val @override String value; + @val boolean likelyTvRelease; + @val boolean likelyMovieRelease; + + Source(String value, String quality, boolean likelyTvRelease, boolean likelyMovieRelease) { + this.value = value; + this.pattern = Pattern.compile(quality, Pattern.CASE_INSENSITIVE); + this.likelyTvRelease = likelyTvRelease; + this.likelyMovieRelease = likelyMovieRelease; } public static Source fromValue(String value) { - return VALUE_MAP.get(value); + return value == null ? null : Source.values().stream() + .filter(v -> v.pattern.matcher(value).find()) + .findAny() + .orElse(null); } public boolean isTypeForValue(String value) { - return Arrays.stream(getValues()).map(String::toLowerCase).anyMatch(value::equals); + return fromValue(value) != null; } @Override public String toString() { - return values[0]; + return regex; } } - @Getter - @RequiredArgsConstructor + @AllArgsConstructor public enum VideoExtensions { MKV("mkv"), MP4("mp4"), @@ -129,65 +144,9 @@ 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", - "1080p", "ts", "dvdscreener", "r5", "bdrip", "brrip", "720p", "xvid", "cam", "480p", "x264", "x265", - "1080i", "pdtv", "divx", "webrip", "h264", "h265", "rerip", "webdl"); - - private static final Set QUALITY_KEYWORDS_REGEX_SET = Set.of("web[ .-]dl", "dd5[ .]1"); - - public static final Set EXTENSIONS = Set.of("mkv", "mp4", "avi", "wmv", "ts", "m4v"); - - // order is important!!!!!! - 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:&()!.,_-]+)", - // 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:&()!.,_-]+)", - // 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:&()!.,_-]+)", - // 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:&()!.,_]+)", - // 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:&()!.,_-]+)", - // example hawaii.five-0.2010.410.hdtv-lol.mp4 - "(?['\\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:&()!.,_-]+)", - // format episode.0101.title - // format episode.101.title - // exclude format movietitle.720p - "(?['\\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:&()!.,_-]+)", - // 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(); - - 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(); - - private static final String QUALITY_KEYWORDS_REGEX = - 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 Set EXTENSIONS = + VideoExtensions.values().stream().map(VideoExtensions::getValue).collect(Collectors.toSet()); } 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..fef7afc6 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 @@ -1,12 +1,13 @@ package org.lodder.subtools.sublibrary.data.imdb; +import static org.lodder.subtools.multisubdownloader.Messages.*; + import java.util.Collection; import java.util.Comparator; import java.util.Optional; import java.util.OptionalInt; -import org.apache.commons.lang3.StringUtils; -import org.lodder.subtools.multisubdownloader.Messages; +import com.pivovarit.function.ThrowingBiFunction; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; @@ -15,19 +16,14 @@ 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); + private static final String PROVIDER_NAME = "IMDB"; private static ImdbAdapter instance; private final Manager manager; private final UserInteractionHandler userInteractionHandler; @@ -41,48 +37,45 @@ private ImdbAdapter(Manager manager, UserInteractionHandler userInteractionHandl try { return new ImdbApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(PROVIDER_NAME, e); } }); this.imdbSearchIdApi = new LazySupplier<>(() -> { try { return new ImdbSearchIdApi(manager); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(PROVIDER_NAME, e); } }); } - public String getProviderName() { - return "IMDB"; - } - public Optional getMovieDetails(int imdbId) { return manager.valueBuilder() - .cacheType(CacheType.DISK) - .key("%s-MovieDetails:%s".formatted(getProviderName(), 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); - return Optional.empty(); - } - }).getOptional(); + .cacheType(CacheType.DISK) + .key("%s-MovieDetails:%s".formatted(PROVIDER_NAME, imdbId)) + .optionalSupplier(() -> { + try { + return imdbApi.get().getMovieDetails(imdbId); + } catch (ImdbException e) { + LOGGER.error("API %s getMovieDetails for id [%s] (%s)".formatted(PROVIDER_NAME, imdbId, + e.getMessage()), e); + return Optional.empty(); + } + }).getOptional(); } public OptionalInt getImdbId(String title, Integer year) { try { return manager.valueBuilder() - .cacheType(CacheType.DISK) - .key("%s-id-%s-%s".formatted(getProviderName(), title, year)) - .optionalIntSupplier(() -> getImdbIdOnImdb(title, year) - .orElseMap(() -> getImdbIdOnGoogle(title, year)) - .orElseMap(() -> getImdbIdOnYahoo(title, year)) - .orElseMap(() -> promptUserToEnterImdbId(title, year))) - .storeTempNullValue().getOptionalInt(); + .cacheType(CacheType.DISK) + .key("%s-id-%s-%s".formatted(PROVIDER_NAME, 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(PROVIDER_NAME, title, e.getMessage()), e); return OptionalInt.empty(); } } @@ -100,38 +93,40 @@ private OptionalInt getImdbIdOnYahoo(String title, Integer year) { } private OptionalInt getImdbIdCommon(String title, Integer year, - ThrowingBiFunction, ImdbSearchIdException> providerSerieIdSupplier) { + ThrowingBiFunction, ImdbSearchIdException> providerSerieIdSupplier) { Collection providerSerieIds; 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(PROVIDER_NAME, 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)) - .toList(), - Messages.getString("Prompter.SelectImdbMatchForSerie").formatted(title), - getProviderName(), - ProviderSerieId::getName) - .mapToInt(providerSerieId -> Integer.parseInt(providerSerieId.getId())); + .selectFromList( + providerSerieIds.stream().sorted(Comparator + .comparing((ProviderSerieId providerSerieId) -> providerSerieId.name.replaceAll( + "[^A-Za-z]", "") + .equalsIgnoreCase(formattedTitle), Comparator.reverseOrder()) + .thenComparing(ProviderSerieId::getName)) + .toList(), + getText("Prompter.SelectImdbMatchForSerie", title), + PROVIDER_NAME, + ProviderSerieId::getName) + .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.enterNumber(PROVIDER_NAME, + getText("Prompter.EnterImdbMatchForSerie", title), getText("Prompter.ValueIsNotValid")); } - 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..53f29bcc 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']").text(); + int year = Integer.parseInt( + element.selectFirstByCss("span.nobr").text().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..30925681 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").text() + " " + e.text(), + e -> e.selectFirst("a").attr("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); + e -> e.selectFirstByTag("span").text().replace(" - IMDb", ""); Function toHrefMapper = e -> e.attr("href"); - return getImdbIdCommon(searchResults, title, year, toStringMapper, toHrefMapper); + 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..34865012 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))); + .getElementsByTagName("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..6bff2276 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,8 @@ package org.lodder.subtools.sublibrary.data.tvdb; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import javax.swing.*; import java.io.Serializable; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -9,9 +12,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; import org.lodder.subtools.sublibrary.Manager.ValueBuilderIsPresentIntf; @@ -23,20 +23,14 @@ 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); + private static final String PROVIDER_NAME = "TVDB"; private static TheTvdbAdapter instance; private final Manager manager; private final UserInteractionHandler userInteractionHandler; @@ -49,15 +43,11 @@ private TheTvdbAdapter(Manager manager, UserInteractionHandler userInteractionHa try { return new TheTvdbApi(manager, "A1720D2DDFDCE82D"); } catch (Exception e) { - throw new SubtitlesProviderInitException(getProviderName(), e); + throw new SubtitlesProviderInitException(PROVIDER_NAME, e); } }); } - public String getProviderName() { - return "TVDB"; - } - private TheTvdbApi getApi() { return jtvapi.get(); } @@ -65,8 +55,8 @@ private TheTvdbApi getApi() { 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)); + .cacheType(CacheType.DISK) + .key("$PROVIDER_NAME-tvdbSerie-$encodedSerieName"); if (valueBuilder.isPresent() && (!valueBuilder.isTemporaryObject() || !valueBuilder.isExpiredTemporary())) { return valueBuilder.returnType(TheTvdbSerie.class).getOptional(); } @@ -80,59 +70,64 @@ 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]", "")), - Comparator.reverseOrder()) - .thenComparing(TheTvdbSerie::getFirstAired, Comparator.reverseOrder()); + .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))); + tvdbSerie = userInteractionHandler.selectFromList(serieIds.stream().sorted(comparator).toList(), + getText("Prompter.SelectTvdbMatchForSerie", serieName), + PROVIDER_NAME, 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(); } } if (tvdbSerie.isEmpty()) { valueBuilder.optionalValue(tvdbSerie) - .storeTempNullValue() - .timeToLive(OptionalExtension.map(valueBuilder.getTemporaryTimeToLive(), v -> v * 2) - .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) - .storeAsTempValue(); + .storeTempNullValue() + .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()))) - .storeTempNullValue() - .store(); + .cacheType(CacheType.DISK) + .key("$PROVIDER_NAME-serieId-$encodedSerieName") + .optionalValue(tvdbSerie.map(tvdbS -> new SerieMapping(serieName, tvdbS.id, tvdbS.serieName))) + .storeTempNullValue() + .store(); } return tvdbSerie; } 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)) - .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); - return Optional.empty(); - } - }).storeTempNullValue().getOptional(); + .cacheType(CacheType.DISK) + .key("%s-episode-%s-%s-%s".formatted(PROVIDER_NAME, tvdbId, season, episode)) + .optionalSupplier(() -> { + try { + return getApi().getEpisode(tvdbId, season, episode, Language.ENGLISH); + } catch (TheTvdbException e) { + LOGGER.error( + "API $PROVIDER_NAME 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..f44f5fd9 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() + .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); }); } } 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..fb654720 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; @@ -27,11 +23,11 @@ public interface MovieReleaseBuilderOther { MovieReleaseBuilderOther quality(String quality); - MovieReleaseBuilderOther description(String description); - MovieReleaseBuilderOther releaseGroup(String releaseGroup); MovieReleaseBuilderOther year(Integer year); + + MovieReleaseBuilderOther extension(String extension); MovieRelease build(); } @@ -48,40 +44,48 @@ public static class MovieReleaseBuilder implements MovieReleaseBuilderOther, Mov private String quality; private Path file; - private String description; private String releaseGroup; + private String extension; @Override public MovieRelease build() { - return new MovieRelease(file, description, releaseGroup, quality, name, year == null ? 0 : year); + return new MovieRelease(file, releaseGroup, quality, extension, name, year == null ? 0 : year); } } - private MovieRelease(Path file, String description, String releaseGroup, String quality, String name, int year) { - super(VideoType.MOVIE, file, description, releaseGroup, quality); + private MovieRelease(Path file, String releaseGroup, String quality, String extension, String name, int year) { + super(VideoType.MOVIE, file, releaseGroup, quality, extension); this.name = name; this.year = year; } 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..400b7d33 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 @@ -1,66 +1,64 @@ package org.lodder.subtools.sublibrary.model; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashSet; 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 releaseGroup; + @val String extension; public void addMatchingSub(Subtitle sub) { - matchingSubs.add(sub); + matchingSubsSet.add(sub); } public List getMatchingSubs() { - return new ArrayList<>(matchingSubs); + return List.copyOf(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; - this.description = description; + protected Release(VideoType videoType, Path filePath, String releaseGroup, String quality, String extension) { + this.videoType = videoType; + this.filePath = filePath; this.releaseGroup = releaseGroup; this.quality = quality; + this.extension = extension; } 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 extension; } public boolean hasExtension(String extension) { - return StringUtils.endsWith(getFileName(), "." + extension); + return StringUtils.isNotBlank(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..9156cdc9 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 { @@ -60,8 +57,6 @@ public interface TvReleaseBuilderOther { TvReleaseBuilderOther quality(String quality); - TvReleaseBuilderOther description(String description); - TvReleaseBuilderOther special(boolean special); TvReleaseBuilderOther releaseGroup(String releaseGroup); @@ -72,6 +67,8 @@ public interface TvReleaseBuilderOther { TvReleaseBuilderOther originalName(String originalName); + TvReleaseBuilderOther extension(String extension); + TvRelease build(); } @@ -82,15 +79,16 @@ 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; private List episodes; private boolean special; private String quality; + private String extension; private Path file; - private String description; private String releaseGroup; private String customName; private String originalName; @@ -109,14 +107,15 @@ 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, releaseGroup, quality, extension, 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) { - super(VideoType.EPISODE, file, description, releaseGroup, quality); + private TvRelease(Path file, String releaseGroup, String quality, String extension, String name, + String originalName, String customName, String title, int season, List episodeNumbers, + boolean special) { + super(VideoType.EPISODE, file, releaseGroup, quality, extension); this.name = name; this.title = title; this.season = season; @@ -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..8bbac86d 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 @@ -2,20 +2,24 @@ import java.util.Collection; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Function; import java.util.function.Predicate; +import manifold.ext.props.rt.api.val; +import org.apache.commons.lang3.StringUtils; 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); @@ -27,6 +31,10 @@ default Optional enter(String title, String message) { Optional enter(String title, String message, String errorMessage, Predicate validator); + default OptionalInt enterNumber(String title, String message, String errorMessage) { + return enter(title, message, errorMessage, StringUtils::isNumeric).mapToInt(Integer::parseInt); + } + void showMessage(String message, String title, MessageSeverity messageSeverity); enum MessageSeverity { 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..3d9a59b7 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,10 @@ 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.DefaultInputHandler; +import org.codehaus.plexus.components.interactivity.DefaultOutputHandler; import org.codehaus.plexus.components.interactivity.DefaultPrompter; import org.codehaus.plexus.components.interactivity.Prompter; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; @@ -12,15 +16,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(new DefaultOutputHandler(), new DefaultInputHandler()); + @val @override UserInteractionSettingsIntf settings; + + public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { + this.settings = settings; + } @Override public Optional selectFromList(Collection options, String message, String title) { @@ -28,8 +31,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 +46,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 +58,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..69d80a92 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 @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.util; -import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -10,6 +9,7 @@ import java.nio.file.attribute.BasicFileAttributes; import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.Nullable; @RequiredArgsConstructor public class CopyDirVisitor extends SimpleFileVisitor { @@ -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/DeleteDirVisitor.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/DeleteDirVisitor.java index b5678ea6..162e8dc2 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/DeleteDirVisitor.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/DeleteDirVisitor.java @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.util; -import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -8,6 +7,8 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import org.jspecify.annotations.Nullable; + public class DeleteDirVisitor extends SimpleFileVisitor { @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..6decb8e2 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 @@ -1,14 +1,13 @@ package org.lodder.subtools.sublibrary.util; -import java.util.List; import java.util.Map; import java.util.regex.MatchResult; -public interface NamedMatchResult extends MatchResult { +import manifold.ext.props.rt.api.val; - List orderedGroups(); +public interface NamedMatchResult extends MatchResult { - Map namedGroups(); + @val Map namedGroups; String group(String groupName); 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 deleted file mode 100755 index ca09809b..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedMatcher.java +++ /dev/null @@ -1,211 +0,0 @@ -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; - -public class NamedMatcher implements NamedMatchResult { - - private Matcher matcher; - private NamedPattern parentPattern; - - NamedMatcher() { - } - - NamedMatcher(NamedPattern parentPattern, MatchResult matcher) { - this.parentPattern = parentPattern; - this.matcher = (Matcher) matcher; - } - - NamedMatcher(NamedPattern parentPattern, CharSequence input) { - this.parentPattern = parentPattern; - this.matcher = parentPattern.pattern().matcher(input); - } - - public Pattern standardPattern() { - return matcher.pattern(); - } - - public NamedPattern namedPattern() { - return parentPattern; - } - - public NamedMatcher usePattern(NamedPattern newPattern) { - this.parentPattern = newPattern; - matcher.usePattern(newPattern.pattern()); - return this; - } - - public NamedMatcher reset() { - matcher.reset(); - return this; - } - - public NamedMatcher reset(CharSequence input) { - matcher.reset(input); - return this; - } - - public boolean matches() { - return matcher.matches(); - } - - public NamedMatchResult toMatchResult() { - return new NamedMatcher(this.parentPattern, matcher.toMatchResult()); - } - - public boolean find() { - return matcher.find(); - } - - public boolean find(int start) { - return matcher.find(start); - } - - public boolean lookingAt() { - return matcher.lookingAt(); - } - - public NamedMatcher appendReplacement(StringBuffer sb, String replacement) { - matcher.appendReplacement(sb, replacement); - return this; - } - - public StringBuffer appendTail(StringBuffer sb) { - return matcher.appendTail(sb); - } - - @Override - public String group() { - return matcher.group(); - } - - @Override - public String group(int group) { - return matcher.group(group); - } - - @Override - 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)); - } - - @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); - } - - private int groupIndex(String groupName) { - return parentPattern.groupNames().indexOf(groupName) + 1; - } - - @Override - public int start() { - return matcher.start(); - } - - @Override - public int start(int group) { - return matcher.start(group); - } - - @Override - public int start(String groupName) { - return start(groupIndex(groupName)); - } - - @Override - public int end() { - return matcher.end(); - } - - @Override - public int end(int group) { - return matcher.end(group); - } - - @Override - public int end(String groupName) { - return end(groupIndex(groupName)); - } - - public NamedMatcher region(int start, int end) { - matcher.region(start, end); - return this; - } - - public int regionEnd() { - return matcher.regionEnd(); - } - - public int regionStart() { - return matcher.regionStart(); - } - - public boolean hitEnd() { - return matcher.hitEnd(); - } - - public boolean requireEnd() { - return matcher.requireEnd(); - } - - public boolean hasAnchoringBounds() { - return matcher.hasAnchoringBounds(); - } - - public boolean hasTransparentBounds() { - return matcher.hasTransparentBounds(); - } - - public String replaceAll(String replacement) { - return matcher.replaceAll(replacement); - } - - public String replaceFirst(String replacement) { - return matcher.replaceFirst(replacement); - } - - public NamedMatcher useAnchoringBounds(boolean b) { - matcher.useAnchoringBounds(b); - return this; - } - - public NamedMatcher useTransparentBounds(boolean b) { - matcher.useTransparentBounds(b); - return this; - } - - public boolean equals(Matcher obj) { - return matcher.equals(obj); - } - - @Override - public int hashCode() { - return matcher.hashCode(); - } - - @Override - public String toString() { - return matcher.toString(); - } - -} 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 deleted file mode 100755 index c0d89bc7..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/NamedPattern.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.lodder.subtools.sublibrary.util; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -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; - - public static NamedPattern compile(String regex) { - return new NamedPattern(regex, 0); - } - - public static NamedPattern compile(String regex, int flags) { - return new NamedPattern(regex, flags); - } - - private NamedPattern(String regex, int i) { - namedPattern = regex; - pattern = buildStandardPattern(regex); - groupNames = extractGroupNames(regex); - } - - public int flags() { - return pattern.flags(); - } - - 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); - } - - public String[] split(CharSequence input) { - return pattern.split(input); - } - - @Override - public String toString() { - return namedPattern; - } - - static List extractGroupNames(String namedPattern) { - List groupNames = new ArrayList<>(); - Matcher matcher = NAMED_GROUP_PATTERN.matcher(namedPattern); - while (matcher.find()) { - groupNames.add(matcher.group(1)); - } - return groupNames; - } - - static Pattern buildStandardPattern(String namedPattern) { - return Pattern.compile(NAMED_GROUP_PATTERN.matcher(namedPattern).replaceAll("(")); - } - -} 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/CookieManager.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java index c3e7c760..b1e22b7c 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java @@ -12,12 +12,11 @@ import java.util.StringTokenizer; /** - * CookieManager is a simple utility for handling cookies when working with java.net.URL and - * java.net.URLConnection objects. + * CookieManager is a simple utility for handling cookies when working with java.net.URL and java.net.URLConnection + * objects. *

          *

          - * Cookiemanager cm = new CookieManager(); URL url = new - * URL("http://www.hccp.org/test/cookieTest.jsp"); + * Cookiemanager cm = new CookieManager(); URL url = new URL("http://www.hccp.org/test/cookieTest.jsp"); *

          * . . . *

          @@ -50,14 +49,11 @@ public CookieManager() { } /** - * Retrieves and stores cookies returned by the host on the other side of the the open - * java.net.URLConnection. + * Retrieves and stores cookies returned by the host on the other side of the open java.net.URLConnection. *

          - * The connection MUST have been opened using the connect() method or a IOException will be - * thrown. + * The connection MUST have been opened using the connect() method or a IOException will be thrown. * - * @param conn - * a java.net.URLConnection - must be open, or IOException will be thrown + * @param conn a java.net.URLConnection - must be open, or IOException will be thrown */ public void storeCookies(URLConnection conn) { @@ -100,7 +96,7 @@ public void storeCookies(URLConnection conn) { while (st.hasMoreTokens()) { String token = st.nextToken(); cookie.put(token.substring(0, token.indexOf(NAME_VALUE_SEPARATOR)).toLowerCase(), - token.substring(token.indexOf(NAME_VALUE_SEPARATOR) + 1)); + token.substring(token.indexOf(NAME_VALUE_SEPARATOR) + 1)); } } } @@ -110,25 +106,22 @@ public void storeCookies(String domain, Map cookieMap) { if (cookieMap == null || cookieMap.isEmpty()) { return; } - Map> domainStore = store.computeIfAbsent(domain, key -> new HashMap<>()); + Map> domainStore = store.computeIfAbsent(domain, _ -> new HashMap<>()); cookieMap.forEach((k, v) -> domainStore.put(k, Map.of(k, v))); } /** - * Prior to opening a URLConnection, calling this method will set all unexpired cookies that match - * the path or sub paths for this underlying URL + * Prior to opening a URLConnection, calling this method will set all unexpired cookies that match the path or sub + * paths for this underlying URL *

          * The connection MUST NOT have been opened method or an IOException will be thrown. * - * @param conn - * a java.net.URLConnection - must NOT be open, or IOException will be thrown - * @throws java.io.IOException - * Thrown if conn has already been opened. + * @param conn a java.net.URLConnection - must NOT be open, or IOException will be thrown + * @throws java.io.IOException Thrown if conn has already been opened. */ public void setCookies(URLConnection conn) throws IOException { - // let's determine the domain and path to retrieve the appropriate - // cookies + // let's determine the domain and path to retrieve the appropriate cookies URL url = conn.getURL(); String domain = getDomainFromHost(url.getHost()); String path = url.getPath(); @@ -144,8 +137,8 @@ public void setCookies(URLConnection conn) throws IOException { // check cookie to ensure path matches and cookie is not expired // if all is cool, add cookie to header string if (comparePaths(cookie.get(PATH), path) - && isNotExpired(cookie.get(EXPIRES))) { - if (cookieStringBuffer.length() > 0) { + && isNotExpired(cookie.get(EXPIRES))) { + if (!cookieStringBuffer.isEmpty()) { cookieStringBuffer.append(SET_COOKIE_SEPARATOR); } @@ -158,9 +151,8 @@ && isNotExpired(cookie.get(EXPIRES))) { try { conn.setRequestProperty(COOKIE, cookieStringBuffer.toString()); } catch (java.lang.IllegalStateException ise) { - throw new IOException( - "Illegal State! Cookies cannot be set on a URLConnection that is already connected. " - + "Only call setCookies(java.net.URLConnection) AFTER calling java.net.URLConnection.connect()."); + throw new IOException("Illegal State! Cookies cannot be set on a URLConnection that is already connected. " + + "Only call setCookies(java.net.URLConnection) AFTER calling java.net.URLConnection.connect()."); } } @@ -174,7 +166,8 @@ private String getDomainFromHost(String host) { private boolean isNotExpired(String cookieExpires) { try { - return cookieExpires == null || LocalDateTime.now().isBefore(LocalDateTime.parse(cookieExpires, DATE_FORMATTER)); + return cookieExpires == null || + LocalDateTime.now().isBefore(LocalDateTime.parse(cookieExpires, DATE_FORMATTER)); } catch (DateTimeParseException e) { e.printStackTrace(); return false; @@ -182,7 +175,8 @@ private boolean isNotExpired(String cookieExpires) { } private boolean comparePaths(String cookiePath, String targetPath) { - return cookiePath == null || "/".equals(cookiePath) || targetPath.regionMatches(0, cookiePath, 0, cookiePath.length()); + return cookiePath == null || "/".equals(cookiePath) || + targetPath.regionMatches(0, cookiePath, 0, cookiePath.length()); } /** 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..7286cd88 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 @@ -10,26 +10,25 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLConnection; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Map; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; +import extensions.java.io.InputStream.InputStreamExt; +import extensions.java.nio.file.Path.PathExt; +import jakarta.ws.rs.core.HttpHeaders; +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.jsoup.helper.HttpConnection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import lombok.RequiredArgsConstructor; - @RequiredArgsConstructor public class HttpClient { @@ -42,21 +41,22 @@ public HttpClient() { } public String doGet(URL url, String userAgent) throws IOException, HttpClientException { - URLConnection conn = url.openConnection(); - cookieManager.setCookies(conn); - - if (userAgent != null && userAgent.length() > 0) { - conn.setRequestProperty("user-agent", userAgent); - } - - int respCode = ((HttpURLConnection) conn).getResponseCode(); - - if (respCode == 200) { - String result = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8); - ((HttpURLConnection) conn).disconnect(); - return result; + HttpURLConnection conn = null; + try { + conn = (HttpURLConnection) url.openConnection(); + cookieManager.setCookies(conn); + if (StringUtils.isNotBlank(userAgent)) { + conn.setRequestProperty(HttpHeaders.USER_AGENT, userAgent); + } + if (conn.responseCode == 200) { + return InputStreamExt.asString(conn.getInputStream(), StandardCharsets.UTF_8); + } + throw new HttpClientException(conn); + } finally { + if (conn != null) { + conn.disconnect(); + } } - throw new HttpClientException((HttpURLConnection) conn); } public String doPost(URL url, String userAgent, Map data) throws HttpClientException { @@ -64,17 +64,18 @@ public String doPost(URL url, String userAgent, Map data) throws try { String urlParameters = data.entrySet().stream() - .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)) - .collect(Collectors.joining("&")); + .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)) + .collect(Collectors.joining("&")); conn = (HttpURLConnection) url.openConnection(); cookieManager.setCookies(conn); conn.setRequestMethod("POST"); if (StringUtils.isNotBlank(userAgent)) { - conn.setRequestProperty("user-agent", userAgent); + conn.setRequestProperty(HttpHeaders.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(HttpHeaders.CONTENT_TYPE, HttpConnection.FORM_URL_ENCODED); + conn.setRequestProperty(HttpHeaders.CONTENT_LENGTH, + String.valueOf(urlParameters.getBytes(StandardCharsets.UTF_8).length)); conn.setUseCaches(false); conn.setDoInput(true); conn.setDoOutput(true); @@ -87,14 +88,10 @@ public String doPost(URL url, String userAgent, Map data) throws cookieManager.storeCookies(conn); - if ((conn.getResponseCode() == 302) && isUrl(conn.getHeaderField("Location"))) { - return doGet(new URI(conn.getHeaderField("Location")).toURL(), userAgent); + if (conn.responseCode == 302 && isUrl(conn.getHeaderField(HttpHeaders.LOCATION))) { + return doGet(new URI(conn.getHeaderField(HttpHeaders.LOCATION)).toURL(), userAgent); } - - String result = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8); - conn.disconnect(); - return result; - + return InputStreamExt.asString(conn.getInputStream(), StandardCharsets.UTF_8); } catch (IOException | URISyntaxException e) { throw new HttpClientException(e, conn); } finally { @@ -108,22 +105,23 @@ 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) { @@ -136,7 +134,7 @@ public boolean doDownloadFile(URL url, final Path file) { private InputStream getInputStream(URL url) throws Exception { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); cookieManager.setCookies(conn); - conn.addRequestProperty("User-Agent", "Mozilla"); + conn.addRequestProperty(HttpHeaders.USER_AGENT, "Mozilla"); conn.addRequestProperty("Referer", url.toString()); conn.setInstanceFollowRedirects(false); @@ -146,17 +144,17 @@ private InputStream getInputStream(URL url) throws Exception { if (status != HttpURLConnection.HTTP_OK) { if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM - || status == HttpURLConnection.HTTP_SEE_OTHER) { - if (HttpClient.isUrl(conn.getHeaderField("Location"))) { - url = new URI(conn.getHeaderField("Location")).toURL(); + || status == HttpURLConnection.HTTP_SEE_OTHER) { + String locationHeader = conn.getHeaderField(HttpHeaders.LOCATION); + URL newUrl; + if (HttpClient.isUrl(locationHeader)) { + newUrl = new URI(locationHeader).toURL(); } 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(); + newUrl = new URI("%s://%s/%s".formatted(url.protocol, conn.getURL().host, + locationHeader.trim().replace(" ", "%20"))).toURL(); } - return getInputStream(url); + return getInputStream(newUrl); } - throw new Exception("error: " + status); } else { return conn.getInputStream(); @@ -164,14 +162,15 @@ 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.CASE_INSENSITIVE); - Matcher matcher = urlPattern.matcher(str); - return matcher.find(); + Pattern urlPattern = Pattern.compile( + "((https?|ftp|gopher|telnet|file):((//)|(\\\\\\\\))+[\\\\w\\\\d:#@%/;$()~_?\\\\+-=\\\\\\\\\\\\.&]*)", + Pattern.CASE_INSENSITIVE); + return urlPattern.matcher(str).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); @@ -181,5 +180,4 @@ public String downloadText(String url) throws IOException { public void storeCookies(String domain, Map cookieMap) { cookieManager.storeCookies(domain, cookieMap); } - } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClientException.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClientException.java index 3785f027..934ec9f8 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClientException.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/HttpClientException.java @@ -4,59 +4,42 @@ import java.io.Serial; import java.net.HttpURLConnection; +import manifold.ext.props.rt.api.val; + public class HttpClientException extends Exception { @Serial private static final long serialVersionUID = 5583416046207372599L; - private int responseCode = -1; - private String responseMessage = ""; + @val int responseCode; + @val String responseMessage; public HttpClientException(HttpURLConnection connection) { - storeConnectionResponseInfo(connection); - } - - public HttpClientException() { - super(); - } - - public HttpClientException(String message, HttpURLConnection connection) { - super(message); - storeConnectionResponseInfo(connection); - } - - public HttpClientException(String message, Throwable cause, HttpURLConnection connection) { - super(message, cause); - storeConnectionResponseInfo(connection); + this(null, connection); } public HttpClientException(Throwable cause, HttpURLConnection connection) { super(cause); - storeConnectionResponseInfo(connection); - } - - protected HttpClientException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace, HttpURLConnection connection) { - super(message, cause, enableSuppression, writableStackTrace); - storeConnectionResponseInfo(connection); + this.responseCode = getResponseCode(connection); + this.responseMessage = getResponseMessage(connection); } - private void storeConnectionResponseInfo(HttpURLConnection connection) { + private int getResponseCode(HttpURLConnection connection) { if (connection != null) { try { - this.responseCode = connection.getResponseCode(); - this.responseMessage = connection.getResponseMessage(); + return connection.getResponseCode(); } catch (IOException e) { - // let's keep this quiet! } } + return -1; } - public int getResponseCode() { - return this.responseCode; - } - - public String getResponseMessage() { - return this.responseMessage; + private String getResponseMessage(HttpURLConnection connection) { + if (connection != null) { + try { + return connection.getResponseMessage(); + } catch (IOException e) { + } + } + return ""; } - } 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..6c2849f4 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("<", "<"); @@ -77,20 +79,19 @@ private StringUtils() { */ public static String unescapeHTML(String source) { - int i, j; - boolean continueLoop; int skip = 0; + String text = source; do { continueLoop = false; - i = source.indexOf("&", skip); + int i = text.indexOf("&", skip); if (i > -1) { - j = source.indexOf(";", i); + int j = text.indexOf(";", i); if (j > i) { - String entityToLookFor = source.substring(i, j + 1); + String entityToLookFor = text.substring(i, j + 1); String value = htmlEntities.get(entityToLookFor); if (value != null) { - source = source.substring(0, i) + value + source.substring(j + 1); + text = text.substring(0, i) + value + text.substring(j + 1); } else { skip = i + 1; } @@ -98,7 +99,7 @@ public static String unescapeHTML(String source) { } } } while (continueLoop); - return source; + return text; } } 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..bf92518e 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,48 +1,35 @@ package org.lodder.subtools.sublibrary.xml; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; 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; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import org.lodder.subtools.sublibrary.util.http.HttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class XMLHelper { - private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class); + private static final Logger LOGGER = LoggerFactory.getLogger(XMLHelper.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) { @@ -56,98 +43,25 @@ public static String getStringTagRawValue(String sTag, Element eElement) { return ""; } - 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)); - } - public static int getIntTagValue(String sTag, Element eElement) { LOGGER.trace("getIntTagValue: sTag [{}]", sTag); Node nValue = eElement.getElementsByTagName(sTag).item(0).getChildNodes().item(0); return nValue == null ? 0 : Integer.parseInt(nValue.getNodeValue()); } - public static boolean getBooleanTagValue(String sTag, Element eElement) { - LOGGER.trace("getBooleanTagValue: sTag [{}]", sTag); - Node nValue = eElement.getElementsByTagName(sTag).item(0).getChildNodes().item(0); - return nValue != null && Boolean.parseBoolean(nValue.getNodeValue()); - } - - 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)); - } - - public static String cleanBadChars(String string) { - LOGGER.trace("cleanBadChars: string [{}]", string); - /* Remove bad chars for the find function of bierdopje api. */ - string = string.toLowerCase().replace(" and ", " & "); - string = string.replace("&", ""); - string = string.replace("#", ""); - string = string.replace("*", ""); - string = string.replace("!", ""); - string = string.replace("$", ""); - string = string.replace(" ", " "); - return string.trim(); - } - - public static void writeToFile(Path file, Document doc) throws Exception { - String xmlString = getXMLAsString(doc); - try (OutputStream os = Files.newOutputStream(file)) { - byte[] xmlStringContent = xmlString.getBytes(StandardCharsets.UTF_8); - os.write(xmlStringContent); - } - } - - public static String getXMLAsString(Document doc) throws Exception { - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - - StreamResult result = new StreamResult(new StringWriter()); - DOMSource source = new DOMSource(doc); - transformer.transform(source, result); - - return result.getWriter().toString(); + public static Document getDocument(String string) throws ParserConfigurationException, IOException { + return getDocument(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8))); } - public static String getXMLAsString(Element eElement) throws Exception { - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - - StreamResult result = new StreamResult(new StringWriter()); - DOMSource source = new DOMSource(eElement); - transformer.transform(source, result); - - 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 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/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/main/resources/messages.properties b/SubLibrary/src/main/resources/resourcebundle/Message.properties similarity index 95% rename from SubLibrary/src/main/resources/messages.properties rename to SubLibrary/src/main/resources/resourcebundle/Message.properties index 6aae81c3..5ad0d17b 100644 --- a/SubLibrary/src/main/resources/messages.properties +++ b/SubLibrary/src/main/resources/resourcebundle/Message.properties @@ -72,14 +72,14 @@ App.Logging=Logging App.Movie=Movie App.NoFolderSelected=No folder selected App.NoReleaseEntered=No Movie/Episode/Release entered -App.NoValidLanguage=Invalid language code +App.NoValidLanguage=Invalid language code App.OK=OK App.OptionConfirmProviderMapping=Confirm provider mappings App.OptionDryRun=Enable dry run, only show available subtitles App.OptionHelpMsg=print this message App.OptionNoGuiMsg=run in cli mode App.OptionOptionDebugMsg=enables logging -App.OptionOptionDownloadAllMsg=Download all found subs using '-v1' system +App.OptionOptionDownloadAllMsg=Download all found subs using '-v1' system App.OptionOptionFolderMsg=folder to search App.OptionOptionForceMsg=force to overwrite the subtitle on disk App.OptionOptionImportPreferencesMsg=import preferences @@ -120,7 +120,7 @@ InputPanel.UpdateType.Nightly=Nightly LoggingPanel.RateLimitReached=Rate limit reached for %s, please wait %s seconds LoggingPanel.UpdateCheckFailed=Update check failed MainWindow.CurrentVersion=current version -MainWindow.DownloadingSubtitle=Dowloading subtitle for %s +MainWindow.DownloadingSubtitle=Downloading subtitle for %s MainWindow.ignoreExistingSubtitles=Ignore existing subtitles MainWindow.LocationNewEpisodes=Location new episodes MainWindow.NoUpdateAvailable=No new update available @@ -280,18 +280,17 @@ SelectDialog.Option=Option SelectDialog.EnterListSelectedSubtitles=Enter comma separated list of numbers of selected subtitles: SelectDialog.SelectCorrectSubtitle=Select correct subtitle SelectDialog.SelectCorrectSubtitleThisRelease=Select correct subtitle for this release\: -SelectDialog.SelectSerieNameForName=Select correct serie name for serie [%s]. +SelectDialog.SelectSerieNameForName=Select correct serie name for serie [%s]. SelectDialog.SelectSerieNameForNameWithSeason=Select correct serie name for serie [%s] and season [%s]. Splash.starting=MultiSubDownloader Starting ... StructureBuilderDialog.AvailableTagsClickToAdd=Available tags (click to add). -StructureBuilderDialog.Description=Additional info from file name of the moviefile, e.g. the release group. +StructureBuilderDialog.ReleaseGroup=Release group StructureBuilderDialog.EpisodeTitle=Episode title -StructureBuilderDialog.MovieDescription=Additional info from file name of the moviefile, e.g. the release group. 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 +299,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/resourcebundle/Message_nl.properties similarity index 95% rename from SubLibrary/src/main/resources/messages_nl.properties rename to SubLibrary/src/main/resources/resourcebundle/Message_nl.properties index 246ac73b..c62b03d0 100644 --- a/SubLibrary/src/main/resources/messages_nl.properties +++ b/SubLibrary/src/main/resources/resourcebundle/Message_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 @@ -241,13 +241,13 @@ PreferenceDialog.UseAddic7edLogin=Addic7ed login gebruiken PreferenceDialog.UseOpenSubtitlesLogin=OpenSubtitles login gebruiken PreferenceDialog.UseProxyServer=Proxyserver gebruiken PreferenceDialog.Username=Gebruikersnaam -PreferenceDialog.UseTvdbName=Vervang serie naam met TheTVDB serie naam in bestandsnaam +PreferenceDialog.UseTvdbName=Vervang serie naam met TheTVDB serie naam in bestandsnaam ProgressDialog.Title=Vooruitgang 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 @@ -283,15 +283,14 @@ SelectDialog.SelectCorrectSubtitleThisRelease=Selecteer de correcte ondertitel v SelectDialog.SelectSerieNameForName=Selecteer de correcte serie naam voor serie [%s]. SelectDialog.SelectSerieNameForNameWithSeason=Selecteer de correcte serie naam voor serie [%s] en seizoen [%s]. Splash.starting=MultiSubDownloader Starten ... -StructureBuilderDialog.AvailableTagsClickToAdd=Beschikbare tags (klik erop om ze in te voegen). -StructureBuilderDialog.Description=Overige info uit de bestandsnaam van het filmbestand, bv. de release groep. +StructureBuilderDialog.AvailableTagsClickToAdd=Beschikbare tags (klik erop om ze toe te voegen). +StructureBuilderDialog.ReleaseGroup=Release groep StructureBuilderDialog.EpisodeTitle=Titel van de aflevering. -StructureBuilderDialog.MovieDescription=Overige info uit de bestandsnaam van het filmbestand, bijv. de release groep. 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 +299,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/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java new file mode 100644 index 00000000..c422dd1a --- /dev/null +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java @@ -0,0 +1,38 @@ +package org.lodder.subtools.sublibrary.assertions; + +import static org.assertj.core.api.Assertions.*; + +import manifold.ext.rt.api.Self; +import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.VideoType; + +public class MovieReleaseAssert extends ReleaseAssert { + + public MovieReleaseAssert(MovieRelease actual) { + super(actual); + } + + public @Self MovieReleaseAssert hasYear(int year) { + isNotNull(); + assertThat(actual.year).isEqualTo(year); + return this; + } + + public @Self MovieReleaseAssert withoutYear() { + isNotNull(); + assertThat(actual.year).isZero(); + return this; + } + + public @Self MovieReleaseAssert hasName(String name) { + isNotNull(); + assertThat(actual.name).isEqualTo(name); + return this; + } + + public @Self MovieReleaseAssert hasMovieVideoType() { + isNotNull(); + assertThat(actual.videoType).isEqualTo(VideoType.MOVIE); + return this; + } +} diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/ReleaseAssert.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/ReleaseAssert.java new file mode 100644 index 00000000..b8813fe7 --- /dev/null +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/ReleaseAssert.java @@ -0,0 +1,62 @@ +package org.lodder.subtools.sublibrary.assertions; + +import static org.assertj.core.api.Assertions.*; + +import manifold.ext.rt.api.Self; +import org.assertj.core.api.AbstractAssert; +import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.TvRelease; + +public class ReleaseAssert extends AbstractAssert, R> { + + public ReleaseAssert(R actual) { + super(actual, ReleaseAssert.class); + } + + public MovieReleaseAssert isMovie() { + assertThat(actual).isInstanceOf(MovieRelease.class); + return new MovieReleaseAssert((MovieRelease) actual); + } + + public TvReleaseAssert isSerie() { + assertThat(actual).isInstanceOf(TvRelease.class); + return new TvReleaseAssert((TvRelease) actual); + } + + public @Self ReleaseAssert hasFileName(String fileName) { + isNotNull(); + assertThat(actual.fileName).isEqualTo(fileName); + return this; + } + + public @Self ReleaseAssert hasExtension(String extension) { + isNotNull(); + assertThat(actual.extension).isEqualTo(extension); + return this; + } + + public @Self ReleaseAssert hasReleaseGroup(String releaseGroup) { + isNotNull(); + assertThat(actual.releaseGroup).isEqualTo(releaseGroup); + return this; + } + + public @Self ReleaseAssert withoutReleaseGroup() { + isNotNull(); + assertThat(actual.releaseGroup).isEmpty(); + return this; + } + + public @Self ReleaseAssert hasQuality(String quality) { + isNotNull(); + assertThat(actual.quality).isEqualTo(quality); + return this; + } + + public @Self ReleaseAssert withoutQuality() { + isNotNull(); + assertThat(actual.quality).isEmpty(); + return this; + } +} diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/SubLibraryAssertions.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/SubLibraryAssertions.java new file mode 100644 index 00000000..e2f0ead6 --- /dev/null +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/SubLibraryAssertions.java @@ -0,0 +1,22 @@ +package org.lodder.subtools.sublibrary.assertions; + +import lombok.experimental.UtilityClass; +import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.TvRelease; + +@UtilityClass +public class SubLibraryAssertions { + + public static TvReleaseAssert assertThat(TvRelease actual) { + return new TvReleaseAssert(actual); + } + + public static MovieReleaseAssert assertThat(MovieRelease actual) { + return new MovieReleaseAssert(actual); + } + + public static ReleaseAssert assertThat(Release actual) { + return new ReleaseAssert<>(actual); + } +} diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java new file mode 100644 index 00000000..a7ae9f62 --- /dev/null +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java @@ -0,0 +1,62 @@ +package org.lodder.subtools.sublibrary.assertions; + +import static org.assertj.core.api.Assertions.*; + +import manifold.ext.rt.api.Self; +import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.VideoType; + +public class TvReleaseAssert extends ReleaseAssert { + + public TvReleaseAssert(TvRelease actual) { + super(actual); + } + + public @Self TvReleaseAssert hasSeason(int season) { + isNotNull(); + assertThat(actual.season).isEqualTo(season); + return this; + } + + public @Self TvReleaseAssert hasEpisodes(Integer... episodeNumbers) { + isNotNull(); + assertThat(actual.episodeNumbers).containsExactly(episodeNumbers); + return this; + } + + public @Self TvReleaseAssert hasName(String name) { + isNotNull(); + assertThat(actual.name).isEqualTo(name); + return this; + } + + public @Self TvReleaseAssert hasTitle(String title) { + isNotNull(); + assertThat(actual.title).isEqualTo(title); + return this; + } + + public @Self TvReleaseAssert withoutTitle() { + isNotNull(); + assertThat(actual.title).isNull(); + return this; + } + + public @Self TvReleaseAssert isSpecial() { + isNotNull(); + assertThat(actual.special).isTrue(); + return this; + } + + public @Self TvReleaseAssert isNotSpecial() { + isNotNull(); + assertThat(actual.special).isFalse(); + return this; + } + + public @Self TvReleaseAssert hasEpisodeVideoType() { + isNotNull(); + assertThat(actual.videoType).isEqualTo(VideoType.EPISODE); + return this; + } +} diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/cache/InMemoryCacheTest.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/cache/InMemoryCacheTest.java index 7dc8677f..b48115fd 100644 --- a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/cache/InMemoryCacheTest.java +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/cache/InMemoryCacheTest.java @@ -9,11 +9,11 @@ class InMemoryCacheTest { @Test void testAddRemoveObjects() { InMemoryCache cache = - InMemoryCache.builder().keyType(String.class).valueType(String.class) - .timeToLive(200L) - .timerInterval(100L) - .maxItems(6) - .build(); + InMemoryCache.builder().keyType(String.class).valueType(String.class) + .timeToLive(200L) + .timerInterval(100L) + .maxItems(6) + .build(); cache.put("eBay", "eBay"); cache.put("Paypal", "Paypal"); @@ -36,11 +36,11 @@ void testAddRemoveObjects() { void testExpiredCacheObjects() throws InterruptedException { InMemoryCache cache = - InMemoryCache.builder().keyType(String.class).valueType(String.class) - .timeToLive(1L) - .timerInterval(1L) - .maxItems(10) - .build(); + InMemoryCache.builder().keyType(String.class).valueType(String.class) + .timeToLive(1L) + .timerInterval(1L) + .maxItems(10) + .build(); cache.put("eBay", "eBay"); cache.put("Paypal", "Paypal"); @@ -56,11 +56,11 @@ void testObjectsCleanupTime() throws InterruptedException { int size = 500000; InMemoryCache cache = - InMemoryCache.builder().keyType(String.class).valueType(String.class) - .timeToLive(100L) - .timerInterval(100L) - .maxItems(500000) - .build(); + InMemoryCache.builder().keyType(String.class).valueType(String.class) + .timeToLive(100L) + .timerInterval(100L) + .maxItems(500000) + .build(); for (int i = 0; i < size; i++) { String value = Integer.toString(i); @@ -73,7 +73,7 @@ void testObjectsCleanupTime() throws InterruptedException { cache.cleanup(); double finish = (System.currentTimeMillis() - start) / 1000.0; - System.out.println("Cleanup times for " + size + " objects are " + finish + " s"); + System.out.println("Cleanup times for $size objects are $finish s"); } } 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..6adc6297 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,32 +1,30 @@ package org.lodder.subtools.sublibrary.control; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.lodder.subtools.sublibrary.assertions.SubLibraryAssertions.assertThat; import java.nio.file.Path; -import java.util.List; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; 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.model.VideoType; 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); - assertThat(releaseGroup).isEqualTo("A"); + releaseGroup = ReleaseParser.extractReleaseGroup("The.Following.S03E01.HDTV.XviD-AFG", true); + assertThat(releaseGroup).isEmpty(); - 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,154 +35,314 @@ 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()); - - assertThat(q).containsExactly("720p", "hdtv", "x264"); + assertThat(ReleaseParser.getQualityKeyWords(release.quality)).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()); - - assertThat(q).containsExactly("1080p", "web-dl", "dd5 1", "h264"); + assertThat(ReleaseParser.getQualityKeyWords(release.quality)) + .containsExactly("1080p", "web-dl", "dd5.1", "x264"); } - @Test - void testTV() throws Exception { - ReleaseParser releaseparser = new ReleaseParser(); - - 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"); - - TvRelease tvrelease = (TvRelease) release; - - assertThat(tvrelease.getSeason()).isEqualTo(10); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).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(""); - - tvrelease = (TvRelease) release; - - assertThat(tvrelease.getSeason()).isEqualTo(4); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).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"); - - tvrelease = (TvRelease) release; - - assertThat(tvrelease.getSeason()).isEqualTo(1); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).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"); - - tvrelease = (TvRelease) release; - - assertThat(tvrelease.getSeason()).isEqualTo(4); - assertThat(tvrelease.getEpisodeNumbers().size()).isEqualTo(1); - assertThat((int) tvrelease.getEpisodeNumbers().get(0)).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"); - - 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); + @Nested + class testTV { + + private final ReleaseParser releaseparser = new ReleaseParser(); + + @Nested + class StartsWithSeasonEpisode { + + + @Test + void StartsWithSeasonEpisode1() throws Exception { + Path file = Path.of("S04E02 - White Collar - Most Wanted.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("S04E02 - White Collar - Most Wanted.mkv") + .withoutReleaseGroup() + .withoutQuality() + .hasSeason(4) + .hasEpisodes(2) + .hasName("White Collar") + .hasTitle("Most Wanted"); + } + + @Test + void StartsWithSeasonEpisode2() throws Exception { + Path file = Path.of("(S04E02) - White Collar - Most Wanted.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("(S04E02) - White Collar - Most Wanted.mkv") + .withoutReleaseGroup() + .withoutQuality() + .hasSeason(4) + .hasEpisodes(2) + .hasName("White Collar") + .hasTitle("Most Wanted"); + } + + @Test + void StartsWithSeasonEpisode3() throws Exception { + Path file = Path.of("(S04E02) White Collar - Most Wanted.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("(S04E02) White Collar - Most Wanted.mkv") + .withoutReleaseGroup() + .withoutQuality() + .hasSeason(4) + .hasEpisodes(2) + .hasName("White Collar") + .hasTitle("Most Wanted"); + } + } + + + @Test + void testTV1() throws Exception { + Path file = Path.of("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv") + .hasReleaseGroup("DIMENSION") + .hasQuality("720p hdtv x264") + .hasSeason(10) + .hasEpisodes(12) + .hasName("Criminal Minds") + .withoutTitle(); + } + + @Test + void testTV4() throws Exception { + Path file = Path.of("Spartacus.Gods.of.The.Arena.Pt.IV.720p.HDTV.X264-DIMENSION.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("Spartacus.Gods.of.The.Arena.Pt.IV.720p.HDTV.X264-DIMENSION.mkv") + .hasReleaseGroup("DIMENSION") + .hasQuality("720p hdtv x264") + .hasSeason(1) + .hasEpisodes(4) + .hasName("Spartacus Gods of The Arena") + .withoutTitle(); + } + + @Test + void testTV5() throws Exception { + Path file = Path.of("hawaii.five-0.2010.410.hdtv-lol.mp4"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mp4") + .hasFileName("hawaii.five-0.2010.410.hdtv-lol.mp4") + .hasReleaseGroup("lol") + .hasQuality("hdtv") + .hasSeason(4) + .hasEpisodes(10) + .hasName("hawaii five-0") + .withoutTitle(); + } + + @Test + void testTV6() throws Exception { + Path file = Path.of("Greys.Anatomy.S10E01E02.720p.HDTV.X264-DIMENSION.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("Greys.Anatomy.S10E01E02.720p.HDTV.X264-DIMENSION.mkv") + .hasReleaseGroup("DIMENSION") + .hasQuality("720p hdtv x264") + .hasSeason(10) + .hasEpisodes(1, 2) + .hasName("Greys Anatomy") + .withoutTitle(); + } + + @Test + void testTV7() throws Exception { + Path file = Path.of("Greys.Anatomy.S10E01E02 Seal Our Fate 720p.HDTV.X264-DIMENSION.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("Greys.Anatomy.S10E01E02 Seal Our Fate 720p.HDTV.X264-DIMENSION.mkv") + .hasReleaseGroup("DIMENSION") + .hasQuality("720p hdtv x264") + .hasSeason(10) + .hasEpisodes(1, 2) + .hasName("Greys Anatomy") + .hasTitle("Seal Our Fate"); + } + + + @Test + void testTV8() throws Exception { + Path file = Path.of("(2-11) Joey and the High School Friend.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("(2-11) Joey and the High School Friend.mkv") + .withoutReleaseGroup() + .withoutQuality() + .hasSeason(2) + .hasEpisodes(11) + .hasName("Joey and the High School Friend") + .withoutTitle(); + } + + @Test + void testTV9() throws Exception { + Path file = Path.of("The.Boys.S04E05.Beware.the.jabberwock.my.son.1080p.web.dl.hevc.x265.rmteam.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isSerie() + .hasEpisodeVideoType() + .hasExtension("mkv") + .hasFileName("The.Boys.S04E05.Beware.the.jabberwock.my.son.1080p.web.dl.hevc.x265.rmteam.mkv") + .hasReleaseGroup("rmteam") + .hasQuality("1080p web-dl x265") + .hasSeason(4) + .hasEpisodes(5) + .hasName("The Boys") + .hasTitle("Beware the jabberwock my son"); + } } @Test - void testReleaseParseExceptionMessage() throws ReleaseParseException { + void testReleaseParseExceptionMessage() { Path file = Path.of("exceptiontesting.mkv"); - assertThatExceptionOfType(ReleaseParseException.class) - .isThrownBy(() -> new ReleaseParser().parse(file)) - .withMessage("Unknown format, can't be parsed: " + file.toAbsolutePath()); + assertThatExceptionOfType(ReleaseParseException.class).isThrownBy(() -> new ReleaseParser().parse(file)) + .withMessage("Unknown format, can't be parsed: " + file.toAbsolutePath()); } - @Test - void testMovie() throws Exception { - ReleaseParser releaseparser = new ReleaseParser(); - - 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"); - - MovieRelease movieRelease = (MovieRelease) release; - - assertThat((int) movieRelease.getYear()).isEqualTo(1989); - assertThat(movieRelease.getName()).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"); - - movieRelease = (MovieRelease) release; - - assertThat((int) movieRelease.getYear()).isEqualTo(2014); - assertThat(movieRelease.getName()).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"); - - movieRelease = (MovieRelease) release; - - assertThat((int) movieRelease.getYear()).isEqualTo(2014); - assertThat(movieRelease.getName()).isEqualTo("The Trip to Italy"); + @Nested + class TestMovie { + private final ReleaseParser releaseparser = new ReleaseParser(); + + @Test + void testMovie1() 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) + .isMovie() + .hasMovieVideoType() + .hasExtension("mkv") + .hasFileName("Back.to.the.Future.Part.II.1989.720p.BluRay.X264-AMIABLE.mkv") + .hasReleaseGroup("AMIABLE") + .hasQuality("720p bluray x264") + .hasYear(1989) + .hasName("Back to the Future Part II"); + } + + @Test + void testMovie2() throws Exception { + Path file = Path.of("Back.to.the.Future.Part.21.1989.720p.BluRay.X264-AMIABLE.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isMovie() + .hasMovieVideoType() + .hasExtension("mkv") + .hasFileName("Back.to.the.Future.Part.21.1989.720p.BluRay.X264-AMIABLE.mkv") + .hasReleaseGroup("AMIABLE") + .hasQuality("720p bluray x264") + .hasYear(1989) + .hasName("Back to the Future Part 21"); + } + + @Test + void testMovie3() throws Exception { + Path file = Path.of("The.Equalizer.2014.720p.BluRay.x264-SPARKS.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isMovie() + .hasMovieVideoType() + .hasExtension("mkv") + .hasFileName("The.Equalizer.2014.720p.BluRay.x264-SPARKS.mkv") + .hasReleaseGroup("SPARKS") + .hasQuality("720p bluray x264") + .hasYear(2014) + .hasName("The Equalizer"); + } + + @Test + void testMovie4() throws Exception { + Path file = Path.of("The.Trip.to.Italy.2014.LIMITED.720p.BluRay.x264-GECKOS.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isMovie() + .hasMovieVideoType() + .hasExtension("mkv") + .hasFileName("The.Trip.to.Italy.2014.LIMITED.720p.BluRay.x264-GECKOS.mkv") + .hasReleaseGroup("GECKOS") + .hasQuality("720p bluray x264") + .hasYear(2014) + .hasName("The Trip to Italy"); + } + + @Test + void testMovie5() throws Exception { + Path file = Path.of("Final.Destination.5.720p.Bluray.x264-TWiZTED.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isMovie() + .hasMovieVideoType() + .hasExtension("mkv") + .hasFileName("Final.Destination.5.720p.Bluray.x264-TWiZTED.mkv") + .hasReleaseGroup("TWiZTED") + .hasQuality("720p bluray x264") + .withoutYear() + .hasName("Final Destination 5"); + } + + @Test + void testMovie6() throws Exception { + Path file = Path.of("Final.Destination.5.2011.720p.Bluray.x264-TWiZTED.mkv"); + Release release = releaseparser.parse(file); + + assertThat(release) + .isMovie() + .hasMovieVideoType() + .hasExtension("mkv") + .hasFileName("Final.Destination.5.2011.720p.Bluray.x264-TWiZTED.mkv") + .hasReleaseGroup("TWiZTED") + .hasQuality("720p bluray x264") + .hasYear(2011) + .hasName("Final Destination 5"); + } } } diff --git a/pom.xml b/pom.xml index 0189cdb4..144f2e81 100644 --- a/pom.xml +++ b/pom.xml @@ -1,295 +1,336 @@ - - 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 + 2025.1.10 + 1.18.38 + 24 + 24 + - - 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 + + -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.9.0 + + + systems.manifold + manifold-ext-rt + ${manifold.version} + + + systems.manifold + manifold-props-rt + ${manifold.version} + + + systems.manifold + manifold-tuple-rt + ${manifold.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + ch.qos.logback + logback-classic + 1.1.2 + + + org.openapitools + openapi-generator-core + 7.12.0 + + + io.swagger.core.v3 + swagger-annotations + 2.2.29 + + + com.squareup.okhttp3 + logging-interceptor + 4.12.0 + + + com.google.code.gson + gson + 2.12.1 + + + error_prone_annotations + com.google.errorprone + + + + + io.gsonfire + gson-fire + 1.9.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.18.3 + + + com.pivovarit + throwing-function + 1.6.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.17.0 + + + org.jsoup + jsoup + 1.19.1 + + + org.jspecify + jspecify + 1.0.0 + + + org.apache.commons + commons-collections4 + 4.4 + + + org.hsqldb + hsqldb + 2.7.4 + + + jakarta.ws.rs + jakarta.ws.rs-api + 4.0.0 + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + + + com.uwetrottmann.thetvdb-java + thetvdb-java + 2.4.0 + + + com.miglayout + miglayout-swing + 11.4.2 + + + 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.3 + + + junit + junit + + + + + org.json + json + 20250107 + + + 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.16.1 + test + + + org.assertj + assertj-core + 3.27.3 + test + + + org.junit.jupiter + junit-jupiter-api + 5.12.1 + test + + + \ No newline at end of file From f125a79f3641ded123b8e36df43cc7605b8a4ab2 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 13 Apr 2025 19:42:30 +0200 Subject: [PATCH 02/58] use jspecify annotation --- .../multisubdownloader/cli/actions/CliSearchAction.java | 2 +- .../gui/actions/search/FileGuiSearchAction.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 4bcc8f7c..79123a50 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 @@ -7,13 +7,13 @@ import java.util.ArrayList; import java.util.List; -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.jspecify.annotations.NonNull; import org.lodder.subtools.multisubdownloader.CLI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; 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 2bd8c0bb..258dd60a 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 @@ -4,10 +4,10 @@ import java.util.ArrayList; import java.util.List; -import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import org.jspecify.annotations.NonNull; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.FileListAction; From 9bd8f3844de50af7d4c1c5653b626e3dd92f70e1 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 13 Apr 2025 19:42:43 +0200 Subject: [PATCH 03/58] removed unused Getter annotation --- .../subtools/multisubdownloader/UserInteractionHandlerGUI.java | 2 -- 1 file changed, 2 deletions(-) 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 8f37a710..da3e7cee 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java @@ -3,13 +3,11 @@ import javax.swing.*; import java.util.List; -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; -@Getter public class UserInteractionHandlerGUI extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandlerGUI implements UserInteractionHandler { From 415aa0b24990e627d2a25f03f9b84b6a4bcfb17f Mon Sep 17 00:00:00 2001 From: EotT123 Date: Tue, 15 Apr 2025 18:55:41 +0200 Subject: [PATCH 04/58] suppress warnings --- .../java/org/lodder/subtools/multisubdownloader/lib/Info.java | 1 + 1 file changed, 1 insertion(+) 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 2c5d5ee5..879341e2 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 @@ -6,6 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@SuppressWarnings("java:S106") @UtilityClass public class Info { From 231efd88b6e8c65bf51e42f48267685e4e201f95 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 16 Apr 2025 20:29:09 +0200 Subject: [PATCH 05/58] small change --- .../java/org/lodder/subtools/multisubdownloader/GUI.java | 2 +- .../gui/extra/progress/StatusMessenger.java | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) 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 0caf2bf9..3b5c6a07 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -189,7 +189,7 @@ public void windowClosing(WindowEvent e) { contentPane.add(pnlLogging, gbcPnlLogging); StatusLabel lblStatus = new StatusLabel(""); - StatusMessenger.getInstance().addListener(lblStatus); + StatusMessenger.instance.addListener(lblStatus); final GridBagConstraints gbcLblStatus = new GridBagConstraints(); gbcLblStatus.anchor = GridBagConstraints.SOUTHWEST; gbcLblStatus.gridx = 0; 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 5a4debb0..41f334d4 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 @@ -3,8 +3,10 @@ import java.util.LinkedList; import java.util.List; +import manifold.ext.props.rt.api.val; + public class StatusMessenger implements Messenger { - private static final StatusMessenger instance = new StatusMessenger(); + @val static StatusMessenger instance = new StatusMessenger(); private final List statusMessengers = new LinkedList<>(); @@ -12,10 +14,6 @@ private StatusMessenger() { // private constructor to prevent instantiation } - public static StatusMessenger getInstance() { - return instance; - } - public void addListener(Messenger sm) { synchronized (statusMessengers) { statusMessengers.add(sm); From 17bd78f81c6d54a98f12ad7ec3cf3276c3c7da52 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 16 Apr 2025 22:57:06 +0200 Subject: [PATCH 06/58] use optional parameters (1) --- MultiSubDownloader/pom.xml | 11 ++- .../subtools/multisubdownloader/CLI.java | 26 +++-- .../subtools/multisubdownloader/GUI.java | 22 ++--- .../cli/actions/CliSearchAction.java | 91 +---------------- .../actions/search/FileGuiSearchAction.java | 48 +-------- .../actions/search/TextGuiSearchAction.java | 48 +-------- .../gui/extra/JListWithImages.java | 29 +----- .../gui/panels/preference/GeneralPanel.java | 6 +- .../preference/SerieProvidersPanel.java | 3 +- .../panels/preference/StructureFilePanel.java | 21 ++-- .../preference/StructureFolderPanel.java | 15 ++- .../lib/library/FilenameLibraryBuilder.java | 97 +++---------------- .../lib/library/LibraryBuilder.java | 6 +- .../lib/library/PathLibraryBuilder.java | 88 +++-------------- pom.xml | 5 + 15 files changed, 89 insertions(+), 427 deletions(-) diff --git a/MultiSubDownloader/pom.xml b/MultiSubDownloader/pom.xml index bb838ade..ffb93632 100644 --- a/MultiSubDownloader/pom.xml +++ b/MultiSubDownloader/pom.xml @@ -63,6 +63,10 @@ systems.manifold manifold-props-rt + + systems.manifold + manifold-params-rt + org.projectlombok lombok @@ -155,9 +159,14 @@ systems.manifold - manifold-strings + manifold-params ${manifold.version} + + + + + 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 134e0f87..40480cbd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java @@ -96,20 +96,18 @@ public void download(List releases) { public void search() { try { - CliSearchAction - .createWithSettings(settings) - .subtitleProviderStore(app.makeSubtitleProviderStore()) - .indexingProgressListener(new CLIFileIndexerProgress().verbose(verboseProgress)) - .searchProgressListener(new CLISearchProgress().verbose(verboseProgress)) - .cli(this) - .fileListAction(new FileListAction(this.settings)) - .language(language) - .releaseFactory(new ReleaseFactory(this.settings, app.makeManager())) - .filtering(new SubtitleFiltering(this.settings)) - .folders(folders) - .recursive(recursive) - .overwriteSubtitles(force) - .build() + new CliSearchAction(settings:settings, + subtitleProviderStore:app.makeSubtitleProviderStore(), + indexingProgressListener:new CLIFileIndexerProgress().verbose(verboseProgress), + searchProgressListener:new CLISearchProgress().verbose(verboseProgress), + cli:this, + fileListAction:new FileListAction(this.settings), + language:language, + releaseFactory:new ReleaseFactory(this.settings, app.makeManager()), + filtering:new SubtitleFiltering(this.settings), + folders:folders, + recursive:recursive, + overwriteSubtitles:force) /* CLI has no benefit of running this in a separate Thread */ .run(); } catch (SearchSetupException e) { 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 3b5c6a07..47ed3621 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -4,8 +4,10 @@ import static org.lodder.subtools.multisubdownloader.gui.extra.table.SearchColumnName.*; import javax.swing.*; -import javax.swing.event.*; -import javax.swing.table.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.event.MouseListener; @@ -249,12 +251,8 @@ private void createTextSearchPanel() { resultPanel.setTable(createSubtitleTable()); resultPanel.setDownloadAction(_ -> downloadText()); - TextGuiSearchAction searchAction = TextGuiSearchAction.createWithSettings(settings) - .subtitleProviderStore(app.makeSubtitleProviderStore()) - .mainWindow(this) - .searchPanel(pnlSearchText) - .releaseFactory(new ReleaseFactory(settings, app.makeManager())) - .build(); + TextGuiSearchAction searchAction = new TextGuiSearchAction(settings, app.makeSubtitleProviderStore(), + this, pnlSearchText, new ReleaseFactory(settings, app.makeManager())); pnlSearchTextInput.addSearchAction(searchAction); } @@ -279,12 +277,8 @@ private void createFileSearchPanel() { resultPanel.setTable(createVideoTable()); - FileGuiSearchAction searchAction = FileGuiSearchAction.createWithSettings(settings) - .subtitleProviderStore(app.makeSubtitleProviderStore()) - .mainWindow(this) - .searchPanel(pnlSearchFile) - .releaseFactory(new ReleaseFactory(settings, app.makeManager())) - .build(); + FileGuiSearchAction searchAction = new FileGuiSearchAction(settings, app.makeSubtitleProviderStore(), this, + pnlSearchFile, new ReleaseFactory(settings, app.makeManager())); pnlSearchFileInput.addSelectFolderAction(_ -> selectIncomingFolder()); pnlSearchFileInput.addSearchAction(searchAction); 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 79123a50..a66554b8 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 @@ -7,13 +7,9 @@ import java.util.ArrayList; import java.util.List; -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.jspecify.annotations.NonNull; import org.lodder.subtools.multisubdownloader.CLI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; @@ -33,7 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({ Files.class }) +@ExtensionMethod({Files.class}) public class CliSearchAction extends SearchAction { private static final Logger LOGGER = LoggerFactory.getLogger(CliSearchAction.class); @@ -50,89 +46,10 @@ public class CliSearchAction extends SearchAction { @get(Protected) @override IndexingProgressListener indexingProgressListener; @get(Protected) @override SearchProgressListener searchProgressListener; - public interface CliSearchActionBuilderSubtitleProviderStore { - CliSearchActionBuilderIndexingProgressListener subtitleProviderStore( - SubtitleProviderStore subtitleProviderStore); - } - - public interface CliSearchActionBuilderIndexingProgressListener { - CliSearchActionBuilderSearchProgressListener indexingProgressListener( - IndexingProgressListener indexingProgressListener); - } - - public interface CliSearchActionBuilderSearchProgressListener { - CliSearchActionBuilderCLI searchProgressListener(SearchProgressListener searchProgressListener); - } - - public interface CliSearchActionBuilderCLI { - CliSearchActionBuilderFileListAction cli(CLI cli); - } - - public interface CliSearchActionBuilderFileListAction { - CliSearchActionBuilderLanguage fileListAction(FileListAction fileListAction); - } - - public interface CliSearchActionBuilderLanguage { - CliSearchActionBuilderReleaseFactory language(Language language); - } - - public interface CliSearchActionBuilderReleaseFactory { - CliSearchActionBuilderFiltering releaseFactory(ReleaseFactory releaseFactory); - } - - public interface CliSearchActionBuilderFiltering { - CliSearchActionBuilderFolders filtering(@NonNull SubtitleFiltering filtering); - } - - public interface CliSearchActionBuilderFolders { - CliSearchActionBuilderOther folders(List folders); - } - - public interface CliSearchActionBuilderOther { - CliSearchActionBuilderOther overwriteSubtitles(boolean overwriteSubtitles); - - CliSearchActionBuilderOther recursive(boolean recursive); - - CliSearchAction build() throws SearchSetupException; - } - - public static CliSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { - return new CliSearchActionBuilder(settings); - } - - @RequiredArgsConstructor - @Setter - @Accessors(fluent = true, chain = true) - public static class CliSearchActionBuilder - implements CliSearchActionBuilderSearchProgressListener, CliSearchActionBuilderIndexingProgressListener, - CliSearchActionBuilderSubtitleProviderStore, CliSearchActionBuilderCLI, - CliSearchActionBuilderFileListAction, CliSearchActionBuilderLanguage, CliSearchActionBuilderReleaseFactory, - CliSearchActionBuilderFiltering, CliSearchActionBuilderFolders, CliSearchActionBuilderOther { - private final Settings settings; - private SubtitleProviderStore subtitleProviderStore; - private IndexingProgressListener indexingProgressListener; - private SearchProgressListener searchProgressListener; - private CLI cli; - private FileListAction fileListAction; - private Language language; - private ReleaseFactory releaseFactory; - private SubtitleFiltering filtering; - private List folders; - private boolean overwriteSubtitles; - private boolean recursive; - - @Override - public CliSearchAction build() throws SearchSetupException { - return new CliSearchAction(settings, subtitleProviderStore, indexingProgressListener, - searchProgressListener, cli, fileListAction, language, releaseFactory, filtering, folders, - overwriteSubtitles, recursive); - } - } - - private CliSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, + public 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) + FileListAction fileListAction, Language language, ReleaseFactory releaseFactory, SubtitleFiltering filtering, + List folders, boolean overwriteSubtitles=true, boolean recursive=true) throws SearchSetupException { super(settings, subtitleProviderStore); this.indexingProgressListener = indexingProgressListener; 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 258dd60a..09f637e7 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 @@ -4,9 +4,6 @@ import java.util.ArrayList; import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; import org.jspecify.annotations.NonNull; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; @@ -26,50 +23,7 @@ public final class FileGuiSearchAction extends GuiSearchAction searchPanel); - } - - public interface FileGuiSearchActionBuilderReleaseFactory { - FileGuiSearchActionBuilderBuild releaseFactory(ReleaseFactory releaseFactory); - } - - public interface FileGuiSearchActionBuilderBuild { - FileGuiSearchAction build(); - } - - public static FileGuiSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { - return new FileGuiSearchActionBuilder(settings); - } - - @RequiredArgsConstructor - @Setter - @Accessors(chain = true, fluent = true) - public static class FileGuiSearchActionBuilder - implements FileGuiSearchActionBuilderBuild, FileGuiSearchActionBuilderReleaseFactory, - FileGuiSearchActionBuilderSearchPanel, FileGuiSearchActionBuilderGUI, - FileGuiSearchActionBuilderSubtitleProviderStore { - private final Settings settings; - private SubtitleProviderStore subtitleProviderStore; - private GUI mainWindow; - private SearchPanel searchPanel; - private ReleaseFactory releaseFactory; - - @Override - public FileGuiSearchAction build() { - return new FileGuiSearchAction(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); - } - } - - private FileGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, + public FileGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel searchPanel, ReleaseFactory releaseFactory) { super(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); this.filelistAction = new FileListAction(settings); 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 c00ef153..26eefaa7 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 @@ -4,9 +4,6 @@ import java.util.Arrays; import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.exceptions.SearchSetupException; @@ -25,50 +22,7 @@ public final class TextGuiSearchAction extends GuiSearchAction { - public interface TextGuiSearchActionBuilderSubtitleProviderStore { - TextGuiSearchActionBuilderGUI subtitleProviderStore(SubtitleProviderStore subtitleProviderStore); - } - - public interface TextGuiSearchActionBuilderGUI { - TextGuiSearchActionBuilderSearchPanel mainWindow(GUI mainWindow); - } - - public interface TextGuiSearchActionBuilderSearchPanel { - TextGuiSearchActionBuilderReleaseFactory searchPanel(SearchPanel searchPanel); - } - - public interface TextGuiSearchActionBuilderReleaseFactory { - TextGuiSearchActionBuilderBuild releaseFactory(ReleaseFactory releaseFactory); - } - - public interface TextGuiSearchActionBuilderBuild { - TextGuiSearchAction build(); - } - - public static TextGuiSearchActionBuilderSubtitleProviderStore createWithSettings(Settings settings) { - return new TextGuiSearchActionBuilder(settings); - } - - @RequiredArgsConstructor - @Setter - @Accessors(chain = true, fluent = true) - public static class TextGuiSearchActionBuilder - implements TextGuiSearchActionBuilderBuild, TextGuiSearchActionBuilderReleaseFactory, - TextGuiSearchActionBuilderSearchPanel, TextGuiSearchActionBuilderGUI, - TextGuiSearchActionBuilderSubtitleProviderStore { - private final Settings settings; - private SubtitleProviderStore subtitleProviderStore; - private GUI mainWindow; - private SearchPanel searchPanel; - private ReleaseFactory releaseFactory; - - @Override - public TextGuiSearchAction build() { - return new TextGuiSearchAction(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); - } - } - - private TextGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, + public TextGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel searchPanel, ReleaseFactory releaseFactory) { super(settings, subtitleProviderStore, mainWindow, searchPanel, releaseFactory); } 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 c9fd716f..6625fd40 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,8 +10,6 @@ import java.util.stream.Stream; import com.google.common.base.Objects; -import lombok.Setter; -import lombok.experimental.Accessors; import manifold.ext.props.rt.api.val; public class JListWithImages extends JList> { @@ -22,36 +20,13 @@ public class JListWithImages extends JList> { private final Function toStringMapper; private final boolean distinctValues; - private JListWithImages(Function toStringMapper, boolean distinctValues) { - this.toStringMapper = toStringMapper == null ? Object::toString : toStringMapper; + public JListWithImages(Function toStringMapper=Object::toString, boolean distinctValues=true) { + this.toStringMapper = toStringMapper; this.distinctValues = distinctValues; setCellRenderer(new ImageListCellRenderer()); setModel(new DefaultListModel<>()); } - public static JListWithImages forType(Class type) { - return createForType(type).build(); - } - - public static JListWithImagesBuilder createForType(Class type) { - return new JListWithImagesBuilder<>(); - } - - @Setter - @Accessors(chain = true, fluent = true) - public static class JListWithImagesBuilder { - private Function toStringMapper; - private boolean distinctValues; - - public JListWithImagesBuilder distinctValues() { - return distinctValues(true); - } - - public JListWithImages build() { - return new JListWithImages<>(toStringMapper, distinctValues); - } - } - public void addItems(Image image, Collection values) { values.forEach(value -> addItem(image, value)); } 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 041f4e99..2eb0af5f 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 @@ -64,8 +64,7 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { "aligny center, span 1 2"); new JScrollPane() - .viewportView(this.defaultIncomingFoldersList = - JListWithImages.createForType(Path.class).distinctValues().build()) + .viewportView(this.defaultIncomingFoldersList = new JListWithImages<>()) .addTo(settingsPanel, "growx, span, wrap"); new JButton(getText("PreferenceDialog.AddFolder")) @@ -86,8 +85,7 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { .addTo(settingsPanel, "aligny center, span 1 2"); new JScrollPane() - .viewportView(this.excludeList = - JListWithImages.createForType(PathOrRegex.class).distinctValues().build()) + .viewportView(this.excludeList = new JListWithImages<>()) .addTo(settingsPanel, "growx, span, wrap"); Consumer addExcludeItemConsumer = type -> { 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 5a7f59d7..7016aa2d 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 @@ -89,8 +89,7 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { // LOCAL this.chkSourceLocal = new JCheckBox(getText("PreferenceDialog.Local")); JScrollPane scrLocalSources = - new JScrollPane().viewportView(this.localSourcesFoldersList = - JListWithImages.createForType(Path.class).distinctValues().build()); + new JScrollPane().viewportView(this.localSourcesFoldersList = new JListWithImages<>()); JButton btnBrowseLocalSources = new JButton(getText("PreferenceDialog.AddFolder")) .actionListener(() -> MemoryFolderChooser.getInstance() .selectDirectory(this, getText("PreferenceDialog.SelectFolder")) 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 e5c1c323..0939ae85 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 @@ -3,7 +3,9 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.border.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; import java.awt.*; import java.io.Serial; import java.util.LinkedHashMap; @@ -150,16 +152,13 @@ private void addLanguage(Language lang, String langCode) { } private Function getLibraryStructureBuilder() { - return structure -> FilenameLibraryBuilder.builder() - .structure(structure) - .replaceSpace(chkReplaceSpace.isSelected()) - .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedValue()) - .includeLanguageCode(chkIncludeLanguageCode.isSelected()) - .languageTags(languageMapping.toSettingsMap()) - .useTvdbName(false) - .tvdbAdapter(null) - .rename(true) - .build(); + return structure -> new FilenameLibraryBuilder( + structure:structure, + replaceSpace:chkReplaceSpace.isSelected(), + replacingSpaceChar:cbxReplaceSpaceChar.getSelectedValue(), + includeLanguageCode:chkIncludeLanguageCode.isSelected(), + languageTags:languageMapping.toSettingsMap(), + rename:true); } @Override 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 be2656ed..19b371b4 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 @@ -81,15 +81,12 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType } private Function getLibraryStructureBuilder() { - return structure -> PathLibraryBuilder.builder() - .structure(structure) - .replaceSpace(chkReplaceSpace.isSelected()) - .replacingSpaceChar(cbxReplaceSpaceChar.getSelectedValue()) - .useTvdbName(false) - .tvdbAdapter(null) - .libraryFolder(txtLibraryFolder.getObject()) - .move(true) - .build(); + return structure -> new PathLibraryBuilder( + structure:structure, + replaceSpace:chkReplaceSpace.isSelected(), + replacingSpaceChar:cbxReplaceSpaceChar.getSelectedValue(), + libraryFolder:txtLibraryFolder.getObject(), + move:true); } @Override 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 69b9e302..8decfd1d 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 @@ -3,8 +3,6 @@ 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.MovieStructureTag; @@ -27,10 +25,10 @@ public final 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) { - super(useTvdb, tvdbAdapter); + public FilenameLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, + boolean includeLanguageCode, Map languageTags, TheTvdbAdapter tvdbAdapter=null, + boolean rename) { + super(tvdbAdapter); this.structure = structure; this.replaceSpace = replaceSpace; this.replacingSpaceChar = replacingSpaceChar; @@ -39,85 +37,16 @@ private FilenameLibraryBuilder(String structure, boolean replaceSpace, char repl this.rename = rename; } - public static FilenameLibraryBuilder fromSettings(LibrarySettings librarySettings, Manager manager, + public static FilenameLibraryBuilder fromSettings(LibrarySettings libSettings, Manager manager, UserInteractionHandler userInteractionHandler) { - return FilenameLibraryBuilder.builder() - .structure(librarySettings.folderStructure) - .replaceSpace(librarySettings.folderReplaceSpace) - .replacingSpaceChar(librarySettings.folderReplacingSpaceChar) - .includeLanguageCode(librarySettings.includeLanguageCode) - .languageTags(librarySettings.langCodeMap) - .useTvdbName(librarySettings.useTVDBNaming) - .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) - .rename(librarySettings.hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)) - .build(); - } - - public static FilenameLibraryBuilderStructureIntf builder() { - return new FilenameLibraryBuilderBuilder(); - } - - public interface FilenameLibraryBuilderStructureIntf { - FilenameLibraryBuilderReplaceSpaceIntf structure(String structure); - } - - public interface FilenameLibraryBuilderReplaceSpaceIntf { - FilenameLibraryBuilderReplaceSpaceCharIntf replaceSpace(boolean replaceSpace); - } - - public interface FilenameLibraryBuilderReplaceSpaceCharIntf { - FilenameLibraryBuilderIncludeLanguageCodeIntf replacingSpaceChar(char replacingSpaceChar); - } - - public interface FilenameLibraryBuilderIncludeLanguageCodeIntf { - FilenameLibraryBuilderLanguageTagIntf includeLanguageCode(boolean includeLanguageCode); - } - - public interface FilenameLibraryBuilderLanguageTagIntf { - FilenameLibraryBuilderUseTvdbNameIntf languageTags(Map languageTags); - } - - public interface FilenameLibraryBuilderUseTvdbNameIntf extends FilenameLibraryBuilderBuildIntf { - FilenameLibraryBuilderTvdbAdapterIntf useTvdbName(boolean useTvdbName); - } - - public interface FilenameLibraryBuilderTvdbAdapterIntf { - FilenameLibraryBuilderRenameIntf tvdbAdapter(TheTvdbAdapter tvdbAdapter); - } - - public interface FilenameLibraryBuilderRenameIntf { - FilenameLibraryBuilderBuildIntf rename(boolean rename); - } - - public interface FilenameLibraryBuilderBuildIntf { - FilenameLibraryBuilder build(); - } - - @Setter - @Accessors(chain = true, fluent = true) - public static class FilenameLibraryBuilderBuilder - implements FilenameLibraryBuilderStructureIntf, FilenameLibraryBuilderReplaceSpaceIntf, - FilenameLibraryBuilderReplaceSpaceCharIntf, FilenameLibraryBuilderIncludeLanguageCodeIntf, - FilenameLibraryBuilderLanguageTagIntf, FilenameLibraryBuilderUseTvdbNameIntf, - FilenameLibraryBuilderTvdbAdapterIntf, FilenameLibraryBuilderRenameIntf, FilenameLibraryBuilderBuildIntf { - private String structure; - - private boolean replaceSpace; - private char replacingSpaceChar; - - private boolean includeLanguageCode; - private Map languageTags; - - private boolean useTvdbName; - private TheTvdbAdapter tvdbAdapter; - - private boolean rename; - - @Override - public FilenameLibraryBuilder build() { - return new FilenameLibraryBuilder(structure, replaceSpace, replacingSpaceChar, includeLanguageCode, - languageTags, useTvdbName, tvdbAdapter, rename); - } + return new FilenameLibraryBuilder( + structure:libSettings.folderStructure, + replaceSpace:libSettings.folderReplaceSpace, + replacingSpaceChar:libSettings.folderReplacingSpaceChar, + includeLanguageCode:libSettings.includeLanguageCode, + languageTags:libSettings.langCodeMap, + tvdbAdapter:libSettings.useTVDBNaming ? TheTvdbAdapter.getInstance(manager, userInteractionHandler) : null, + rename:libSettings.hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVEANDRENAME)); } @Override 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 7c455bbd..548be19a 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 @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; 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; @@ -14,13 +15,12 @@ @RequiredArgsConstructor public abstract sealed class LibraryBuilder permits FilenameLibraryBuilder, PathLibraryBuilder { - private final boolean useTvdb; - private final TheTvdbAdapter tvdbAdapter; + private final @Nullable TheTvdbAdapter tvdbAdapter; public abstract Path build(Release release); protected String getShowName(String name) { - return useTvdb ? tvdbAdapter.getSerie(name).map(TheTvdbSerie::getSerieName).orElse(name) : name; + return tvdbAdapter != null ? tvdbAdapter.getSerie(name).map(TheTvdbSerie::getSerieName).orElse(name) : name; } protected String replace(String structure, StructureTag tag, String value) { 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 dfb3960a..c2c51963 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,8 +3,6 @@ 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; @@ -24,9 +22,9 @@ public final 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) { - super(useTvdb, tvdbAdapter); + public PathLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, + TheTvdbAdapter tvdbAdapter=null, Path libraryFolder, boolean move) { + super(tvdbAdapter); this.structure = structure; this.replaceSpace = replaceSpace; this.replacingSpaceChar = replacingSpaceChar; @@ -34,79 +32,15 @@ private PathLibraryBuilder(String structure, boolean replaceSpace, char replacin this.move = move; } - public static PathLibraryBuilder fromSettings(LibrarySettings librarySettings, Manager manager, + public static PathLibraryBuilder fromSettings(LibrarySettings libSettings, Manager manager, UserInteractionHandler userInteractionHandler) { - return PathLibraryBuilder.builder() - .structure(librarySettings.folderStructure) - .replaceSpace(librarySettings.folderReplaceSpace) - .replacingSpaceChar(librarySettings.folderReplacingSpaceChar) - .useTvdbName(librarySettings.useTVDBNaming) - .tvdbAdapter(TheTvdbAdapter.getInstance(manager, userInteractionHandler)) - .libraryFolder(librarySettings.folder) - .move(librarySettings.hasAnyLibraryAction(LibraryActionType.MOVE, LibraryActionType.MOVEANDRENAME)) - .build(); - } - - public static PathLibraryBuilderStructureIntf builder() { - return new PathLibraryBuilderBuilder(); - } - - public interface PathLibraryBuilderStructureIntf { - PathLibraryBuilderReplaceSpaceIntf structure(String structure); - } - - public interface PathLibraryBuilderReplaceSpaceIntf { - PathLibraryBuilderReplaceSpaceCharIntf replaceSpace(boolean replaceSpace); - } - - public interface PathLibraryBuilderReplaceSpaceCharIntf { - PathLibraryBuilderUseTvdbNameIntf replacingSpaceChar(char replacingSpaceChar); - } - - public interface PathLibraryBuilderUseTvdbNameIntf extends PathLibraryBuilderBuildIntf { - PathLibraryBuilderTvdbAdapterIntf useTvdbName(boolean useTvdbName); - } - - public interface PathLibraryBuilderTvdbAdapterIntf { - PathLibraryBuilderLibraryFolderIntf tvdbAdapter(TheTvdbAdapter tvdbAdapter); - } - - public interface PathLibraryBuilderLibraryFolderIntf { - PathLibraryBuilderMoveIntf libraryFolder(Path libraryFolder); - } - - public interface PathLibraryBuilderMoveIntf { - PathLibraryBuilderBuildIntf move(boolean move); - } - - public interface PathLibraryBuilderBuildIntf { - PathLibraryBuilder build(); - } - - @Setter - @Accessors(chain = true, fluent = true) - public static class PathLibraryBuilderBuilder - implements PathLibraryBuilderStructureIntf, PathLibraryBuilderReplaceSpaceIntf, - PathLibraryBuilderReplaceSpaceCharIntf, PathLibraryBuilderUseTvdbNameIntf, - PathLibraryBuilderTvdbAdapterIntf, PathLibraryBuilderLibraryFolderIntf, PathLibraryBuilderMoveIntf, - PathLibraryBuilderBuildIntf { - private String structure; - - private boolean replaceSpace; - private char replacingSpaceChar; - - private boolean useTvdbName; - private TheTvdbAdapter tvdbAdapter; - - private Path libraryFolder; - - private boolean move; - - @Override - public PathLibraryBuilder build() { - return new PathLibraryBuilder(structure, replaceSpace, replacingSpaceChar, useTvdbName, tvdbAdapter, - libraryFolder, move); - } + return new PathLibraryBuilder( + structure:libSettings.folderStructure, + replaceSpace:libSettings.folderReplaceSpace, + replacingSpaceChar:libSettings.folderReplacingSpaceChar, + tvdbAdapter:libSettings.useTVDBNaming ? TheTvdbAdapter.getInstance(manager, userInteractionHandler) : null, + libraryFolder:libSettings.folder, + move:libSettings.hasAnyLibraryAction(LibraryActionType.MOVE, LibraryActionType.MOVEANDRENAME)); } @Override diff --git a/pom.xml b/pom.xml index 144f2e81..45b8f335 100644 --- a/pom.xml +++ b/pom.xml @@ -151,6 +151,11 @@ manifold-tuple-rt ${manifold.version} + + systems.manifold + manifold-params-rt + ${manifold.version} + org.projectlombok lombok From ebcee691a9de946ccfb3281511241b61c38b3453 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 16 Apr 2025 23:00:29 +0200 Subject: [PATCH 07/58] use optional parameters (2) --- .../gui/dialog/RenameDialog.java | 11 +- .../gui/extra/BoxModelProperties.java | 29 ++++ .../gui/extra/TitlePanel.java | 154 ++++-------------- .../gui/panels/preference/GeneralPanel.java | 10 +- .../gui/panels/preference/OptionsPanel.java | 19 ++- .../preference/SerieProvidersPanel.java | 4 +- .../panels/preference/StructureFilePanel.java | 12 +- .../preference/StructureFolderPanel.java | 12 +- .../preference/SubtitleBackupPanel.java | 11 +- .../panels/preference/VideoLibraryPanel.java | 10 +- 10 files changed, 114 insertions(+), 158 deletions(-) create mode 100644 MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java 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 6b9bed93..d8f411b2 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 @@ -19,8 +19,9 @@ 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.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldPath; import org.lodder.subtools.multisubdownloader.gui.panels.preference.EpisodeLibraryPanel; @@ -52,10 +53,10 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String setBounds(100, 100, 650, 680); contentPane.setLayout(new MigLayout("fill, nogrid", "[]", "[][]20:push[]")); - TitlePanel.title(Messages.getText("PreferenceDialog.Settings")) - .padding(0) - .paddingLeft(20) - .fillContents(true) + new TitlePanelBuilder( + title:getText("PreferenceDialog.Settings"), + padding:new BoxModelProperties(0, 20, 0, 0), + fillContents:true) .addTo(contentPane, "span, grow, wrap") .addComponent("shrink", new JLabel(Messages.getText("PreferenceDialog.Location"))) .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().columns(20)) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java new file mode 100644 index 00000000..e269b6b4 --- /dev/null +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java @@ -0,0 +1,29 @@ +package org.lodder.subtools.multisubdownloader.gui.extra; + +import manifold.ext.props.rt.api.val; + +public class BoxModelProperties { + @val Integer top; + @val Integer left; + @val Integer bottom; + @val Integer right; + + public BoxModelProperties(Integer top=null, Integer left=null, Integer bottom=null, Integer right=null) { + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; + } + + public BoxModelProperties(int padding) { + this(padding, padding, padding, padding); + } + + public String getInsets() { + return "insets %s %s %s %s".formatted(getPadding(top), getPadding(left), getPadding(bottom), getPadding(right)); + } + + private String getPadding(Integer padding) { + return padding == null ? "n" : String.valueOf(padding); + } +} 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 9ddd22ee..da083e7b 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 @@ -4,9 +4,6 @@ import java.awt.*; import java.io.Serial; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; import manifold.ext.props.rt.api.var; import net.miginfocom.swing.MigLayout; @@ -15,139 +12,48 @@ public class TitlePanel extends JPanel { private static final long serialVersionUID = 1L; @var JPanel panel; - 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)); - super.add(new JSeparator(), "growx, gapy 6, wrap"); - super.add(this.panel = new JPanel(panelLayout), "growx, span"); - } - - private static String getPadding(int padding) { - return padding == -1 ? "n" : String.valueOf(padding); - } - - public static BuilderOtherIntf title(String title) { - return new Builder(title); - } - - public interface BuilderOtherIntf { - BuilderOtherIntf useGrid(); - - BuilderOtherIntf fillContents(boolean fillContents); - - BuilderOtherIntf margin(int margin); - - BuilderOtherIntf margin(int top, int left, int bottom, int right); - - BuilderOtherIntf marginTop(int top); - - BuilderOtherIntf marginLeft(int left); - - BuilderOtherIntf marginBottom(int bottom); - - BuilderOtherIntf marginRight(int right); - - BuilderOtherIntf marginSides(int marginSide); - - BuilderOtherIntf panelLayout(LayoutManager panelLayout); - - BuilderOtherIntf panelColumnConstraints(String panelColumnConstraints); - - BuilderOtherIntf padding(int padding); - - BuilderOtherIntf padding(int top, int left, int bottom, int right); - - BuilderOtherIntf paddingTop(int top); - - BuilderOtherIntf paddingLeft(int left); - - BuilderOtherIntf paddingBottom(int bottom); + public static class TitlePanelBuilder { - BuilderOtherIntf paddingRight(int right); - - BuilderOtherIntf paddingSides(int paddingSide); - - JPanel addTo(Container component); - - JPanel addTo(Container component, Object constraints); - } - - @RequiredArgsConstructor - @Setter - @Accessors(chain = true, fluent = true) - public static class Builder implements - BuilderOtherIntf { private final String title; - private boolean useGrid; - private boolean fillContents = true; - private int marginTop = -1; - private int marginLeft = -1; - private int marginBottom = -1; - private int marginRight = -1; - private int paddingTop = -1; - private int paddingLeft = -1; - private int paddingBottom = -1; - private int paddingRight = -1; - private LayoutManager panelLayout; - private String panelColumnConstraints = ""; - - @Override - public Builder useGrid() { - this.useGrid = true; - return this; - } - - @Override - public Builder margin(int margin) { - return margin(margin, margin, margin, margin); - } - - @Override - public Builder margin(int top, int left, int bottom, int right) { - return marginTop(top).marginLeft(left).marginBottom(bottom).marginRight(right); - } - - @Override - public Builder marginSides(int marginSide) { - return marginLeft(marginSide).marginRight(marginSide); + private final boolean useGrid; + private final boolean fillContents; + private final BoxModelProperties margin; + private final BoxModelProperties padding; + private final LayoutManager panelLayout; + private final String panelColumnConstraints; + + public TitlePanelBuilder(String title, boolean useGrid=false, boolean fillContents=true, + BoxModelProperties margin=new BoxModelProperties(), + BoxModelProperties padding=new BoxModelProperties(), LayoutManager panelLayout=null, + String panelColumnConstraints="") { + this.title = title; + this.useGrid = useGrid; + this.fillContents = fillContents; + this.margin = margin; + this.padding = padding; + this.panelLayout = panelLayout; + this.panelColumnConstraints = panelColumnConstraints; } - @Override - public Builder padding(int padding) { - return padding(padding, padding, padding, padding); - } - - @Override - public Builder padding(int top, int left, int bottom, int right) { - return paddingTop(top).paddingLeft(left).paddingBottom(bottom).paddingRight(right); - } - - @Override - public Builder paddingSides(int paddingSide) { - return paddingLeft(paddingSide).paddingRight(paddingSide); - } - - @Override public JPanel addTo(Container component) { return addTo(component, ""); } - @Override 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), - getPadding(paddingLeft), getPadding(paddingBottom), getPadding(paddingRight)), - panelColumnConstraints); - } + LayoutManager panelLayoutNew = panelLayout != null ? panelLayout : new MigLayout( + (fillContents ? "fill," : "") + (useGrid ? "" : "nogrid,") + padding.getInsets(), + panelColumnConstraints); TitlePanel titlePanel = - new TitlePanel(title, panelLayout, marginTop, marginLeft, marginBottom, marginRight); + new TitlePanel(title, panelLayoutNew, margin); component.add(titlePanel, constraints); return titlePanel.panel; - } } + + private TitlePanel(String title, LayoutManager panelLayout, BoxModelProperties margin) { + super(new MigLayout("fillx, nogrid, " + margin.getInsets())); + super.add(new JLabel(title)); + super.add(new JSeparator(), "growx, gapy 6, wrap"); + super.add(this.panel = new JPanel(panelLayout), "growx, span"); + } } 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 2eb0af5f..7cbd8419 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 @@ -12,11 +12,12 @@ import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; +import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; 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.extra.TitlePanel.TitlePanelBuilder; 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; @@ -46,8 +47,11 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { this.gui = gui; this.settingsCtrl = settingsCtrl; - JPanel settingsPanel = TitlePanel.title(getText("PreferenceDialog.Settings")) - .padding(0).paddingLeft(20).useGrid().fillContents(false) + JPanel settingsPanel = new TitlePanelBuilder( + title:getText("PreferenceDialog.Settings"), + padding:new BoxModelProperties(0, 20, 0, 0), + useGrid:true, + fillContents:false) .addTo(this, "span, grow, wrap"); { 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 776998b3..ebbcb510 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 @@ -6,8 +6,9 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.SettingsProcessEpisodeSource; @@ -33,10 +34,10 @@ public OptionsPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - TitlePanel.title(getText("PreferenceDialog.DownloadOptions")) - .marginBottom(0) - .padding(0) - .paddingLeft(20) + new TitlePanelBuilder( + title:getText("PreferenceDialog.DownloadOptions"), + margin:new BoxModelProperties(null, null, 0, null), + padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow, wrap") .addComponent(this.chkAlwaysConfirm = new JCheckBox(getText("PreferenceDialog.CheckBeforeDownloading")), "wrap") @@ -52,10 +53,10 @@ public OptionsPanel(SettingsControl settingsCtrl) { .build() .addComponent(this.pnlDefaultSelection = new DefaultSelectionPanel(settingsCtrl))); - TitlePanel.title(getText("PreferenceDialog.SearchFilter")) - .marginBottom(0) - .padding(0) - .paddingLeft(20) + new TitlePanelBuilder( + title:getText("PreferenceDialog.SearchFilter"), + margin:new BoxModelProperties(null, null, 0, null), + padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow, wrap") .addComponent( this.chkSubtitleExactMethod = new JCheckBox(getText("PreferenceDialog.SearchFilterExact")), 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 7016aa2d..1bfb6887 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 @@ -12,7 +12,7 @@ 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.extra.TitlePanel.TitlePanelBuilder; 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; @@ -44,7 +44,7 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.SelectPreferredSources")) + JPanel titlePanel = new TitlePanelBuilder(title:getText("PreferenceDialog.SelectPreferredSources")) .addTo(this, "span, grow"); { 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 0939ae85..4c83db0c 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 @@ -21,8 +21,9 @@ 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.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; 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; @@ -49,11 +50,10 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.RenameFiles")) - .margin(0) - .padding(0) - .marginLeft(20) - .paddingLeft(20) + JPanel titlePanel = new TitlePanelBuilder( + title:getText("PreferenceDialog.RenameFiles"), + margin:new BoxModelProperties(0, 20, 0, 0), + padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow"); new JLabel(getText("PreferenceDialog.Structure")).addTo(titlePanel, "shrink"); 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 19b371b4..f44ec150 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 @@ -7,7 +7,9 @@ import java.util.function.Function; import net.miginfocom.swing.MigLayout; +import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.gui.dialog.StructureBuilderDialog; +import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; @@ -36,9 +38,13 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.MoveToLibrary")) - .margin(0).padding(0).marginLeft(20).paddingLeft(20).useGrid() - .panelColumnConstraints("[shrink][grow][shrink]").addTo(this, "span, grow"); + JPanel titlePanel = new TitlePanel.TitlePanelBuilder( + title:Messages.getText("PreferenceDialog.MoveToLibrary"), + margin:new BoxModelProperties(0, 20, 0, 0), + padding:new BoxModelProperties(0, 20, 0, 0), + useGrid:true, + panelColumnConstraints:"[shrink][grow][shrink]") + .addTo(this, "span, grow"); new JLabel(getText("PreferenceDialog.Location")).addTo(titlePanel, "shrink"); this.txtLibraryFolder = 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 7466e11b..4c6d712f 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 @@ -6,6 +6,8 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.lodder.subtools.multisubdownloader.Messages; +import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; @@ -25,9 +27,12 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { super(new MigLayout("insets 0, fillx, nogrid")); this.librarySettings = librarySettings; - JPanel titlePanel = TitlePanel.title(getText("PreferenceDialog.SubtitlesBackup")) - .margin(0).padding(0).paddingLeft(20).addTo(this, "span, growx"); - + JPanel titlePanel = new TitlePanel.TitlePanelBuilder( + title:Messages.getText("PreferenceDialog.SubtitlesBackup"), + margin:new BoxModelProperties(0), + padding:new BoxModelProperties(0, 20, 0, 0)) + .addTo(this, "span, growx"); + { this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); 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 ec2070b7..ca36f4ec 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 @@ -9,8 +9,9 @@ import lombok.experimental.ExtensionMethod; import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PartialDisableComboBox; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; @@ -39,8 +40,11 @@ public abstract sealed class VideoLibraryPanel extends JPanel implements Prefere this.pnlBackup = renameMode ? null : new SubtitleBackupPanel(librarySettings).addTo(this, "wrap, span, growx"); - JPanel performActionPanel = TitlePanel.title(getText("PreferenceDialog.PerformActions")) - .margin(0).padding(0).paddingLeft(20).addTo(this, "span, growx"); + JPanel performActionPanel = new TitlePanelBuilder( + title:getText("PreferenceDialog.PerformActions"), + margin:new BoxModelProperties(0), + padding:new BoxModelProperties(0, 20, 0, 0)) + .addTo(this, "span, growx"); { this.chkUseTVDBNaming = new JCheckBox(getText("PreferenceDialog.UseTvdbName")).visible( From ed5f3407ca9d4518881e0de24e1ee8c85740cc12 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 17 Apr 2025 21:43:11 +0200 Subject: [PATCH 08/58] use optional parameters (3) --- .../addic7ed/JAddic7edApi.java | 15 +++++---------- .../model/Addic7edSubtitleDescriptor.java | 18 ++---------------- 2 files changed, 7 insertions(+), 26 deletions(-) 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 bb79f2bf..7a1fecb8 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 @@ -168,13 +168,9 @@ public List getSubtitles(SerieMapping addic7edSerieM } } if (lang != null && download != null && title != null) { - Addic7edSubtitleDescriptor sub = - new Addic7edSubtitleDescriptor().setUploader(uploader) - .setTitle(title.trim()) - .setVersion(version.trim()) - .setUrl(download) - .setLanguage(Language.fromValueOptional(lang.trim()).orElse(null)) - .setHearingImpaired(hearingImpaired); + Addic7edSubtitleDescriptor sub = new Addic7edSubtitleDescriptor(version.trim(), + Language.fromValueOptional(lang.trim()).orElse(null), download, title.trim(), + uploader, hearingImpaired); if (!isDuplicate(lSubtitles, sub)) { lSubtitles.add(sub); } @@ -190,9 +186,8 @@ public List getSubtitles(SerieMapping addic7edSerieM } 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())); + return lSubtitles.stream().anyMatch(s -> s.language == sub.language && StringUtils.equals(s.url, sub.url) && + StringUtils.equals(s.version, sub.version)); } private Document getContent(String url) throws Addic7edException { 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 fa474494..bbe2c0f1 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,21 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.model; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; import org.lodder.subtools.sublibrary.Language; -@EqualsAndHashCode -@Accessors(chain = true) -@Getter -@Setter -public class Addic7edSubtitleDescriptor { - - private String version; - private Language language; - private String url; - private String title; - private String uploader; - private boolean hearingImpaired; +public record Addic7edSubtitleDescriptor(String version, Language language, String url, String title, String uploader, + boolean hearingImpaired) { } From a39d391661e4e4ddca1eccad413e58e5c0e2f659 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 17 Apr 2025 21:53:29 +0200 Subject: [PATCH 09/58] changes --- .../util/prompter/PrompterBuilderValueFromList.java | 13 ++++++------- .../prompter/PrompterBuilderValuesFromList.java | 12 ++++++------ .../sublibrary/util/prompter/PrompterUtil.java | 9 ++++----- 3 files changed, 16 insertions(+), 18 deletions(-) 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 c4d66411..1133b536 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 @@ -1,8 +1,6 @@ package org.lodder.subtools.sublibrary.util.prompter; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -10,6 +8,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.google.common.collect.Lists; import lombok.Setter; import lombok.experimental.Accessors; import org.apache.commons.lang3.StringUtils; @@ -26,12 +25,12 @@ private PrompterBuilderValueFromList() { // Interface \\ // --------- \\ - public static ValueFromListPromptBuilderIntf getStringFromList(Collection elements) { + public static ValueFromListPromptBuilderIntf getStringFromList(Iterable elements) { return getElementFromList(elements).toStringMapper(Function.identity()); } - public static ValueFromListToStringMapperBuilderIntf getElementFromList(Collection elements) { - return new ValueFromListBuilder<>(new ArrayList<>(elements)); + public static ValueFromListToStringMapperBuilderIntf getElementFromList(Iterable elements) { + return new ValueFromListBuilder<>(elements); } public static ValueFromListToStringMapperBuilderIntf getElementFromList(T[] elements) { @@ -71,8 +70,8 @@ public static class ValueFromListBuilder private T emptyValue; private TableDisplayer tableDisplayer; - ValueFromListBuilder(List elements) { - this.elements = elements; + ValueFromListBuilder(Iterable elements) { + this.elements = Lists.newArrayList(elements); } @Override 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 1b846c87..9db84523 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 @@ -2,13 +2,13 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.google.common.collect.Lists; import lombok.Setter; import lombok.experimental.Accessors; import org.apache.commons.lang3.StringUtils; @@ -25,12 +25,12 @@ private PrompterBuilderValuesFromList() { // Interface \\ // --------- \\ - public static ValuesFromListPromptBuilderIntf getStringsFromList(Collection elements) { + public static ValuesFromListPromptBuilderIntf getStringsFromList(Iterable elements) { return getElementsFromList(elements).toStringMapper(Function.identity()); } - public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(Collection elements) { - return new ValuesFromListBuilder<>(new ArrayList<>(elements)); + public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(Iterable elements) { + return new ValuesFromListBuilder<>(elements); } public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(T[] elements) { @@ -67,8 +67,8 @@ public static class ValuesFromListBuilder private boolean includeNull; private TableDisplayer tableDisplayer; - ValuesFromListBuilder(List elements) { - this.elements = new ArrayList<>(elements); + ValuesFromListBuilder(Iterable elements) { + this.elements = Lists.newArrayList(elements); } @Override 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 b6d9c9a1..86ca8545 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 @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.util.prompter; -import java.util.Collection; import java.util.function.Function; import org.codehaus.plexus.components.interactivity.Prompter; @@ -81,11 +80,11 @@ public static ValueBuilderOtherMapperIntf getString() { // ## Get Value From List ## \\ // ######################### \\ - public static ValueFromListPromptBuilderIntf getStringFromList(Collection elements) { + public static ValueFromListPromptBuilderIntf getStringFromList(Iterable elements) { return PrompterBuilderValueFromList.getElementFromList(elements).toStringMapper(Function.identity()); } - public static ValueFromListToStringMapperBuilderIntf getElementFromList(Collection elements) { + public static ValueFromListToStringMapperBuilderIntf getElementFromList(Iterable elements) { return PrompterBuilderValueFromList.getElementFromList(elements); } @@ -97,11 +96,11 @@ public static ValueFromListToStringMapperBuilderIntf getElementFromList(T // ## Get Values From List ## \\ // ########################## \\ - public static ValuesFromListPromptBuilderIntf getStringsFromList(Collection elements) { + public static ValuesFromListPromptBuilderIntf getStringsFromList(Iterable elements) { return PrompterBuilderValuesFromList.getStringsFromList(elements); } - public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(Collection elements) { + public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(Iterable elements) { return PrompterBuilderValuesFromList.getElementsFromList(elements); } From d541c546c7d403dde42911a8b82e3cf8ec475d97 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 17 Apr 2025 23:03:30 +0200 Subject: [PATCH 10/58] use optional parameters (4) --- .../actions/search/TextGuiSearchAction.java | 2 +- .../sublibrary/control/ReleaseParser.java | 15 +++--- .../sublibrary/model/MovieRelease.java | 46 ++----------------- 3 files changed, 11 insertions(+), 52 deletions(-) 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 26eefaa7..8490f997 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 @@ -50,7 +50,7 @@ protected List createReleases() { .episode(inputPanel.episode) .quality(inputPanel.quality) .build(); - case MOVIE -> MovieRelease.builder().name(name).quality(inputPanel.quality).build(); + case MOVIE -> new MovieRelease(name:name, quality:inputPanel.quality); default -> releaseFactory.createRelease(Path.of( name + (Arrays.stream(VideoExtensions.values()).anyMatch(ext -> name.endsWith("." + ext)) ? "" : ".")), 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 6fe2ed79..8573e876 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 @@ -117,14 +117,13 @@ private Release parsePatternResult(Path file, String fileParseName, String exten if (StringUtils.equals(parserResults.parts.first, fileParseName)) { throw new ReleaseParseException("Could not parse " + fileParseName); } - return MovieRelease.builder() - .name(cleanUnwantedChars(parserResults.parts.first)) - .file(file) - .year(parserResults.getNamedMatchValue(YEAR)) - .releaseGroup(releaseGroup) - .quality(StringUtils.toRootLowerCase(quality)) - .extension(extension) - .build(); + return new MovieRelease( + name:cleanUnwantedChars(parserResults.parts.first), + file:file, + year:parserResults.getNamedMatchValue(YEAR), + releaseGroup:releaseGroup, + quality:StringUtils.toRootLowerCase(quality), + extension:extension); } } } 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 fb654720..e7e13d18 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,57 +3,17 @@ import java.nio.file.Path; import java.util.OptionalInt; -import lombok.Setter; -import lombok.experimental.Accessors; import manifold.ext.props.rt.api.var; public final class MovieRelease extends Release { @var String name; - @var Integer year; + @var int year; private int imdbId; private int tvdbId; - public interface MovieReleaseBuilderName { - MovieReleaseBuilderOther name(String name); - } - - public interface MovieReleaseBuilderOther { - MovieReleaseBuilderOther file(Path file); - - MovieReleaseBuilderOther quality(String quality); - - MovieReleaseBuilderOther releaseGroup(String releaseGroup); - - MovieReleaseBuilderOther year(Integer year); - - MovieReleaseBuilderOther extension(String extension); - - MovieRelease build(); - } - - public static MovieReleaseBuilderName builder() { - return new MovieReleaseBuilder(); - } - - @Setter - @Accessors(chain = true, fluent = true) - public static class MovieReleaseBuilder implements MovieReleaseBuilderOther, MovieReleaseBuilderName { - private String name; - private Integer year; - - private String quality; - private Path file; - private String releaseGroup; - private String extension; - - @Override - public MovieRelease build() { - return new MovieRelease(file, releaseGroup, quality, extension, name, year == null ? 0 : year); - } - } - - private MovieRelease(Path file, String releaseGroup, String quality, String extension, String name, int year) { + public MovieRelease(String name, Path file=null, String releaseGroup=null, String quality=null, + String extension=null, int year=0) { super(VideoType.MOVIE, file, releaseGroup, quality, extension); this.name = name; this.year = year; From 1243f9c897fbc25844c40a372862bd1b3b18277a Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 21:17:03 +0200 Subject: [PATCH 11/58] small change --- .../gui/actions/search/TextGuiSearchAction.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 8490f997..077a6769 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 @@ -1,7 +1,6 @@ package org.lodder.subtools.multisubdownloader.gui.actions.search; import java.nio.file.Path; -import java.util.Arrays; import java.util.List; import org.lodder.subtools.multisubdownloader.GUI; @@ -52,8 +51,7 @@ protected List createReleases() { .build(); case MOVIE -> new MovieRelease(name:name, quality:inputPanel.quality); default -> releaseFactory.createRelease(Path.of( - name + (Arrays.stream(VideoExtensions.values()).anyMatch(ext -> name.endsWith("." + ext)) ? "" : - ".")), + name + (VideoExtensions.values().stream().anyMatch(ext -> name.endsWith("." + ext)) ? "" : ".")), userInteractionHandler); }; return release != null ? List.of(release) : List.of(); From 424afd49fb87238f8299ea438cfb450fbf4b1f91 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 21:17:20 +0200 Subject: [PATCH 12/58] small typo --- .../org/lodder/subtools/sublibrary/control/ReleaseParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8573e876..7364700a 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 @@ -99,7 +99,7 @@ private Release parsePatternResult(Path file, String fileParseName, String exten if (parserResults.containsNone(SEASON, EPISODES_TEXT)) { if (parserResults.containsNone(ARABIC_NUMBER, ROMAN_NUMBER)) { - // If the season and episode numbers are not found, and neither are the Arabic or Roman numbers, try to + // If the season and episode numbers are not found, and neither are the Arabic nor Roman numbers, try to // parse the season + episode numbers (using less 'safe' formats 'see' and 's_ee'), and the title parserResults.parse(name_season_episode_title_Regex(SeasonEpisodeType.X_XX), name_season_episode_title_Regex(SeasonEpisodeType.XXX), From 7d0048f4ee2cd7711b82301a4831cfd54ae2b05c Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 21:44:37 +0200 Subject: [PATCH 13/58] fix --- .../org/lodder/subtools/sublibrary/model/MovieRelease.java | 4 ++-- .../subtools/sublibrary/assertions/MovieReleaseAssert.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 e7e13d18..e9b47267 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 @@ -8,12 +8,12 @@ public final class MovieRelease extends Release { @var String name; - @var int year; + @var Integer year; private int imdbId; private int tvdbId; public MovieRelease(String name, Path file=null, String releaseGroup=null, String quality=null, - String extension=null, int year=0) { + String extension=null, Integer year=null) { super(VideoType.MOVIE, file, releaseGroup, quality, extension); this.name = name; this.year = year; diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java index c422dd1a..5db82aed 100644 --- a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/MovieReleaseAssert.java @@ -20,7 +20,7 @@ public MovieReleaseAssert(MovieRelease actual) { public @Self MovieReleaseAssert withoutYear() { isNotNull(); - assertThat(actual.year).isZero(); + assertThat(actual.year).isNull(); return this; } From f98094cc3603f71bdd200502f2574b54c4e2bd46 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 22:46:58 +0200 Subject: [PATCH 14/58] use optional parameters (5) --- .../gui/dialog/RenameDialog.java | 6 ++- .../gui/extra/TitlePanel.java | 49 ++++--------------- .../gui/panels/preference/GeneralPanel.java | 22 ++++----- .../gui/panels/preference/OptionsPanel.java | 30 ++++++------ .../preference/SerieProvidersPanel.java | 4 +- .../panels/preference/StructureFilePanel.java | 4 +- .../preference/StructureFolderPanel.java | 2 +- .../preference/SubtitleBackupPanel.java | 2 +- .../panels/preference/VideoLibraryPanel.java | 4 +- 9 files changed, 47 insertions(+), 76 deletions(-) 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 d8f411b2..49d3832a 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 static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.*; import java.awt.*; import java.beans.PropertyChangeEvent; @@ -21,7 +23,7 @@ import org.lodder.subtools.multisubdownloader.actions.RenameAction; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldPath; import org.lodder.subtools.multisubdownloader.gui.panels.preference.EpisodeLibraryPanel; @@ -53,7 +55,7 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String setBounds(100, 100, 650, 680); contentPane.setLayout(new MigLayout("fill, nogrid", "[]", "[][]20:push[]")); - new TitlePanelBuilder( + new TitlePanel( title:getText("PreferenceDialog.Settings"), padding:new BoxModelProperties(0, 20, 0, 0), fillContents:true) 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 da083e7b..e9917a84 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 @@ -12,48 +12,17 @@ public class TitlePanel extends JPanel { private static final long serialVersionUID = 1L; @var JPanel panel; - public static class TitlePanelBuilder { - - private final String title; - private final boolean useGrid; - private final boolean fillContents; - private final BoxModelProperties margin; - private final BoxModelProperties padding; - private final LayoutManager panelLayout; - private final String panelColumnConstraints; - - public TitlePanelBuilder(String title, boolean useGrid=false, boolean fillContents=true, - BoxModelProperties margin=new BoxModelProperties(), - BoxModelProperties padding=new BoxModelProperties(), LayoutManager panelLayout=null, - String panelColumnConstraints="") { - this.title = title; - this.useGrid = useGrid; - this.fillContents = fillContents; - this.margin = margin; - this.padding = padding; - this.panelLayout = panelLayout; - this.panelColumnConstraints = panelColumnConstraints; - } - - public JPanel addTo(Container component) { - return addTo(component, ""); - } - - public JPanel addTo(Container component, Object constraints) { - LayoutManager panelLayoutNew = panelLayout != null ? panelLayout : new MigLayout( - (fillContents ? "fill," : "") + (useGrid ? "" : "nogrid,") + padding.getInsets(), - panelColumnConstraints); - TitlePanel titlePanel = - new TitlePanel(title, panelLayoutNew, margin); - component.add(titlePanel, constraints); - return titlePanel.panel; - } - } - - private TitlePanel(String title, LayoutManager panelLayout, BoxModelProperties margin) { + public TitlePanel(String title, boolean useGrid=false, boolean fillContents=true, + BoxModelProperties margin=new BoxModelProperties(), + BoxModelProperties padding=new BoxModelProperties(), LayoutManager panelLayout=null, + String panelColumnConstraints="") { super(new MigLayout("fillx, nogrid, " + margin.getInsets())); super.add(new JLabel(title)); super.add(new JSeparator(), "growx, gapy 6, wrap"); - super.add(this.panel = new JPanel(panelLayout), "growx, span"); + + LayoutManager panelLayoutNew = panelLayout != null ? panelLayout : new MigLayout( + (fillContents ? "fill," : "") + (useGrid ? "" : "nogrid,") + padding.getInsets(), + panelColumnConstraints); + super.add(this.panel = new JPanel(panelLayoutNew), "growx, span"); } } 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 7cbd8419..eb03e53d 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 @@ -17,7 +17,7 @@ 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.TitlePanelBuilder; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; 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; @@ -47,7 +47,7 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { this.gui = gui; this.settingsCtrl = settingsCtrl; - JPanel settingsPanel = new TitlePanelBuilder( + JPanel settingsPanel = new TitlePanel( title:getText("PreferenceDialog.Settings"), padding:new BoxModelProperties(0, 20, 0, 0), useGrid:true, @@ -122,11 +122,11 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { { - JPanel updatePanel = TitlePanel.title(getText("PreferenceDialog.Update")) - .padding(0) - .paddingLeft(20) - .useGrid() - .fillContents(false) + JPanel updatePanel = new TitlePanel( + title:getText("PreferenceDialog.Update"), + padding:new BoxModelProperties(0, 20, 0, 0), + useGrid:true, + fillContents:false) .addTo(this, "span, grow, wrap"); new JLabel(getText("PreferenceDialog.NewUpdateCheck")).addTo(updatePanel); @@ -141,10 +141,10 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { { - JPanel proxyPanel = TitlePanel.title(getText("PreferenceDialog.ConfigureProxy")) - .padding(0) - .paddingLeft(20) - .fillContents(false) + JPanel proxyPanel = new TitlePanel( + title:getText("PreferenceDialog.ConfigureProxy"), + padding:new BoxModelProperties(0, 20, 0, 0), + fillContents:false) .addTo(this, "span, grow"); PanelCheckBox.checkbox( 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 ebbcb510..c56c0013 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 @@ -8,7 +8,7 @@ import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.SettingsProcessEpisodeSource; @@ -34,7 +34,7 @@ public OptionsPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - new TitlePanelBuilder( + new TitlePanel( title:getText("PreferenceDialog.DownloadOptions"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) @@ -53,7 +53,7 @@ public OptionsPanel(SettingsControl settingsCtrl) { .build() .addComponent(this.pnlDefaultSelection = new DefaultSelectionPanel(settingsCtrl))); - new TitlePanelBuilder( + new TitlePanel( title:getText("PreferenceDialog.SearchFilter"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) @@ -66,24 +66,24 @@ public OptionsPanel(SettingsControl settingsCtrl) { .addComponent(this.chkExcludeHearingImpaired = new JCheckBox(getText("PreferenceDialog.ExcludeHearingImpaired"))); - TitlePanel.title(getText("PreferenceDialog.TableOptions")) - .marginBottom(0) - .padding(0) - .paddingLeft(20) + new TitlePanel( + title:getText("PreferenceDialog.TableOptions"), + margin:new BoxModelProperties(null, null, 0, null), + padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow, wrap") .addComponent(this.chkOnlyFound = new JCheckBox(getText("PreferenceDialog.ShowOnlyFound"))); - TitlePanel.title(getText("PreferenceDialog.ErrorHandlingOption")) - .marginBottom(0) - .padding(0) - .paddingLeft(20) + new TitlePanel( + title:getText("PreferenceDialog.ErrorHandlingOption"), + margin:new BoxModelProperties(null, null, 0, null), + padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow, wrap") .addComponent(this.chkStopOnSearchError = new JCheckBox(getText("PreferenceDialog.StopAfterError"))); - TitlePanel.title(getText("PreferenceDialog.SerieDatabaseSource")) - .marginBottom(0) - .padding(0) - .paddingLeft(20) + new TitlePanel( + title:getText("PreferenceDialog.SerieDatabaseSource"), + margin:new BoxModelProperties(null, null, 0, null), + padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow") .addComponent(this.cbxEpisodeProcessSource = new JComboBox<>(SettingsProcessEpisodeSource.values()), "wrap") 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 1bfb6887..57237160 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 @@ -12,7 +12,7 @@ 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.TitlePanelBuilder; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; 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; @@ -44,7 +44,7 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - JPanel titlePanel = new TitlePanelBuilder(title:getText("PreferenceDialog.SelectPreferredSources")) + JPanel titlePanel = new TitlePanel(title:getText("PreferenceDialog.SelectPreferredSources")) .addTo(this, "span, grow"); { 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 4c83db0c..a30d80fa 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 @@ -23,7 +23,7 @@ import org.lodder.subtools.multisubdownloader.gui.dialog.StructureBuilderDialog; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; 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; @@ -50,7 +50,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titlePanel = new TitlePanelBuilder( + JPanel titlePanel = new TitlePanel( title:getText("PreferenceDialog.RenameFiles"), margin:new BoxModelProperties(0, 20, 0, 0), padding:new BoxModelProperties(0, 20, 0, 0)) 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 f44ec150..9efcfe63 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 @@ -38,7 +38,7 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType super(new MigLayout("insets 0, fill, nogrid")); this.librarySettings = librarySettings; - JPanel titlePanel = new TitlePanel.TitlePanelBuilder( + JPanel titlePanel = new TitlePanel( title:Messages.getText("PreferenceDialog.MoveToLibrary"), margin:new BoxModelProperties(0, 20, 0, 0), padding:new BoxModelProperties(0, 20, 0, 0), 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 4c6d712f..9d8aadb5 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 @@ -27,7 +27,7 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { super(new MigLayout("insets 0, fillx, nogrid")); this.librarySettings = librarySettings; - JPanel titlePanel = new TitlePanel.TitlePanelBuilder( + JPanel titlePanel = new TitlePanel( title:Messages.getText("PreferenceDialog.SubtitlesBackup"), margin:new BoxModelProperties(0), padding:new BoxModelProperties(0, 20, 0, 0)) 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 ca36f4ec..24818453 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 @@ -11,7 +11,7 @@ import net.miginfocom.swing.MigLayout; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PartialDisableComboBox; -import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel.TitlePanelBuilder; +import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; @@ -40,7 +40,7 @@ public abstract sealed class VideoLibraryPanel extends JPanel implements Prefere this.pnlBackup = renameMode ? null : new SubtitleBackupPanel(librarySettings).addTo(this, "wrap, span, growx"); - JPanel performActionPanel = new TitlePanelBuilder( + JPanel performActionPanel = new TitlePanel( title:getText("PreferenceDialog.PerformActions"), margin:new BoxModelProperties(0), padding:new BoxModelProperties(0, 20, 0, 0)) From 24641cdca7876ab6d13146ac1d7b63ace69b9b19 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 22:47:16 +0200 Subject: [PATCH 15/58] small changes --- .../gui/dialog/RenameDialog.java | 19 +++++++++---------- .../adapters/JAddic7edAdapter.java | 14 +++++++------- 2 files changed, 16 insertions(+), 17 deletions(-) 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 49d3832a..f7bc8d19 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 @@ -19,7 +19,6 @@ 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.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; @@ -60,15 +59,15 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String padding:new BoxModelProperties(0, 20, 0, 0), fillContents:true) .addTo(contentPane, "span, grow, wrap") - .addComponent("shrink", new JLabel(Messages.getText("PreferenceDialog.Location"))) + .addComponent("shrink", new JLabel(getText("PreferenceDialog.Location"))) .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().columns(20)) - .addComponent("shrink, wrap", new JButton(Messages.getText("App.Browse")).actionListener( + .addComponent("shrink, wrap", new JButton(getText("App.Browse")).actionListener( () -> MemoryFolderChooser.getInstance() .selectDirectory(contentPane, - Messages.getText("PreferenceDialog.SelectFolderForRenameReplace")) + getText("PreferenceDialog.SelectFolderForRenameReplace")) .ifPresent(txtFolder::setObject))) .addComponent("wrap", - this.chkRecursive = new JCheckBox(Messages.getText("RenameDialog.RecursiveSearch"))); + this.chkRecursive = new JCheckBox(getText("RenameDialog.RecursiveSearch"))); if (videoType == VideoType.EPISODE) { pnlLibrary = new EpisodeLibraryPanel(settings.episodeLibrarySettings, manager, true, @@ -81,10 +80,10 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String new JPanel().layout(new FlowLayout(FlowLayout.RIGHT)) .addTo(contentPane, BorderLayout.SOUTH) - .addComponent(new JButton(Messages.getText("RenameDialog.Rename")).defaultButtonFor(getRootPane()) + .addComponent(new JButton(getText("RenameDialog.Rename")).defaultButtonFor(getRootPane()) .actionListener(() -> rename(videoType, settings, manager, userInteractionHandler)) .actionCommand("Rename")) - .addComponent(new JButton(Messages.getText("App.Cancel")).actionListener(() -> setVisible(false)) + .addComponent(new JButton(getText("App.Cancel")).actionListener(() -> setVisible(false)) .actionCommand("Cancel")); } @@ -96,7 +95,7 @@ private void rename(VideoType videoType, Settings settings, Manager manager, UserInteractionHandler userInteractionHandler) { if (!hasValidSettings()) { - JOptionPane.showMessageDialog(this, Messages.getText("PreferenceDialog.invalidInput"), "Error", + JOptionPane.showMessageDialog(this, getText("PreferenceDialog.invalidInput"), "Error", JOptionPane.ERROR_MESSAGE); return; } @@ -120,7 +119,7 @@ public void propertyChange(PropertyChangeEvent event) { } else { final int progress = renameWorker.getProgress(); progressDialog.updateProgress(progress); - StatusMessenger.instance.message(Messages.getText("RenameDialog.StatusRename")); + StatusMessenger.instance.message(getText("RenameDialog.StatusRename")); } } } @@ -173,7 +172,7 @@ private void rename(Path dir) throws IOException { @Override protected void process(List data) { - data.forEach(s -> StatusMessenger.instance.message(Messages.getText("MainWindow.RenamingFile", s))); + data.forEach(s -> StatusMessenger.instance.message(getText("MainWindow.RenamingFile", s))); } } 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 e97fff5d..3aa82ccb 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 @@ -105,15 +105,15 @@ public Set searchSerieSubtitles(TvRelease tvRelease, public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { return subtitles.stream() - .filter(sub -> language == sub.getLanguage()) - .map(sub -> Subtitle.downloadSource(sub.getUrl()) + .filter(sub -> language == sub.language) + .map(sub -> Subtitle.downloadSource(sub.url) .subtitleSource(subtitleSource) - .fileName(StringExt.removeIllegalFilenameChars(sub.getTitle() + " " + sub.getVersion())) - .language(sub.getLanguage()) - .quality(ReleaseParser.getQualityKeyword(sub.getTitle() + " " + sub.getVersion())) + .fileName(StringExt.removeIllegalFilenameChars(sub.title + " " + sub.version)) + .language(sub.language) + .quality(ReleaseParser.getQualityKeyword(sub.title + " " + sub.version)) .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(sub.getTitle(), sub.getTitle().endsWith(".srt"))) - .uploader(sub.getUploader()) + .releaseGroup(ReleaseParser.extractReleaseGroup(sub.title, sub.title.endsWith(".srt"))) + .uploader(sub.uploader) .hearingImpaired(false)) .collect(Collectors.toSet()); } From 455052476fed34452e1bc8522c836da470c9c4e9 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 22:48:16 +0200 Subject: [PATCH 16/58] add extra extensions --- .../opensubtitles/OpenSubtitlesApi.java | 2 +- .../java/lang/Iterable/IterableExt.java | 17 +++++++++++++++++ .../java/util/Iterator/IteratorExt.java | 19 +++++++++++++++++++ .../java/util/Spliterator/SpliteratorExt.java | 17 +++++++++++++++++ .../org/json/JSONArray/JSONArrayExt.java | 10 +++------- 5 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java create mode 100644 SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java 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 5bc809c2..f32be572 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 @@ -81,7 +81,7 @@ public List getProviderSerieIds(String serieName) throws Op .retryPredicate(exc -> exc instanceof HttpClientException e && e.responseCode == 429) .retryWait(5) .getAsJsonArray() - .stream() + .streamJsonObjects() .filter(show -> "tv".equals(show.getString("kind"))) .map(show -> new OpensubtitleSerieId(show.getString("name"), show.getInt("id"), show.getString("year"))) diff --git a/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java b/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java new file mode 100644 index 00000000..c30b6e8d --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java @@ -0,0 +1,17 @@ +package extensions.java.lang.Iterable; + +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; + +@UtilityClass +@Extension +public class IterableExt { + + public static Stream stream(@This Iterable iterable) { + return StreamSupport.stream(iterable.spliterator(), false); + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java b/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java new file mode 100644 index 00000000..8c7528b2 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java @@ -0,0 +1,19 @@ +package extensions.java.util.Iterator; + +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; + +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; + +@UtilityClass +@Extension +public class IteratorExt { + + public static Stream stream(@This Iterator iterator) { + return Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED).stream(); + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java b/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java new file mode 100644 index 00000000..1732e856 --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java @@ -0,0 +1,17 @@ +package extensions.java.util.Spliterator; + +import java.util.Spliterator; +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; + +@UtilityClass +@Extension +public class SpliteratorExt { + public static Stream stream(@This Spliterator spliterator) { + return StreamSupport.stream(spliterator, false); + } +} \ 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 index 30af1c01..f58f3381 100644 --- a/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java +++ b/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java @@ -1,10 +1,7 @@ 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; @@ -16,9 +13,8 @@ @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); + public static Stream streamJsonObjects(@This JSONArray jsonArray) { + Iterator iterator = (Iterator) (Iterator) jsonArray.iterator(); + return iterator.stream(); } } \ No newline at end of file From ba1540fb7cedf7637f90f5d4bf6599b99ef7a08b Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 23:05:32 +0200 Subject: [PATCH 17/58] use optional parameters (6) --- .../subtools/sublibrary/gui/OptionsPane.java | 148 ++++++------------ .../UserInteractionHandler.java | 9 +- .../UserInteractionHandlerCLI.java | 9 +- .../UserInteractionHandlerGUI.java | 22 +-- 4 files changed, 62 insertions(+), 126 deletions(-) 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 aa0854d4..89f63f23 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 @@ -2,124 +2,58 @@ 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 java.util.stream.StreamSupport; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; -import lombok.experimental.UtilityClass; +import lombok.AllArgsConstructor; +import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; -@UtilityClass public class OptionsPane { public static final Object LOCK = new Object(); - public static OptionsPaneBuilderToStringMapperIntf options(Collection options) { - return OptionsPaneBuilder.options(options); - } - - public static OptionsPaneBuilderToStringMapperIntf options(T[] options) { - return OptionsPaneBuilder.options(options); - } - - public static OptionsPaneBuilderTitleIntf stringOptions(Collection options) { - return OptionsPaneBuilder.options(options); - } - - public static OptionsPaneBuilderTitleIntf stringOptions(String[] options) { - return OptionsPaneBuilder.options(options); - } - - // interface - - public interface OptionsPaneBuilderToStringMapperIntf { - OptionsPaneBuilderTitleIntf toStringMapper(Function toStringMapper); - } - - public interface OptionsPaneBuilderTitleIntf { - OptionsPaneBuilderMessageIntf title(String title); - } - - public interface OptionsPaneBuilderMessageIntf { - OptionsPaneBuilderMessageTypeIntf message(String message); - } - - public interface OptionsPaneBuilderMessageTypeIntf { - OptionsPaneBuilderPromptIntf messageType(int messageType); - - default OptionsPaneBuilderPromptIntf defaultOption() { - return messageType(JOptionPane.DEFAULT_OPTION); - } - - default OptionsPaneBuilderPromptIntf yesNoOption() { - return messageType(JOptionPane.YES_NO_OPTION); + public static class OptionPaneBuilder { + + private final T[] options; + private final @Nullable String title; + private final @Nullable String message; + private final Option messageType; + private final @Nullable Function toStringMapper; + private final @Nullable Component parent; + + public OptionPaneBuilder(T[] options, @Nullable Function toStringMapper, @Nullable String title, + @Nullable String message, + Option messageType, @Nullable Component parent) { + this.options = options; + this.toStringMapper = toStringMapper; + this.title = title; + this.message = message; + this.messageType = messageType; + this.parent = parent; } - default OptionsPaneBuilderPromptIntf yesNoCancelOption() { - return messageType(JOptionPane.YES_NO_CANCEL_OPTION); + public OptionPaneBuilder(Iterable options, @Nullable Function toStringMapper, + @Nullable String title, @Nullable String message, Option messageType, @Nullable Component parent) { + this((T[]) StreamSupport.stream(options.spliterator(), false).toArray(), toStringMapper, title, message, + messageType, parent); } - default OptionsPaneBuilderPromptIntf okCancelOption() { - return messageType(JOptionPane.OK_CANCEL_OPTION); - } - } - - public interface OptionsPaneBuilderPromptIntf { - OptionsPaneBuilderPromptIntf parent(Component parent); - - Optional prompt(); - } - - // builder - - @Setter - @Accessors(fluent = true) - @RequiredArgsConstructor - private static class OptionsPaneBuilder - implements OptionsPaneBuilderPromptIntf, OptionsPaneBuilderMessageTypeIntf, - OptionsPaneBuilderMessageIntf, OptionsPaneBuilderTitleIntf, OptionsPaneBuilderToStringMapperIntf { - private final T[] optionsArray; - private final Collection optionsList; - private String title; - private String message; - private int messageType; - private Function toStringMapper; - private Component parent; - - public static OptionsPaneBuilder options(Collection options) { - return new OptionsPaneBuilder<>(null, options); - } - - public static OptionsPaneBuilder options(S[] options) { - return new OptionsPaneBuilder<>(options, null); - } - - @SuppressWarnings("unchecked") - @Override public Optional prompt() { synchronized (LOCK) { if (toStringMapper == null) { - T[] options; - if (optionsList != null) { - options = (T[]) optionsList.toArray(); - } else { - options = optionsArray; - } return Optional.ofNullable( - (T) JOptionPane.showInputDialog(parent, message, title, messageType, null, options, "0")); + (T) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, options, "0")); } else { - Stream optionsStream = optionsList != null ? optionsList.stream() : optionsArray.stream(); - ElementWrapper[] options = - optionsStream.map(option -> new ElementWrapper<>(option, toStringMapper)) - .toArray(ElementWrapper[]::new); - return Optional - .ofNullable( - (ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType, - null, options, "0")) - .map(ElementWrapper::element); + ElementWrapper[] optionsWrapper = + options.stream().map(option -> new ElementWrapper<>(option, toStringMapper)) + .toArray(ElementWrapper[]::new); + return Optional.ofNullable( + (ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, + optionsWrapper, "0")) + .map(ElementWrapper::element); } } } @@ -127,8 +61,18 @@ public Optional prompt() { private record ElementWrapper(T element, Function toStringMapper) { @Override - public String toString() { + public @NonNull String toString() { return toStringMapper.apply(element); } } + + @AllArgsConstructor + public enum Option { + DEFAULT(JOptionPane.DEFAULT_OPTION), + YES_NO(JOptionPane.YES_NO_OPTION), + YES_NO_CANCEL(JOptionPane.YES_NO_CANCEL_OPTION), + OK_CANCEL(JOptionPane.OK_CANCEL_OPTION); + + @val int value; + } } 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 8bbac86d..abfc7874 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 @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.userinteraction; -import java.util.Collection; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Function; @@ -16,14 +15,14 @@ public interface UserInteractionHandler { boolean confirm(String message, String title); - Optional selectFromList(Collection options, String message, String title); + Optional selectFromList(Iterable options, String message, String title); - Optional selectFromList(Collection options, String message, String title, + Optional selectFromList(Iterable options, String message, String title, Function toStringMapper); - Optional choice(Collection options, String message, String title); + Optional choice(Iterable options, String message, String title); - Optional choice(Collection options, String message, String title, Function toStringMapper); + Optional choice(Iterable options, String message, String title, Function toStringMapper); default Optional enter(String title, String message) { return enter(title, message, null, null); 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 3d9a59b7..d9c5c6cb 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 @@ -1,6 +1,5 @@ package org.lodder.subtools.sublibrary.userinteraction; -import java.util.Collection; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; @@ -26,12 +25,12 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { } @Override - public Optional selectFromList(Collection options, String message, String title) { + public Optional selectFromList(Iterable options, String message, String title) { return selectFromList(options, message, title, null); } @Override - public Optional selectFromList(Collection options, String message, String title, + public Optional selectFromList(Iterable options, String message, String title, Function toStringMapper) { return PrompterUtil.getElementFromList(options) .toStringMapper(toStringMapper) @@ -41,12 +40,12 @@ public Optional selectFromList(Collection options, String message, Str } @Override - public Optional choice(Collection options, String message, String title) { + public Optional choice(Iterable options, String message, String title) { return choice(options, message, title, null); } @Override - public Optional choice(Collection options, String message, String title, + public Optional choice(Iterable options, String message, String title, Function toStringMapper) { return selectFromList(options, message, title, toStringMapper); } 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 897f2856..71f01542 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 @@ -1,7 +1,6 @@ package org.lodder.subtools.sublibrary.userinteraction; import javax.swing.*; -import java.util.Collection; import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -21,32 +20,27 @@ public class UserInteractionHandlerGUI implements UserInteractionHandler { @val JFrame frame; @Override - public Optional selectFromList(Collection options, String message, String title) { + public Optional selectFromList(Iterable options, String message, String title) { return selectFromList(options, message, title, null); } @Override - public Optional selectFromList(Collection options, String message, String title, - Function toStringMapper) { - if (options.isEmpty()) { + public Optional selectFromList(Iterable options, String message, String title, + Function toStringMapper) { + if (!options.iterator().hasNext()) { return Optional.empty(); } - return OptionsPane.options(options) - .toStringMapper(toStringMapper) - .title(title) - .message(message) - .defaultOption() - .parent(frame) - .prompt(); + return new OptionsPane.OptionPaneBuilder<>(options, toStringMapper, title, message, OptionsPane.Option.DEFAULT, + frame).prompt(); } @Override - public Optional choice(Collection options, String message, String title) { + public Optional choice(Iterable options, String message, String title) { return choice(options, message, title, null); } @Override - public Optional choice(Collection options, String message, String title, + public Optional choice(Iterable options, String message, String title, Function toStringMapper) { String[] optionsAsStrings = options.stream() .map(Objects.requireNonNullElseGet(toStringMapper, () -> String::valueOf)) From 8fefb40364c37503584e04aba0a719e70ad9ace2 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 23:19:19 +0200 Subject: [PATCH 18/58] change --- .../subtools/sublibrary/gui/OptionsPane.java | 73 +++++++++---------- .../UserInteractionHandlerGUI.java | 3 +- 2 files changed, 36 insertions(+), 40 deletions(-) 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 89f63f23..d7150b8d 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 @@ -11,50 +11,47 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -public class OptionsPane { +public class OptionsPane { public static final Object LOCK = new Object(); - public static class OptionPaneBuilder { + private final T[] options; + private final @Nullable String title; + private final @Nullable String message; + private final Option messageType; + private final @Nullable Function toStringMapper; + private final @Nullable Component parent; - private final T[] options; - private final @Nullable String title; - private final @Nullable String message; - private final Option messageType; - private final @Nullable Function toStringMapper; - private final @Nullable Component parent; - - public OptionPaneBuilder(T[] options, @Nullable Function toStringMapper, @Nullable String title, - @Nullable String message, - Option messageType, @Nullable Component parent) { - this.options = options; - this.toStringMapper = toStringMapper; - this.title = title; - this.message = message; - this.messageType = messageType; - this.parent = parent; - } + public OptionsPane(T[] options, @Nullable Function toStringMapper=null, @Nullable String title=null, + @Nullable String message=null, Option messageType, @Nullable Component parent=null) { + this.options = options; + this.toStringMapper = toStringMapper; + this.title = title; + this.message = message; + this.messageType = messageType; + this.parent = parent; + } - public OptionPaneBuilder(Iterable options, @Nullable Function toStringMapper, - @Nullable String title, @Nullable String message, Option messageType, @Nullable Component parent) { - this((T[]) StreamSupport.stream(options.spliterator(), false).toArray(), toStringMapper, title, message, - messageType, parent); - } + public OptionsPane(Iterable options, @Nullable Function toStringMapper=null, + @Nullable String title=null, @Nullable String message=null, Option messageType, + @Nullable Component parent=null) { + this((T[]) StreamSupport.stream(options.spliterator(), false).toArray(), toStringMapper, title, message, + messageType, parent); + } - public Optional prompt() { - synchronized (LOCK) { - if (toStringMapper == null) { - return Optional.ofNullable( - (T) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, options, "0")); - } else { - ElementWrapper[] optionsWrapper = - options.stream().map(option -> new ElementWrapper<>(option, toStringMapper)) - .toArray(ElementWrapper[]::new); - return Optional.ofNullable( - (ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, - optionsWrapper, "0")) - .map(ElementWrapper::element); - } + public Optional prompt() { + synchronized (LOCK) { + if (toStringMapper == null) { + return Optional.ofNullable( + (T) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, options, "0")); + } else { + ElementWrapper[] optionsWrapper = + options.stream().map(option -> new ElementWrapper<>(option, toStringMapper)) + .toArray(ElementWrapper[]::new); + return Optional.ofNullable( + (ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, + optionsWrapper, "0")) + .map(ElementWrapper::element); } } } 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 71f01542..19915c02 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 @@ -30,8 +30,7 @@ public Optional selectFromList(Iterable options, String message, Strin if (!options.iterator().hasNext()) { return Optional.empty(); } - return new OptionsPane.OptionPaneBuilder<>(options, toStringMapper, title, message, OptionsPane.Option.DEFAULT, - frame).prompt(); + return new OptionsPane<>(options, toStringMapper, title, message, OptionsPane.Option.DEFAULT, frame).prompt(); } @Override From 4dbe3a515d02c89370d6565cc8f89fb269e387fc Mon Sep 17 00:00:00 2001 From: EotT123 Date: Fri, 18 Apr 2025 23:20:35 +0200 Subject: [PATCH 19/58] pom update --- SubLibrary/pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SubLibrary/pom.xml b/SubLibrary/pom.xml index 174c4411..40365af9 100644 --- a/SubLibrary/pom.xml +++ b/SubLibrary/pom.xml @@ -91,6 +91,10 @@ systems.manifold manifold-tuple-rt + + systems.manifold + manifold-params-rt + org.projectlombok lombok @@ -161,6 +165,11 @@ manifold-tuple ${manifold.version} + + systems.manifold + manifold-params + ${manifold.version} + From a342fdec658fa9cc175fb0cdd3e56dd26c7b7e19 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 10:04:42 +0200 Subject: [PATCH 20/58] fix --- .../lodder/subtools/sublibrary/gui/OptionsPane.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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 d7150b8d..152a3a5f 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 @@ -22,9 +22,10 @@ public class OptionsPane { private final @Nullable Function toStringMapper; private final @Nullable Component parent; - public OptionsPane(T[] options, @Nullable Function toStringMapper=null, @Nullable String title=null, + public OptionsPane(Iterable options, @Nullable Function toStringMapper=null, + @Nullable String title=null, @Nullable String message=null, Option messageType, @Nullable Component parent=null) { - this.options = options; + this.options = (T[]) StreamSupport.stream(options.spliterator(), false).toArray(); this.toStringMapper = toStringMapper; this.title = title; this.message = message; @@ -32,12 +33,6 @@ public OptionsPane(T[] options, @Nullable Function toStringMapper=nul this.parent = parent; } - public OptionsPane(Iterable options, @Nullable Function toStringMapper=null, - @Nullable String title=null, @Nullable String message=null, Option messageType, - @Nullable Component parent=null) { - this((T[]) StreamSupport.stream(options.spliterator(), false).toArray(), toStringMapper, title, message, - messageType, parent); - } public Optional prompt() { synchronized (LOCK) { From 8ac0a684e1a5b2ed2da15f22d6f77e25d4bb9f0b Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 11:11:18 +0200 Subject: [PATCH 21/58] small changes --- .../subtitleproviders/adapters/Adapter.java | 7 +++-- .../multisubdownloader/util/ExportImport.java | 29 ++++++++++--------- .../sublibrary/data/tvdb/TheTvdbAdapter.java | 6 ++-- 3 files changed, 24 insertions(+), 18 deletions(-) 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 d5623703..34d16c1b 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 @@ -203,10 +203,13 @@ default Optional getProviderSerieId(String serieName, String serie uriForSerie = Optional.empty(); } else { // let the user select the correct provider serie id - uriForSerie = getUserInteractionHandler().selectFromList(providerSerieIds, useSeasonForSerieId() ? + uriForSerie = getUserInteractionHandler().selectFromList( + providerSerieIds, + useSeasonForSerieId() ? getText("SelectDialog.SelectSerieNameForNameWithSeason", displayName, seasonToUse) : getText("SelectDialog.SelectSerieNameForName", displayName), - providerName, this::providerSerieIdToDisplayString); + providerName, + this::providerSerieIdToDisplayString); } if (uriForSerie.isEmpty()) { if (serieNameToSearchFor.equals(serieName)) { 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 f04e32a7..0751ca75 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 static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.*; import java.awt.*; import java.io.IOException; @@ -21,7 +23,6 @@ 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; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; @@ -59,8 +60,8 @@ private enum FileType { public void importSettings(SettingsType listType) { chooseFile(listType.fileType).ifPresent(path -> { if (Files.notExists(path)) { - userInteractionHandler.showMessage(Messages.getText("ImportExport.FileDoesNotExist"), - Messages.getText("ImportExport.ErrorWhileImporting"), MessageSeverity.WARNING); + userInteractionHandler.showMessage(getText("ImportExport.FileDoesNotExist"), + getText("ImportExport.ErrorWhileImporting"), MessageSeverity.WARNING); return; } try { @@ -72,11 +73,11 @@ public void importSettings(SettingsType listType) { default -> throw new IllegalArgumentException("Unexpected value: " + listType); } } catch (CorruptSettingsFileException e) { - userInteractionHandler.showMessage(Messages.getText("ImportExport.ImportCorruptFile"), - Messages.getText("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); + userInteractionHandler.showMessage(getText("ImportExport.ImportCorruptFile"), + getText("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); } catch (Exception e) { - userInteractionHandler.showMessage(Messages.getText("ImportExport.ErrorWhileImporting"), - Messages.getText("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); + userInteractionHandler.showMessage(getText("ImportExport.ErrorWhileImporting"), + getText("ImportExport.ErrorWhileImporting"), MessageSeverity.ERROR); } }); } @@ -92,8 +93,8 @@ public void exportSettings(SettingsType listType) { default -> throw new IllegalArgumentException("Unexpected value: " + listType); } } catch (Exception e) { - userInteractionHandler.showMessage(Messages.getText("ImportExport.ErrorWhileExporting"), - Messages.getText("ImportExport.ErrorWhileExporting"), MessageSeverity.ERROR); + userInteractionHandler.showMessage(getText("ImportExport.ErrorWhileExporting"), + getText("ImportExport.ErrorWhileExporting"), MessageSeverity.ERROR); } }); } @@ -150,7 +151,7 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand .flatMap(Arrays::stream) .forEach(selectionForKeyPrefix -> manager.clearExpiredCacheBuilder() .cacheType(CacheType.DISK) - .keyFilter((String k) -> k.startsWith(selectionForKeyPrefix.keyPrefix())) + .keyFilter((String k) -> k.startsWith(selectionForKeyPrefix.keyPrefix)) .clear()); } serieMappings.forEach(serieMapping -> manager.valueBuilder() @@ -163,11 +164,11 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand private static Optional getImportStyle(UserInteractionHandler userInteractionHandler) { return userInteractionHandler.choice(Arrays.asList(ImportStyle.values()), - Messages.getText("ImportExport.OverwriteOrAdd"), - Messages.getText("ImportExport.OverwriteOrAddTitle"), + getText("ImportExport.OverwriteOrAdd"), + getText("ImportExport.OverwriteOrAddTitle"), option -> switch (option) { - case OVERWRITE -> Messages.getText("ImportExport.Overwrite"); - case APPEND -> Messages.getText("ImportExport.Add"); + case OVERWRITE -> getText("ImportExport.Overwrite"); + case APPEND -> getText("ImportExport.Add"); }); } 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 6bff2276..e452d1a3 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 @@ -80,9 +80,11 @@ public Optional getSerie(String serieName) { Comparator.reverseOrder()) .thenComparing(TheTvdbSerie::getFirstAired, Comparator.reverseOrder()); try { - tvdbSerie = userInteractionHandler.selectFromList(serieIds.stream().sorted(comparator).toList(), + tvdbSerie = userInteractionHandler.selectFromList( + serieIds.stream().sorted(comparator).toList(), getText("Prompter.SelectTvdbMatchForSerie", serieName), - PROVIDER_NAME, s -> "${s.serieName} (${s.firstAired})"); + PROVIDER_NAME, + s -> "${s.serieName} (${s.firstAired})"); if (tvdbSerie.isEmpty()) { tvdbSerie = askUserToEnterTvdbId(serieName) .mapToObj(tvdbId -> api.getSerie(tvdbId, null).orElse(null)); From 0cc69cd95fe383544ffde2da6fe4f7d3e1bae39a Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 14:07:06 +0200 Subject: [PATCH 22/58] temp commit --- .../UserInteractionHandler.java | 19 +-- .../UserInteractionHandlerCLI.java | 34 ++--- .../UserInteractionHandlerGUI.java | 19 +-- .../util/prompter/PrompterBoolean.java | 120 ++++++++++++++++++ .../util/prompter/PrompterBuilderBoolean.java | 91 ------------- .../util/prompter/PrompterBuilderCommon.java | 8 +- .../util/prompter/PrompterBuilderInt.java | 108 ---------------- .../util/prompter/PrompterBuilderString.java | 24 ---- .../util/prompter/PrompterBuilderValue.java | 5 +- .../PrompterBuilderValueFromList.java | 7 +- .../sublibrary/util/prompter/PrompterInt.java | 47 +++++++ .../util/prompter/PrompterString.java | 37 ++++++ .../util/prompter/PrompterUtil.java | 48 +------ 13 files changed, 243 insertions(+), 324 deletions(-) create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderBoolean.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderInt.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderString.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java 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 abfc7874..4f09cfe5 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 @@ -7,6 +7,7 @@ import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; public interface UserInteractionHandler { @@ -15,20 +16,14 @@ public interface UserInteractionHandler { boolean confirm(String message, String title); - Optional selectFromList(Iterable options, String message, String title); + Optional selectFromList(Iterable options, @Nullable String message=null, @Nullable String title=null, + @Nullable Function toStringMapper=null); - Optional selectFromList(Iterable options, String message, String title, - Function toStringMapper); + Optional choice(Iterable options, @Nullable String message=null, @Nullable String title=null, + @Nullable Function toStringMapper=null); - Optional choice(Iterable options, String message, String title); - - Optional choice(Iterable options, String message, String title, Function toStringMapper); - - default Optional enter(String title, String message) { - return enter(title, message, null, null); - } - - Optional enter(String title, String message, String errorMessage, Predicate validator); + Optional enter(String title, String message=null, @Nullable String errorMessage=null, + @Nullable Predicate validator=null); default OptionalInt enterNumber(String title, String message, String errorMessage) { return enter(title, message, errorMessage, StringUtils::isNumeric).mapToInt(Integer::parseInt); 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 d9c5c6cb..93c72334 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 @@ -10,7 +10,10 @@ import org.codehaus.plexus.components.interactivity.DefaultOutputHandler; import org.codehaus.plexus.components.interactivity.DefaultPrompter; import org.codehaus.plexus.components.interactivity.Prompter; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; +import org.lodder.subtools.sublibrary.util.prompter.PrompterBoolean; +import org.lodder.subtools.sublibrary.util.prompter.PrompterString; import org.lodder.subtools.sublibrary.util.prompter.PrompterUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,13 +28,8 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { } @Override - public Optional selectFromList(Iterable options, String message, String title) { - return selectFromList(options, message, title, null); - } - - @Override - public Optional selectFromList(Iterable options, String message, String title, - Function toStringMapper) { + public Optional selectFromList(Iterable options, @Nullable String message, @Nullable String title, + @Nullable Function toStringMapper) { return PrompterUtil.getElementFromList(options) .toStringMapper(toStringMapper) .message(message) @@ -40,27 +38,23 @@ public Optional selectFromList(Iterable options, String message, Strin } @Override - public Optional choice(Iterable options, String message, String title) { - return choice(options, message, title, null); - } - - @Override - public Optional choice(Iterable options, String message, String title, - Function toStringMapper) { + public Optional choice(Iterable options, @Nullable String message, @Nullable String title, + @Nullable Function toStringMapper) { return selectFromList(options, message, title, toStringMapper); } @Override public boolean confirm(String message, String title) { - return PrompterUtil.getBooleanValue().message(message + " (Y/N)").prompt(prompter).get(); + return new PrompterBoolean(message:message + " (Y/N)").prompt(prompter); } @Override - public Optional enter(String title, String message, String errorMessage, Predicate validator) { - return PrompterUtil.getString() - .message(message) - .errorMessage(errorMessage) - .objectValidator(validator) + public Optional enter(String title, String message, @Nullable String errorMessage, + @Nullable Predicate validator) { + return new PrompterString( + message:message, + errorMessage:errorMessage, + validator:validator) .prompt(prompter); } 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 19915c02..de224ce6 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 @@ -9,6 +9,7 @@ import lombok.AllArgsConstructor; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.gui.InputPane; import org.lodder.subtools.sublibrary.gui.OptionsPane; @@ -20,13 +21,8 @@ public class UserInteractionHandlerGUI implements UserInteractionHandler { @val JFrame frame; @Override - public Optional selectFromList(Iterable options, String message, String title) { - return selectFromList(options, message, title, null); - } - - @Override - public Optional selectFromList(Iterable options, String message, String title, - Function toStringMapper) { + public Optional selectFromList(Iterable options, @Nullable String message, + @Nullable String title, @Nullable Function toStringMapper) { if (!options.iterator().hasNext()) { return Optional.empty(); } @@ -34,13 +30,8 @@ public Optional selectFromList(Iterable options, String message, Strin } @Override - public Optional choice(Iterable options, String message, String title) { - return choice(options, message, title, null); - } - - @Override - public Optional choice(Iterable options, String message, String title, - Function toStringMapper) { + public Optional choice(Iterable options, @Nullable String message, @Nullable String title, + @Nullable Function toStringMapper) { String[] optionsAsStrings = options.stream() .map(Objects.requireNonNullElseGet(toStringMapper, () -> String::valueOf)) .toArray(String[]::new); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java new file mode 100644 index 00000000..bd4cdb42 --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java @@ -0,0 +1,120 @@ +package org.lodder.subtools.sublibrary.util.prompter; + +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.codehaus.plexus.components.interactivity.Prompter; +import org.jspecify.annotations.Nullable; + +public class PrompterBoolean { + + public final Function toObjectMapper; + public final Predicate validator; + private final Boolean defaultValue; + private final Supplier defaultValueSupplier; + private final String message; + private final String errorMessage; + + public PrompterBoolean( + Predicate toObjectMapper="y"::equalsIgnoreCase, + @Nullable Predicate validator=v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v), + @Nullable Boolean defaultValue=null, + @Nullable BooleanSupplier defaultValueSupplier=null, + String message, + @Nullable String errorMessage=null) { + this.toObjectMapper = toObjectMapper::test; + this.validator = validator; + this.defaultValue = defaultValue; + this.defaultValueSupplier = defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean; + this.message = message; + this.errorMessage = errorMessage; + } + + public boolean prompt(Prompter prompter) { + return PrompterBuilderCommon.prompt(prompter, toObjectMapper, validator, null, defaultValue, + defaultValueSupplier, message, errorMessage).get(); + } + +// +// private PrompterBoolean() { +// // util class +// } +// +// protected static boolean getValue(Prompter prompter) { +// return new ValueBuilder().prompt(prompter); +// } +// +// protected static ValueBuilderOtherMapperIntf getValue() { +// return new ValueBuilder(); +// } +// +// public static ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue) { +// return new ValueBuilder().defaultValue(defaultValue); +// } +// +// public static ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier) { +// return new ValueBuilder().defaultValueSupplier(defaultValueSupplier); +// } +// +// public static ValueBuilderOtherMapperIntf message(String message, Object... replacements) { +// return new ValueBuilder().message(message, replacements); +// } +// +// public interface ValueBuilderOtherMapperIntf { +// +// ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue); +// +// ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier); +// +// ValueBuilderOtherMapperIntf message(String message, Object... replacements); +// +// ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); +// +// boolean prompt(Prompter prompter); +// } +// +// // ------- \\ +// // Builder \\ +// // ------- \\ +// +// @Setter +// @Accessors(fluent = true, chain = true) +// public static class ValueBuilder implements ValueBuilderOtherMapperIntf { +// public static final Predicate TO_OBJECT_MAPPER = "y"::equalsIgnoreCase; +// public static final Predicate VALIDATOR = v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v); +// private Boolean defaultValue; +// private BooleanSupplier defaultValueSupplier; +// private String message; +// private String errorMessage; +// +// private ValueBuilder() { +// // hide constructor +// } +// +// @Override +// public ValueBuilder defaultValue(boolean defaultValue) { +// this.defaultValue = defaultValue; +// return this; +// } +// +// @Override +// public ValueBuilder message(String message, Object... replacements) { +// this.message = message.formatted(replacements); +// return this; +// } +// +// @Override +// public ValueBuilder errorMessage(String errorMessage, Object... replacements) { +// this.errorMessage = errorMessage.formatted(replacements); +// return this; +// } +// +// @Override +// public boolean prompt(Prompter prompter) { +// return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER::test, VALIDATOR, null, defaultValue, +// defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, errorMessage).get(); +// } +// } +} 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 deleted file mode 100644 index 95ef4859..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderBoolean.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.BooleanSupplier; -import java.util.function.Predicate; - -import lombok.Setter; -import lombok.experimental.Accessors; -import org.codehaus.plexus.components.interactivity.Prompter; - -public class PrompterBuilderBoolean { - - private PrompterBuilderBoolean() { - // util class - } - - protected static boolean getValue(Prompter prompter) { - return new ValueBuilder().prompt(prompter); - } - - protected static ValueBuilderOtherMapperIntf getValue() { - return new ValueBuilder(); - } - - public static ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue) { - return new ValueBuilder().defaultValue(defaultValue); - } - - public static ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier) { - return new ValueBuilder().defaultValueSupplier(defaultValueSupplier); - } - - public static ValueBuilderOtherMapperIntf message(String message, Object... replacements) { - return new ValueBuilder().message(message, replacements); - } - - public interface ValueBuilderOtherMapperIntf { - - ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue); - - ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier); - - ValueBuilderOtherMapperIntf message(String message, Object... replacements); - - ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); - - boolean prompt(Prompter prompter); - } - - // ------- \\ - // Builder \\ - // ------- \\ - - @Setter - @Accessors(fluent = true, chain = true) - public static class ValueBuilder implements ValueBuilderOtherMapperIntf { - public static final Predicate TO_OBJECT_MAPPER = "y"::equalsIgnoreCase; - public static final Predicate VALIDATOR = v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v); - private Boolean defaultValue; - private BooleanSupplier defaultValueSupplier; - private String message; - private String errorMessage; - - private ValueBuilder() { - // hide constructor - } - - @Override - public ValueBuilder defaultValue(boolean defaultValue) { - this.defaultValue = defaultValue; - return this; - } - - @Override - public ValueBuilder message(String message, Object... replacements) { - this.message = message.formatted(replacements); - return this; - } - - @Override - public ValueBuilder errorMessage(String errorMessage, Object... replacements) { - this.errorMessage = errorMessage.formatted(replacements); - return this; - } - - @Override - public boolean prompt(Prompter prompter) { - return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER::test, VALIDATOR, null, defaultValue, - defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, errorMessage).get(); - } - } -} 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 cfbdfaf8..5fdb0399 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 @@ -8,17 +8,19 @@ import org.apache.commons.lang3.StringUtils; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; -public class PrompterBuilderCommon { +class PrompterBuilderCommon { private PrompterBuilderCommon() { // hide constructor } protected static Optional prompt(Prompter prompter, Function toObjectMapper, - Predicate validator, Predicate objValidator, T defaultValue, Supplier defaultValueSupplier, - String message, String errorMessage) { + @Nullable Predicate validator=null, @Nullable Predicate objValidator=null, + @Nullable T defaultValue=null, @Nullable Supplier defaultValueSupplier=null, String message, + @Nullable String errorMessage=null) { try { String value = prompter.prompt(message + System.lineSeparator()); if (StringUtils.isEmpty(value)) { 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 deleted file mode 100644 index d98b3981..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderInt.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.IntPredicate; -import java.util.function.IntSupplier; -import java.util.function.Predicate; -import java.util.function.ToIntFunction; - -import lombok.Setter; -import lombok.experimental.Accessors; -import org.codehaus.plexus.components.interactivity.Prompter; - -public class PrompterBuilderInt { - - private PrompterBuilderInt() { - // util class - } - - protected static int getValue(Prompter prompter) { - return new ValueBuilder().prompt(prompter); - } - - protected static ValueBuilderOtherMapperIntf getValue() { - return new ValueBuilder(); - } - - public static ValueBuilderOtherMapperIntf defaultValue(int defaultValue) { - return new ValueBuilder().defaultValue(defaultValue); - } - - public static ValueBuilderOtherMapperIntf defaultValueSupplier(IntSupplier defaultValueSupplier) { - return new ValueBuilder().defaultValueSupplier(defaultValueSupplier); - } - - public static ValueBuilderOtherMapperIntf message(String message, Object... replacements) { - return new ValueBuilder().message(message, replacements); - } - - public static ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements) { - return new ValueBuilder().errorMessage(errorMessage, replacements); - } - - public interface ValueBuilderOtherMapperIntf { - - ValueBuilderOtherMapperIntf defaultValue(int defaultValue); - - ValueBuilderOtherMapperIntf defaultValueSupplier(IntSupplier defaultValueSupplier); - - ValueBuilderOtherMapperIntf message(String message, Object... replacements); - - ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); - - ValueBuilderOtherMapperIntf validator(IntPredicate validator); - - int prompt(Prompter prompter); - } - - // ------- \\ - // Builder \\ - // ------- \\ - - @Setter - @Accessors(fluent = true, chain = true) - public static class ValueBuilder implements ValueBuilderOtherMapperIntf { - public static final ToIntFunction TO_OBJECT_MAPPER = Integer::parseInt; - public static final Predicate VALIDATOR = v -> { - try { - Integer.parseInt(v); - return true; - } catch (NumberFormatException e) { - return false; - } - }; - private Integer defaultValue; - private IntSupplier defaultValueSupplier; - private String message; - private String errorMessage; - private IntPredicate validator; - - private ValueBuilder() { - // hide constructor - } - - @Override - public ValueBuilder defaultValue(int defaultValue) { - this.defaultValue = defaultValue; - return this; - } - - @Override - public ValueBuilder message(String message, Object... replacements) { - this.message = message.formatted(replacements); - return this; - } - - @Override - public ValueBuilder errorMessage(String errorMessage, Object... replacements) { - this.errorMessage = errorMessage.formatted(replacements); - return this; - } - - @Override - public int prompt(Prompter prompter) { - return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER::applyAsInt, VALIDATOR, - v -> validator == null || validator.test(v), defaultValue, - defaultValueSupplier == null ? null : defaultValueSupplier::getAsInt, message, errorMessage).get(); - } - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderString.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderString.java deleted file mode 100644 index 11f3efff..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderString.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.Function; - -import org.apache.commons.lang3.StringUtils; -import org.codehaus.plexus.components.interactivity.Prompter; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValue.ValueBuilderOtherMapperIntf; - -public class PrompterBuilderString { - - private PrompterBuilderString() { - // util class - } - - protected static String getString(Prompter prompter) { - return PrompterBuilderValue.getValue().toObjectMapper(Function.identity()).validator(StringUtils::isNotBlank) - .prompt(prompter).get(); - } - - protected static ValueBuilderOtherMapperIntf getString() { - return PrompterBuilderValue.getValue().toObjectMapper(Function.identity()).validator(StringUtils::isNotBlank); - } - -} 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 fedeef5f..9d0a6a0a 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 @@ -8,6 +8,7 @@ import lombok.Setter; import lombok.experimental.Accessors; import org.codehaus.plexus.components.interactivity.Prompter; +import org.jspecify.annotations.Nullable; public class PrompterBuilderValue { @@ -88,8 +89,8 @@ public ValueBuilder message(String message, Object... replacements) { } @Override - public ValueBuilder errorMessage(String errorMessage, Object... replacements) { - this.errorMessage = errorMessage.formatted(replacements); + public ValueBuilder errorMessage(@Nullable String errorMessage, Object... replacements) { + this.errorMessage = errorMessage == null ? null : 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 1133b536..a228b53a 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 @@ -14,6 +14,7 @@ import org.apache.commons.lang3.StringUtils; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; +import org.jspecify.annotations.Nullable; public class PrompterBuilderValueFromList { @@ -64,11 +65,11 @@ public interface ValueFromListPromptBuilderIntf { public static class ValueFromListBuilder implements ValueFromListToStringMapperBuilderIntf, ValueFromListPromptBuilderIntf { private final List elements; - private Function toStringMapper; - private String message; + private Function toStringMapper = String::valueOf; + private @Nullable String message; private boolean includeNull; private T emptyValue; - private TableDisplayer tableDisplayer; + private @Nullable TableDisplayer tableDisplayer; ValueFromListBuilder(Iterable elements) { this.elements = Lists.newArrayList(elements); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java new file mode 100644 index 00000000..9c30ea8c --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java @@ -0,0 +1,47 @@ +package org.lodder.subtools.sublibrary.util.prompter; + +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.IntSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.codehaus.plexus.components.interactivity.Prompter; +import org.jspecify.annotations.Nullable; + +public class PrompterInt { + + public static final Function TO_OBJECT_MAPPER = Integer::parseInt; + public static final Predicate VALIDATOR = v -> { + try { + Integer.parseInt(v); + return true; + } catch (NumberFormatException e) { + return false; + } + }; + + public final Predicate objValidator; + private final Integer defaultValue; + private final Supplier defaultValueSupplier; + private final String message; + private final String errorMessage; + + public PrompterInt( + @Nullable IntPredicate validator=_ -> true, + @Nullable Integer defaultValue=null, + @Nullable IntSupplier defaultValueSupplier=null, + String message, + @Nullable String errorMessage=null) { + this.objValidator = validator == null ? null : validator::test; + this.defaultValue = defaultValue; + this.defaultValueSupplier = defaultValueSupplier == null ? null : defaultValueSupplier::getAsInt; + this.message = message; + this.errorMessage = errorMessage; + } + + public int prompt(Prompter prompter) { + return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER, VALIDATOR, objValidator, + defaultValue, defaultValueSupplier, message, errorMessage).get(); + } +} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java new file mode 100644 index 00000000..6421931c --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java @@ -0,0 +1,37 @@ +package org.lodder.subtools.sublibrary.util.prompter; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.codehaus.plexus.components.interactivity.Prompter; +import org.jspecify.annotations.Nullable; + +public class PrompterString { + + public final Predicate validator; + private final String defaultValue; + private final Supplier defaultValueSupplier; + private final String message; + private final String errorMessage; + + public PrompterString( + @Nullable Predicate validator=StringUtils::isNotBlank, + @Nullable String defaultValue=null, + @Nullable Supplier defaultValueSupplier=null, + String message, + @Nullable String errorMessage=null) { + this.validator = validator; + this.defaultValue = defaultValue; + this.defaultValueSupplier = defaultValueSupplier; + this.message = message; + this.errorMessage = errorMessage; + } + + public Optional prompt(Prompter prompter) { + return PrompterBuilderCommon.prompt(prompter, Function.identity(), validator, null, + defaultValue, defaultValueSupplier, message, errorMessage); + } +} 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 86ca8545..bcaa7d75 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 @@ -4,8 +4,6 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValue.ValueBuilderOther2MapperIntf; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValue.ValueBuilderOtherMapperIntf; import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValueFromList.ValueFromListPromptBuilderIntf; import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValueFromList.ValueFromListToStringMapperBuilderIntf; import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValuesFromList.ValuesFromListPromptBuilderIntf; @@ -26,55 +24,11 @@ public static Prompter showMessage(Prompter prompter, String message, Object... } } - public static PrompterBuilderBoolean.ValueBuilderOtherMapperIntf userApproves() { - return getBoolean(); - } - - public static ValueBuilderOther2MapperIntf pressAnyKeyToContinue() { - return getString().defaultValue(""); - } - public static void pressAnyKeyToContinue(Prompter prompter) { - pressAnyKeyToContinue().message("Press any key to continue").prompt(prompter); - } - - // ############### \\ - // ## Get Value ## \\ - // ############### \\ - - public static int getInt(Prompter prompter) { - return PrompterBuilderInt.getValue(prompter); + new PrompterString(message:"Press any key to continue", defaultValue:"").prompt(prompter); } - public static PrompterBuilderInt.ValueBuilderOtherMapperIntf getInt() { - return PrompterBuilderInt.getValue(); - } - - public static PrompterBuilderValue.ValueBuilderOtherMapperIntf getIntValue() { - return PrompterBuilderValue.getValue().toObjectMapper(Integer::parseInt) - .validator(PrompterBuilderInt.ValueBuilder.VALIDATOR); - } - public static boolean getBoolean(Prompter prompter) { - return PrompterBuilderBoolean.getValue(prompter); - } - - public static PrompterBuilderBoolean.ValueBuilderOtherMapperIntf getBoolean() { - return PrompterBuilderBoolean.getValue(); - } - - public static PrompterBuilderValue.ValueBuilderOtherMapperIntf getBooleanValue() { - return PrompterBuilderValue.getValue().toObjectMapper("y"::equalsIgnoreCase) - .validator(PrompterBuilderBoolean.ValueBuilder.VALIDATOR); - } - - public static String getString(Prompter prompter) { - return PrompterBuilderString.getString(prompter); - } - - public static ValueBuilderOtherMapperIntf getString() { - return PrompterBuilderString.getString(); - } // ######################### \\ // ## Get Value From List ## \\ From 6ee2a2e449b186d58361fe9c33c775387ed7bab0 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 21:24:21 +0200 Subject: [PATCH 23/58] temp commit --- .../UserInteractionHandlerCLI.java | 50 ++-- .../java/lang/Iterable/IterableExt.java | 5 + .../interactivity/Prompter/PrompterExt.java | 247 ++++++++++++++++++ .../UserInteractionHandlerCLI.java | 33 ++- .../util/prompter/ColumnDisplayer.java | 1 + .../util/prompter/PrompterBoolean.java | 1 + .../util/prompter/PrompterBuilderCommon.java | 1 + .../util/prompter/PrompterBuilderValue.java | 1 + .../PrompterBuilderValueFromList.java | 141 ---------- .../PrompterBuilderValuesFromList.java | 139 ---------- .../sublibrary/util/prompter/PrompterInt.java | 82 ++++++ .../util/prompter/PrompterString.java | 1 + .../util/prompter/PrompterUtil.java | 64 ----- .../util/prompter/PrompterValueFromList.java | 78 ++++++ .../util/prompter/PrompterValuesFromList.java | 83 ++++++ .../util/prompter/TableDisplayer.java | 4 +- 16 files changed, 551 insertions(+), 380 deletions(-) create mode 100644 SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValueFromList.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValuesFromList.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterUtil.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java 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 a726df15..9b5684aa 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader; +import static org.lodder.subtools.multisubdownloader.Messages.*; import static org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName.*; import java.util.Comparator; @@ -7,6 +8,7 @@ import java.util.function.Function; import java.util.stream.Stream; +import extensions.org.codehaus.plexus.components.interactivity.Prompter.PrompterExt; import org.codehaus.plexus.components.interactivity.DefaultInputHandler; import org.codehaus.plexus.components.interactivity.DefaultOutputHandler; import org.codehaus.plexus.components.interactivity.DefaultPrompter; @@ -15,12 +17,9 @@ import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -import org.lodder.subtools.sublibrary.util.prompter.ColumnDisplayer; -import org.lodder.subtools.sublibrary.util.prompter.PrompterUtil; -import org.lodder.subtools.sublibrary.util.prompter.TableDisplayer; public class UserInteractionHandlerCLI extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandlerCLI - implements UserInteractionHandler { + implements UserInteractionHandler { private final Prompter prompter; public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @@ -30,30 +29,37 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @Override public List selectSubtitles(Release release) { - System.out.printf("\n%s : %s%n", Messages.getText("SelectDialog.SelectCorrectSubtitleThisRelease"), - release.fileName); - return PrompterUtil - .getElementsFromList(release.getMatchingSubs()) - .displayAsTable(createTableDisplayer()) - .message(Messages.getText("SelectDialog.EnterListSelectedSubtitles")) - .sort(Comparator.comparing(Subtitle::getScore)) - .includeNull() - .prompt(prompter); + System.out.printf("\n%s : %s%n", getText("SelectDialog.SelectCorrectSubtitleThisRelease"), + release.fileName); +// return new promptValues<>( +// elements:release.getMatchingSubs(), +// tableDisplayer:createTableDisplayer(), +// message:getText("SelectDialog.EnterListSelectedSubtitles"), +// sort:Comparator.comparing(Subtitle::getScore), +// includeNull:true) +// .prompt(prompter); + return prompter.promptValues( + elements:release.getMatchingSubs(), + tableDisplayer:createTableDisplayer(), + message:getText("SelectDialog.EnterListSelectedSubtitles"), + sorter:Comparator.comparing(Subtitle::getScore), + includeNull:true); } - 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.valueFunction)).toList()); + private PrompterExt.ColumnDisplayer createSubtitleDisplayer(SubtitleTableColumnName column, + Function toStringMapper) { + return new PrompterExt.ColumnDisplayer<>(column.columnName, + subtitle -> String.valueOf(toStringMapper.apply(subtitle))); } @Override public void dryRunOutput(Release release) { createTableDisplayer().display(release.getMatchingSubs()); } + + private PrompterExt.TableDisplayer createTableDisplayer() { + return new PrompterExt.TableDisplayer<>( + Stream.of(SCORE, FILENAME, RELEASEGROUP, QUALITY, SOURCE, UPLOADER, HEARINGIMPAIRED) + .map(stcn -> createSubtitleDisplayer(stcn, stcn.valueFunction)).toList()); + } } diff --git a/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java b/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java index c30b6e8d..9de4bdca 100644 --- a/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java +++ b/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java @@ -1,5 +1,6 @@ package extensions.java.lang.Iterable; +import java.util.Collection; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -14,4 +15,8 @@ public class IterableExt { public static Stream stream(@This Iterable iterable) { return StreamSupport.stream(iterable.spliterator(), false); } + + public static int size(@This Iterable iterable) { + return iterable instanceof Collection collection ? collection.size() : (int) iterable.stream().count(); + } } \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java new file mode 100644 index 00000000..6c6d35bf --- /dev/null +++ b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java @@ -0,0 +1,247 @@ +package extensions.org.codehaus.plexus.components.interactivity.Prompter; + +import static com.pivovarit.gatherers.MoreGatherers.*; +import static java.lang.System.*; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.IntSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import dnl.utils.text.table.TextTable; +import lombok.RequiredArgsConstructor; +import lombok.experimental.UtilityClass; +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.apache.commons.lang3.StringUtils; +import org.codehaus.plexus.components.interactivity.Prompter; +import org.codehaus.plexus.components.interactivity.PrompterException; +import org.jspecify.annotations.Nullable; + +@Extension +@UtilityClass +public class PrompterExt { + + public static void show(@This Prompter prompter, String message, Object... replacements) { + try { + prompter.showMessage(message.formatted(replacements)); + } catch (PrompterException e) { + throw new IllegalStateException(e); + } + } + + public static void pressAnyKeyToContinue(@This Prompter prompter) { + prompter.promptString(message:"Press any key to continue", defaultValue:""); +// new PrompterString(message:"Press any key to continue", defaultValue:"").prompt(prompter); + } + + public static Optional promptString(@This Prompter prompter, + @Nullable Predicate validator=StringUtils::isNotBlank, + @Nullable String defaultValue=null, + @Nullable Supplier defaultValueSupplier=null, + String message, + @Nullable String errorMessage=null) { + + return prompt(prompter, Function.identity(), validator, null, defaultValue, defaultValueSupplier, message, + errorMessage); + } + + public static Optional promptInt(@This Prompter prompter, + @Nullable IntPredicate validator=_ -> true, + @Nullable Integer defaultValue=null, + @Nullable IntSupplier defaultValueSupplier=null, + String message, + @Nullable String errorMessage=null) { + + Predicate isIntValidator = v -> { + try { + Integer.parseInt(v); + return true; + } catch (NumberFormatException e) { + return false; + } + }; + return prompt(prompter, Integer::parseInt, isIntValidator, validator == null ? null : validator::test, + defaultValue, defaultValueSupplier == null ? null : defaultValueSupplier::getAsInt, message, errorMessage); + } + + public static Optional promptBoolean(@This Prompter prompter, + Predicate toObjectMapper="y"::equalsIgnoreCase, + @Nullable Predicate validator=v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v), + @Nullable Boolean defaultValue=null, + @Nullable BooleanSupplier defaultValueSupplier=null, + String message, + @Nullable String errorMessage=null) { + + return prompt(prompter, toObjectMapper::test, validator, null, + defaultValue, defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, + errorMessage); + } + + public static Optional promptValue(@This Prompter prompter, + Iterable elements, + Function toStringMapper=String::valueOf, + String message, + boolean includeNull, + T emptyValue=null, + @Nullable TableDisplayer tableDisplayer=null, + @Nullable Comparator sorter=null) { + List sortedElements = + sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); + try { + String value = promptFromListIndices(prompter, sortedElements, tableDisplayer, toStringMapper, message); + if (StringUtils.isBlank(value) && includeNull) { + return Optional.ofNullable(emptyValue); + } + int number = Integer.parseInt(value); + if (number < 1 || number > sortedElements.size()) { + prompter.show("The entered value isn't in the range [1, %s], try again.", sortedElements.size()); + return prompter.promptValue(elements, toStringMapper, message, includeNull, emptyValue, + tableDisplayer, sorter); + } + return Optional.ofNullable(sortedElements.get(number - 1)); + } catch (NumberFormatException e) { + prompter.show("Enter a valid number, try again."); + return prompter.promptValue(elements, toStringMapper, message, includeNull, emptyValue, + tableDisplayer, sorter); + } + + } + + public static List promptValues(@This Prompter prompter, + Iterable elements, + Function toStringMapper=String::valueOf, + String message, + boolean includeNull, + @Nullable TableDisplayer tableDisplayer=null, + @Nullable Comparator sorter=null) { + + List sortedElements = + sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); + try { + String value = promptFromListIndices(prompter, sortedElements, tableDisplayer, toStringMapper, message); + + if (StringUtils.isBlank(value) && includeNull) { + return List.of(); + } + if (StringUtils.isBlank(value)) { + prompter.show("Enter a valid value, try again."); + return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); + } + int[] choices = value.split(",").stream().mapToInt(Integer::parseInt).map(i -> i - 1).toArray(); + if (choices.stream().distinct().count() != choices.length) { + prompter.show("Choose all distinct options, try again."); + return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); + } + if (choices.stream().anyMatch(number -> number < 0 || number > sortedElements.size() - 1)) { + prompter.show("The entered number(s) aren't in the range [1, %s], try again.", sortedElements.size()); + return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); + } + return choices.stream().map(sortedElements::get).collect(Collectors.toList()); + } catch (NumberFormatException e) { + prompter.show("Invalid number(s) encountered. Enter a comma separated list of the choices."); + return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); + } + } + + private static String promptFromListIndices(Prompter prompter, Iterable elements, + @Nullable TableDisplayer tableDisplayer, Function toStringMapper, + String message) { + try { + if (tableDisplayer != null) { + tableDisplayer.display(elements); + return prompter.prompt(message); + } else { + String choicesMessage = elements.stream() + .gather(zipWithIndex()) + .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) + .collect(Collectors.joining(lineSeparator())) + lineSeparator(); + return prompter.prompt( + StringUtils.isBlank(message) ? choicesMessage : message + lineSeparator() + choicesMessage); + } + } catch (PrompterException e) { + throw new IllegalStateException(e); + } + } + + private static Optional prompt(Prompter prompter, Function toObjectMapper, + @Nullable Predicate validator=null, @Nullable Predicate objValidator=null, + @Nullable T defaultValue=null, @Nullable Supplier defaultValueSupplier=null, String message, + @Nullable String errorMessage=null) { + try { + String value = prompter.prompt(message + System.lineSeparator()); + if (StringUtils.isEmpty(value)) { + if (defaultValue != null) { + return Optional.of(defaultValue); + } else if (defaultValueSupplier != null) { + return Optional.ofNullable(defaultValueSupplier.get()); + } else { + return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, + message, errorMessage); + } + } else { + if (validator != null && !validator.test(value)) { + prompter.showMessage( + StringUtils.isNotBlank(errorMessage) ? errorMessage : 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 : getText("Prompter.ValueIsNotValid")); + return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, + message, errorMessage); + } + return Optional.ofNullable(object); + } + } catch (PrompterException e) { + throw new IllegalStateException(e); + } + } + + @RequiredArgsConstructor + public static class TableDisplayer { + + private final List> columnDisplayers; + + @SafeVarargs + public final void display(T... 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)) + .toArray()) + .toArray(Object[][]::new); + + TextTable tt = new TextTable(columnNames, dataTable); + // this adds the numbering on the left + tt.setAddRowNumbering(true); + tt.printTable(); + } + + public void display(Iterable 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)) + .toArray()) + .toArray(Object[][]::new); + + TextTable tt = new TextTable(columnNames, dataTable); + // this adds the numbering on the left + tt.setAddRowNumbering(true); + tt.printTable(); + } + } + + public record ColumnDisplayer(String columnName, Function toStringMapper) { + } +} 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 93c72334..b944367f 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 @@ -12,9 +12,6 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBoolean; -import org.lodder.subtools.sublibrary.util.prompter.PrompterString; -import org.lodder.subtools.sublibrary.util.prompter.PrompterUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +27,17 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @Override public Optional selectFromList(Iterable options, @Nullable String message, @Nullable String title, @Nullable Function toStringMapper) { - return PrompterUtil.getElementFromList(options) - .toStringMapper(toStringMapper) - .message(message) - .includeNull() - .prompt(prompter); + return prompter.promptValue( + elements:options, + toStringMapper:toStringMapper, + message:message, + includeNull:true); +// return new PrompterValueFromList<>( +// elements:options, +// toStringMapper:toStringMapper, +// message:message, +// includeNull:true) +// .prompt(prompter); } @Override @@ -45,17 +48,23 @@ public Optional choice(Iterable options, @Nullable String message, @Nu @Override public boolean confirm(String message, String title) { - return new PrompterBoolean(message:message + " (Y/N)").prompt(prompter); + return prompter.promptBoolean(message:message + " (Y/N)"); +// return new PrompterBoolean(message:message + " (Y/N)").prompt(prompter); } @Override public Optional enter(String title, String message, @Nullable String errorMessage, @Nullable Predicate validator) { - return new PrompterString( + return prompter.promptString( message:message, errorMessage:errorMessage, - validator:validator) - .prompt(prompter); + validator:validator); + +// return new PrompterString( +// message:message, +// errorMessage:errorMessage, +// validator:validator) +// .prompt(prompter); } @Override diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java index 8d29a6d6..53315e4b 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java @@ -2,4 +2,5 @@ import java.util.function.Function; +@Deprecated public record ColumnDisplayer(String columnName, Function toStringMapper) {} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java index bd4cdb42..4c6ce254 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java @@ -8,6 +8,7 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.jspecify.annotations.Nullable; +@Deprecated public class PrompterBoolean { public final Function toObjectMapper; 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 5fdb0399..d4de6f82 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 @@ -11,6 +11,7 @@ import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; +@Deprecated class PrompterBuilderCommon { private PrompterBuilderCommon() { 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 9d0a6a0a..d7890c30 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 @@ -10,6 +10,7 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.jspecify.annotations.Nullable; +@Deprecated public class PrompterBuilderValue { private PrompterBuilderValue() { 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 deleted file mode 100644 index a228b53a..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValueFromList.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import com.google.common.collect.Lists; -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 org.jspecify.annotations.Nullable; - -public class PrompterBuilderValueFromList { - - private PrompterBuilderValueFromList() { - // util class - } - - // --------- \\ - // Interface \\ - // --------- \\ - - public static ValueFromListPromptBuilderIntf getStringFromList(Iterable elements) { - return getElementFromList(elements).toStringMapper(Function.identity()); - } - - public static ValueFromListToStringMapperBuilderIntf getElementFromList(Iterable elements) { - return new ValueFromListBuilder<>(elements); - } - - public static ValueFromListToStringMapperBuilderIntf getElementFromList(T[] elements) { - return new ValueFromListBuilder<>(Arrays.asList(elements)); - } - - public interface ValueFromListToStringMapperBuilderIntf { - ValueFromListPromptBuilderIntf toStringMapper(Function toStringMapper); - - ValueFromListPromptBuilderIntf displayAsTable(TableDisplayer tableDisplayer); - } - - public interface ValueFromListPromptBuilderIntf { - ValueFromListPromptBuilderIntf sort(Comparator comparator); - - ValueFromListPromptBuilderIntf message(String message, Object... replacements); - - ValueFromListPromptBuilderIntf includeNull(); - - ValueFromListPromptBuilderIntf includeNull(T emptyValue); - - Optional prompt(Prompter prompter); - } - - // ------- \\ - // Builder \\ - // ------- \\ - - @Setter - @Accessors(fluent = true, chain = true) - public static class ValueFromListBuilder - implements ValueFromListToStringMapperBuilderIntf, ValueFromListPromptBuilderIntf { - private final List elements; - private Function toStringMapper = String::valueOf; - private @Nullable String message; - private boolean includeNull; - private T emptyValue; - private @Nullable TableDisplayer tableDisplayer; - - ValueFromListBuilder(Iterable elements) { - this.elements = Lists.newArrayList(elements); - } - - @Override - public ValueFromListBuilder sort(Comparator comparator) { - elements.sort(comparator); - return this; - } - - @Override - public ValueFromListBuilder message(String message, Object... replacements) { - this.message = message.formatted(replacements); - return this; - } - - @Override - public ValueFromListBuilder includeNull() { - this.includeNull = true; - return this; - } - - @Override - public ValueFromListBuilder includeNull(T emptyValue) { - this.includeNull = true; - this.emptyValue = emptyValue; - return this; - } - - @Override - public ValueFromListBuilder displayAsTable(TableDisplayer tableDisplayer) { - this.tableDisplayer = tableDisplayer; - return this; - } - - @Override - public Optional prompt(Prompter prompter) { - try { - String value; - if (tableDisplayer != null) { - tableDisplayer.display(elements); - value = prompter.prompt(message); - } else { - String choicesMessage = IntStream.range(0, elements.size()) - .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); - } - if (StringUtils.isBlank(value) && includeNull) { - return Optional.ofNullable(emptyValue); - } - 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()); - return prompt(prompter); - } - return Optional.ofNullable(elements.get(number - 1)); - } catch (PrompterException e) { - throw new IllegalStateException(e); - } catch (NumberFormatException e) { - return prompt(PrompterUtil.showMessage(prompter, "Enter a valid number, try again.")); - } - } - } - -} 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 deleted file mode 100644 index 9db84523..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValuesFromList.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import com.google.common.collect.Lists; -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; - -public class PrompterBuilderValuesFromList { - - private PrompterBuilderValuesFromList() { - // util class - } - - // --------- \\ - // Interface \\ - // --------- \\ - - public static ValuesFromListPromptBuilderIntf getStringsFromList(Iterable elements) { - return getElementsFromList(elements).toStringMapper(Function.identity()); - } - - public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(Iterable elements) { - return new ValuesFromListBuilder<>(elements); - } - - public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(T[] elements) { - return new ValuesFromListBuilder<>(Arrays.asList(elements)); - } - - public interface ValuesFromListToStringMapperBuilderIntf { - ValuesFromListPromptBuilderIntf toStringMapper(Function toStringMapper); - - ValuesFromListPromptBuilderIntf displayAsTable(TableDisplayer tableDisplayer); - } - - public interface ValuesFromListPromptBuilderIntf { - ValuesFromListPromptBuilderIntf sort(Comparator comparator); - - ValuesFromListPromptBuilderIntf message(String message, Object... replacements); - - ValuesFromListPromptBuilderIntf includeNull(); - - List prompt(Prompter prompter); - } - - // ------- \\ - // Builder \\ - // ------- \\ - - @Setter - @Accessors(fluent = true, chain = true) - public static class ValuesFromListBuilder - implements ValuesFromListToStringMapperBuilderIntf, ValuesFromListPromptBuilderIntf { - private final List elements; - private Function toStringMapper; - private String message; - private boolean includeNull; - private TableDisplayer tableDisplayer; - - ValuesFromListBuilder(Iterable elements) { - this.elements = Lists.newArrayList(elements); - } - - @Override - public ValuesFromListBuilder sort(Comparator comparator) { - elements.sort(comparator); - return this; - } - - @Override - public ValuesFromListBuilder message(String message, Object... replacements) { - this.message = message.formatted(replacements); - return this; - } - - @Override - public ValuesFromListBuilder includeNull() { - this.includeNull = true; - return this; - } - - @Override - public ValuesFromListBuilder displayAsTable(TableDisplayer tableDisplayer) { - this.tableDisplayer = tableDisplayer; - return this; - } - - @Override - public List prompt(Prompter prompter) { - try { - String value; - if (tableDisplayer != null) { - tableDisplayer.display(elements); - value = prompter.prompt(message); - } else { - String choicesMessage = IntStream.range(0, elements.size()) - .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); - } - if (StringUtils.isBlank(value) && includeNull) { - return new ArrayList<>(); - } - if (StringUtils.isBlank(value)) { - return prompt(PrompterUtil.showMessage(prompter, "Enter a valid value, try again.")); - } - 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()); - 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."); - return prompt(prompter); - } - } - } - -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java index 9c30ea8c..8a93c691 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java @@ -9,6 +9,7 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.jspecify.annotations.Nullable; +@Deprecated public class PrompterInt { public static final Function TO_OBJECT_MAPPER = Integer::parseInt; @@ -44,4 +45,85 @@ public int prompt(Prompter prompter) { return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER, VALIDATOR, objValidator, defaultValue, defaultValueSupplier, message, errorMessage).get(); } + +// +// private PrompterBoolean() { +// // util class +// } +// +// protected static boolean getValue(Prompter prompter) { +// return new ValueBuilder().prompt(prompter); +// } +// +// protected static ValueBuilderOtherMapperIntf getValue() { +// return new ValueBuilder(); +// } +// +// public static ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue) { +// return new ValueBuilder().defaultValue(defaultValue); +// } +// +// public static ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier) { +// return new ValueBuilder().defaultValueSupplier(defaultValueSupplier); +// } +// +// public static ValueBuilderOtherMapperIntf message(String message, Object... replacements) { +// return new ValueBuilder().message(message, replacements); +// } +// +// public interface ValueBuilderOtherMapperIntf { +// +// ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue); +// +// ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier); +// +// ValueBuilderOtherMapperIntf message(String message, Object... replacements); +// +// ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); +// +// boolean prompt(Prompter prompter); +// } +// +// // ------- \\ +// // Builder \\ +// // ------- \\ +// +// @Setter +// @Accessors(fluent = true, chain = true) +// public static class ValueBuilder implements ValueBuilderOtherMapperIntf { +// public static final Predicate TO_OBJECT_MAPPER = "y"::equalsIgnoreCase; +// public static final Predicate VALIDATOR = v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v); +// private Boolean defaultValue; +// private BooleanSupplier defaultValueSupplier; +// private String message; +// private String errorMessage; +// +// private ValueBuilder() { +// // hide constructor +// } +// +// @Override +// public ValueBuilder defaultValue(boolean defaultValue) { +// this.defaultValue = defaultValue; +// return this; +// } +// +// @Override +// public ValueBuilder message(String message, Object... replacements) { +// this.message = message.formatted(replacements); +// return this; +// } +// +// @Override +// public ValueBuilder errorMessage(String errorMessage, Object... replacements) { +// this.errorMessage = errorMessage.formatted(replacements); +// return this; +// } +// +// @Override +// public boolean prompt(Prompter prompter) { +// return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER::test, VALIDATOR, null, defaultValue, +// defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, errorMessage).get(); +// } +// } } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java index 6421931c..cf80633c 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java @@ -9,6 +9,7 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.jspecify.annotations.Nullable; +@Deprecated public class PrompterString { public final Predicate validator; 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 deleted file mode 100644 index bcaa7d75..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterUtil.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.Function; - -import org.codehaus.plexus.components.interactivity.Prompter; -import org.codehaus.plexus.components.interactivity.PrompterException; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValueFromList.ValueFromListPromptBuilderIntf; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValueFromList.ValueFromListToStringMapperBuilderIntf; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValuesFromList.ValuesFromListPromptBuilderIntf; -import org.lodder.subtools.sublibrary.util.prompter.PrompterBuilderValuesFromList.ValuesFromListToStringMapperBuilderIntf; - -public class PrompterUtil { - - private PrompterUtil() { - // util class - } - - public static Prompter showMessage(Prompter prompter, String message, Object... replacements) { - try { - prompter.showMessage(message.formatted(replacements)); - return prompter; - } catch (PrompterException e) { - throw new IllegalStateException(e); - } - } - - public static void pressAnyKeyToContinue(Prompter prompter) { - new PrompterString(message:"Press any key to continue", defaultValue:"").prompt(prompter); - } - - - - // ######################### \\ - // ## Get Value From List ## \\ - // ######################### \\ - - public static ValueFromListPromptBuilderIntf getStringFromList(Iterable elements) { - return PrompterBuilderValueFromList.getElementFromList(elements).toStringMapper(Function.identity()); - } - - public static ValueFromListToStringMapperBuilderIntf getElementFromList(Iterable elements) { - return PrompterBuilderValueFromList.getElementFromList(elements); - } - - public static ValueFromListToStringMapperBuilderIntf getElementFromList(T[] elements) { - return PrompterBuilderValueFromList.getElementFromList(elements); - } - - // ########################## \\ - // ## Get Values From List ## \\ - // ########################## \\ - - public static ValuesFromListPromptBuilderIntf getStringsFromList(Iterable elements) { - return PrompterBuilderValuesFromList.getStringsFromList(elements); - } - - public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(Iterable elements) { - return PrompterBuilderValuesFromList.getElementsFromList(elements); - } - - public static ValuesFromListToStringMapperBuilderIntf getElementsFromList(T[] elements) { - return PrompterBuilderValuesFromList.getElementsFromList(elements); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java new file mode 100644 index 00000000..642b0679 --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java @@ -0,0 +1,78 @@ +package org.lodder.subtools.sublibrary.util.prompter; + +import static com.pivovarit.gatherers.MoreGatherers.*; +import static java.lang.System.*; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.codehaus.plexus.components.interactivity.Prompter; +import org.codehaus.plexus.components.interactivity.PrompterException; +import org.jspecify.annotations.Nullable; + +@Deprecated +public class PrompterValueFromList { + + private final Iterable elements; + private final Function toStringMapper; + private final String message; + private final boolean includeNull; + private final T emptyValue; + @Nullable private final TableDisplayer tableDisplayer; + @Nullable private final Comparator comparator; + + public PrompterValueFromList( + Iterable elements, + Function toStringMapper, + String message, + boolean includeNull, + T emptyValue=null, + @Nullable TableDisplayer tableDisplayer=null, + @Nullable Comparator sort=null + ) { + this.elements = elements; + this.toStringMapper = toStringMapper; + this.message = message; + this.includeNull = includeNull; + this.emptyValue = emptyValue; + this.tableDisplayer = tableDisplayer; + this.comparator = sort; + } + + public Optional prompt(Prompter prompter) { + List sortedElements = + comparator == null ? elements.stream().toList() : elements.stream().sorted(comparator).toList(); + try { + String value; + if (tableDisplayer != null) { + tableDisplayer.display(sortedElements); + value = prompter.prompt(message); + } else { + String choicesMessage = sortedElements.stream() + .gather(zipWithIndex()) + .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) + .collect(Collectors.joining(lineSeparator())) + lineSeparator(); + value = prompter.prompt( + StringUtils.isBlank(message) ? choicesMessage : message + lineSeparator() + choicesMessage); + } + if (StringUtils.isBlank(value) && includeNull) { + return Optional.ofNullable(emptyValue); + } + int number = Integer.parseInt(value); + if (number < 1 || number > sortedElements.size()) { + prompter.show("The entered value isn't in the range [1, %s], try again.", sortedElements.size()); + return prompt(prompter); + } + return Optional.ofNullable(sortedElements.get(number - 1)); + } catch (PrompterException e) { + throw new IllegalStateException(e); + } catch (NumberFormatException e) { + prompter.show("Enter a valid number, try again."); + return prompt(prompter); + } + } +} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java new file mode 100644 index 00000000..307e1474 --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java @@ -0,0 +1,83 @@ +package org.lodder.subtools.sublibrary.util.prompter; + +import static com.pivovarit.gatherers.MoreGatherers.*; +import static java.lang.System.*; + +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.codehaus.plexus.components.interactivity.Prompter; +import org.codehaus.plexus.components.interactivity.PrompterException; +import org.jspecify.annotations.Nullable; + +@Deprecated +public class PrompterValuesFromList { + + private final Iterable elements; + private final Function toStringMapper; + private final String message; + private final boolean includeNull; + @Nullable private final TableDisplayer tableDisplayer; + @Nullable private final Comparator comparator; + + // TODO: tableDisplayer or ToStingMapper + public PrompterValuesFromList( + Iterable elements, + Function toStringMapper = String::valueOf, + String message, + boolean includeNull, + @Nullable TableDisplayer tableDisplayer=null, + @Nullable Comparator sort=null + ) { + this.elements = elements; + this.toStringMapper = toStringMapper; + this.message = message; + this.includeNull = includeNull; + this.tableDisplayer = tableDisplayer; + this.comparator = sort; + } + + public List prompt(Prompter prompter) { + List sortedElements = + comparator == null ? elements.stream().toList() : elements.stream().sorted(comparator).toList(); + try { + String value; + if (tableDisplayer != null) { + tableDisplayer.display(sortedElements); + value = prompter.prompt(message); + } else { + String choicesMessage = sortedElements.stream() + .gather(zipWithIndex()) + .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) + .collect(Collectors.joining(lineSeparator())) + lineSeparator(); + value = prompter.prompt( + StringUtils.isBlank(message) ? choicesMessage : message + lineSeparator() + choicesMessage); + } + if (StringUtils.isBlank(value) && includeNull) { + return List.of(); + } + if (StringUtils.isBlank(value)) { + prompter.show("Enter a valid value, try again."); + return prompt(prompter); + } + int[] choices = value.split(",").stream().mapToInt(Integer::parseInt).map(i -> i - 1).toArray(); + if (choices.stream().distinct().count() != choices.length) { + prompter.show("Choose all distinct options, try again."); + return prompt(prompter); + } + if (choices.stream().anyMatch(number -> number < 0 || number > sortedElements.size() - 1)) { + prompter.show("The entered number(s) aren't in the range [1, %s], try again.", sortedElements.size()); + return prompt(prompter); + } + return choices.stream().map(sortedElements::get).collect(Collectors.toList()); + } catch (PrompterException e) { + throw new IllegalStateException(e); + } catch (NumberFormatException e) { + prompter.show("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/TableDisplayer.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java index 013950fb..1fe2695a 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 @@ -5,6 +5,7 @@ import dnl.utils.text.table.TextTable; import lombok.RequiredArgsConstructor; +@Deprecated @RequiredArgsConstructor public class TableDisplayer { @@ -25,8 +26,7 @@ public final void display(T... tableElements) { tt.printTable(); } - public void display(List tableElements) { - + public void display(Iterable tableElements) { String[] columnNames = columnDisplayers.stream().map(ColumnDisplayer::columnName).toArray(String[]::new); Object[][] dataTable = tableElements.stream() .map(tableElement -> columnDisplayers.stream() From 425c4217e9a3819842918453162550e601f5953f Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 21:39:10 +0200 Subject: [PATCH 24/58] temporary commit --- .../UserInteractionHandlerCLI.java | 16 +- SubLibrary/pom.xml | 6 +- .../java/lang/String/StringExt.java | 15 + .../interactivity/Prompter/PrompterExt.java | 387 ++++++++++++------ .../sublibrary/data/imdb/ImdbAdapter.java | 3 +- .../subtools/sublibrary/gui/InputPane.java | 281 ++++++------- .../subtools/sublibrary/gui/OptionsPane.java | 7 +- .../UserInteractionHandler.java | 15 +- .../UserInteractionHandlerCLI.java | 60 +-- .../UserInteractionHandlerGUI.java | 32 +- .../subtools/sublibrary/util/Validator.java | 15 + .../util/prompter/ColumnDisplayer.java | 6 - .../util/prompter/PrompterBoolean.java | 121 ------ .../util/prompter/PrompterBuilderCommon.java | 57 --- .../util/prompter/PrompterBuilderValue.java | 104 ----- .../sublibrary/util/prompter/PrompterInt.java | 129 ------ .../util/prompter/PrompterString.java | 38 -- .../util/prompter/PrompterValueFromList.java | 78 ---- .../util/prompter/PrompterValuesFromList.java | 83 ---- .../util/prompter/TableDisplayer.java | 42 -- .../resourcebundle/Message.properties | 7 + .../resourcebundle/Message_nl.properties | 7 + pom.xml | 5 + 23 files changed, 517 insertions(+), 997 deletions(-) create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Validator.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderCommon.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValue.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java 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 9b5684aa..e0bc02cd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java @@ -31,19 +31,13 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { public List selectSubtitles(Release release) { System.out.printf("\n%s : %s%n", getText("SelectDialog.SelectCorrectSubtitleThisRelease"), release.fileName); -// return new promptValues<>( -// elements:release.getMatchingSubs(), -// tableDisplayer:createTableDisplayer(), -// message:getText("SelectDialog.EnterListSelectedSubtitles"), -// sort:Comparator.comparing(Subtitle::getScore), -// includeNull:true) -// .prompt(prompter); - return prompter.promptValues( + return prompter.promptValuesFromList( + message:getText("SelectDialog.EnterListSelectedSubtitles"), elements:release.getMatchingSubs(), + toStringMapper:Subtitle::getFileName, + includeNull:true, tableDisplayer:createTableDisplayer(), - message:getText("SelectDialog.EnterListSelectedSubtitles"), - sorter:Comparator.comparing(Subtitle::getScore), - includeNull:true); + sorter:Comparator.comparing(Subtitle::getScore)); } private PrompterExt.ColumnDisplayer createSubtitleDisplayer(SubtitleTableColumnName column, diff --git a/SubLibrary/pom.xml b/SubLibrary/pom.xml index 40365af9..ca3c0ebc 100644 --- a/SubLibrary/pom.xml +++ b/SubLibrary/pom.xml @@ -118,7 +118,11 @@ net.jodah typetools - + + com.pivovarit + more-gatherers + + org.mockito mockito-core diff --git a/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java b/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java index 78e7026d..0e0d1750 100644 --- a/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java +++ b/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java @@ -5,11 +5,14 @@ import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.function.Function; import lombok.experimental.UtilityClass; import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; @UtilityClass @Extension @@ -30,4 +33,16 @@ public static String urlEncode(@This String text) { public static InputStream toInputStream(@This String text, Charset charset) { return new ByteArrayInputStream(text.getBytes(charset)); } + + public static Optional parseAsNumber(@This @Nullable String text, + Function mapper) { + if (text == null) { + return Optional.empty(); + } + try { + return Optional.of(mapper.apply(text)); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } } diff --git a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java index 6c6d35bf..05067daf 100644 --- a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java +++ b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java @@ -4,15 +4,16 @@ import static java.lang.System.*; import static org.lodder.subtools.multisubdownloader.Messages.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Optional; -import java.util.function.BooleanSupplier; +import java.util.OptionalInt; import java.util.function.Function; -import java.util.function.IntPredicate; -import java.util.function.IntSupplier; -import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; import dnl.utils.text.table.TextTable; @@ -21,14 +22,26 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.function.TriFunction; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; import org.jspecify.annotations.Nullable; +import org.lodder.subtools.sublibrary.util.Validator; @Extension @UtilityClass public class PrompterExt { + public static Validator NON_BLANK_VALIDATOR = + new Validator<>(StringUtils::isNotBlank, getText("Prompter.ValueNonBlank")); + public static Validator INT_VALIDATOR = + new Validator<>(v -> v.parseAsNumber(Integer::parseInt).isPresent()); + public static Validator BOOLEAN_VALIDATOR = + new Validator<>(v -> getText("Prompter.YesAbbreviation").equalsIgnoreCase(v) || + getText("Prompter.Yes").equalsIgnoreCase(v) || + getText("Prompter.NoAbbreviation").equalsIgnoreCase(v) || + getText("Prompter.No").equalsIgnoreCase(v)); + public static void show(@This Prompter prompter, String message, Object... replacements) { try { prompter.showMessage(message.formatted(replacements)); @@ -38,170 +51,251 @@ public static void show(@This Prompter prompter, String message, Object... repla } public static void pressAnyKeyToContinue(@This Prompter prompter) { - prompter.promptString(message:"Press any key to continue", defaultValue:""); -// new PrompterString(message:"Press any key to continue", defaultValue:"").prompt(prompter); + prompt(prompter:prompter, message:"Press any key to continue", toObjectMapper:Function.identity()); } - public static Optional promptString(@This Prompter prompter, - @Nullable Predicate validator=StringUtils::isNotBlank, - @Nullable String defaultValue=null, - @Nullable Supplier defaultValueSupplier=null, - String message, - @Nullable String errorMessage=null) { + public static Optional promptString(@This Prompter prompter, String message, + List> inputValidators=List.of(NON_BLANK_VALIDATOR), + List> objectValidators=new ArrayList>()) { - return prompt(prompter, Function.identity(), validator, null, defaultValue, defaultValueSupplier, message, - errorMessage); + return prompt(prompter, message, inputValidators, Function.identity(), objectValidators); } - public static Optional promptInt(@This Prompter prompter, - @Nullable IntPredicate validator=_ -> true, - @Nullable Integer defaultValue=null, - @Nullable IntSupplier defaultValueSupplier=null, - String message, - @Nullable String errorMessage=null) { - - Predicate isIntValidator = v -> { - try { - Integer.parseInt(v); - return true; - } catch (NumberFormatException e) { - return false; - } - }; - return prompt(prompter, Integer::parseInt, isIntValidator, validator == null ? null : validator::test, - defaultValue, defaultValueSupplier == null ? null : defaultValueSupplier::getAsInt, message, errorMessage); + public static OptionalInt promptInt(@This Prompter prompter, String message, + List> inputValidators=List.of(NON_BLANK_VALIDATOR, INT_VALIDATOR), + Function toObjectMapper=Integer::parseInt, + List> objectValidators=new ArrayList>()) { + + return prompt(prompter, message, inputValidators, toObjectMapper, objectValidators).mapToInt(v -> v); } - public static Optional promptBoolean(@This Prompter prompter, - Predicate toObjectMapper="y"::equalsIgnoreCase, - @Nullable Predicate validator=v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v), - @Nullable Boolean defaultValue=null, - @Nullable BooleanSupplier defaultValueSupplier=null, - String message, - @Nullable String errorMessage=null) { + public static Optional promptBoolean(@This Prompter prompter, String message, + List> inputValidators=List.of(NON_BLANK_VALIDATOR, BOOLEAN_VALIDATOR)) { - return prompt(prompter, toObjectMapper::test, validator, null, - defaultValue, defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, - errorMessage); + return prompt(prompter, message, inputValidators, Boolean::parseBoolean, List.of()); } - public static Optional promptValue(@This Prompter prompter, +// public static Optional promptValueFromList(@This Prompter prompter, +// String message, +// Iterable elements, +// Function toStringMapper=String::valueOf, +// boolean includeNull, +// @Nullable TableDisplayer tableDisplayer=null, +// @Nullable Comparator sorter=null) { +// +// List sortedElements = +// sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); +// String choicesMessage; +// if (tableDisplayer != null) { +// choicesMessage = tableDisplayer.getAsString(sortedElements); +// } else { +// choicesMessage = message + lineSeparator() + +// sortedElements.stream() +// .gather(zipWithIndex()) +// .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) +// .collect(Collectors.joining(lineSeparator())) + +// lineSeparator(); +// } +// +// int numberOfElements = sortedElements.size(); +// List> inputValidators = new ArrayList<>(); +// if (!includeNull) { +// inputValidators.add(NON_BLANK_VALIDATOR); +// } +// inputValidators.add(new Validator<>(v -> v == null || v.parseAsNumber(Integer::parseUnsignedInt).isPresent())); +// +// Function toObjectMapper = v -> v == null ? null : Integer.parseUnsignedInt(v); +// +// List> objectValidators = +// List.of(new Validator<>(number -> number == null || (number > 0 && number <= numberOfElements), +// getText("Prompter.ValueNotInRange", numberOfElements))); +// +// return prompter.promptInt(choicesMessage, inputValidators, toObjectMapper, objectValidators) +// .mapToObj(idx -> sortedElements.get(idx - 1)); +// } +// +// +// public static List promptValuesFromList(@This Prompter prompter, +// String message, +// Iterable elements, +// Function toStringMapper=String::valueOf, +// boolean includeNull, +// @Nullable TableDisplayer tableDisplayer=null, +// @Nullable Comparator sorter=null) { +// +// List sortedElements = +// sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); +// String choicesMessage; +// if (tableDisplayer != null) { +// choicesMessage = tableDisplayer.getAsString(sortedElements); +// } else { +// choicesMessage = message + lineSeparator() + +// sortedElements.stream() +// .gather(zipWithIndex()) +// .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) +// .collect(Collectors.joining(lineSeparator())) + +// lineSeparator(); +// } +// +// int numberOfElements = sortedElements.size(); +// List> inputValidators = new ArrayList<>(); +// if (!includeNull) { +// inputValidators.add(NON_BLANK_VALIDATOR); +// } +// inputValidators.add( +// new Validator<>(v -> v == null || +// v.split(",").stream().allMatch(n -> n.parseAsNumber(Integer::parseUnsignedInt).isPresent()))); +// +// Function toObjectsMapper = +// v -> v.split(",").stream().mapToInt(Integer::parseUnsignedInt).toArray(); +// +// List> objectValidators = List.of( +// new Validator<>(numbers -> numbers.stream().distinct().count() == numbers.stream().count(), +// getText("Prompter.DistinctValues")), +// new Validator<>(numbers -> numbers.stream().allMatch(number -> number > 0 && number <= numberOfElements), +// getText("Prompter.ValueNotInRange", numberOfElements))); +// +// return prompter.promptValues(choicesMessage, inputValidators, toObjectsMapper, objectValidators).stream() +// .map(idx -> sortedElements.get(idx - 1)).toList(); +// } + + public static Optional promptValueFromList(@This Prompter prompter, + String message, Iterable elements, Function toStringMapper=String::valueOf, - String message, boolean includeNull, - T emptyValue=null, @Nullable TableDisplayer tableDisplayer=null, @Nullable Comparator sorter=null) { - List sortedElements = - sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); - try { - String value = promptFromListIndices(prompter, sortedElements, tableDisplayer, toStringMapper, message); - if (StringUtils.isBlank(value) && includeNull) { - return Optional.ofNullable(emptyValue); - } - int number = Integer.parseInt(value); - if (number < 1 || number > sortedElements.size()) { - prompter.show("The entered value isn't in the range [1, %s], try again.", sortedElements.size()); - return prompter.promptValue(elements, toStringMapper, message, includeNull, emptyValue, - tableDisplayer, sorter); - } - return Optional.ofNullable(sortedElements.get(number - 1)); - } catch (NumberFormatException e) { - prompter.show("Enter a valid number, try again."); - return prompter.promptValue(elements, toStringMapper, message, includeNull, emptyValue, - tableDisplayer, sorter); - } + Validator inputValidator = + new Validator<>(v -> v == null || v.parseAsNumber(Integer::parseUnsignedInt).isPresent()); + Function toObjectMapper = v -> v == null ? null : Integer.parseUnsignedInt(v); + int numberOfElements = elements.size(); + List> objectValidators = + List.of(new Validator<>(number -> number == null || (number > 0 && number <= numberOfElements), + getText("Prompter.ValueNotInRange", numberOfElements))); + + TriFunction>, List, Optional> promptFunction = (choicesMessage, + inputValidators, sortedElements) -> + // TODO use extension method + PrompterExt.promptInt(prompter, choicesMessage, inputValidators, toObjectMapper, objectValidators) + .mapToObj(idx -> sortedElements.get(idx - 1)); + + return promptFromList(message, elements, toStringMapper, includeNull, tableDisplayer, sorter, + List.of(inputValidator), promptFunction); } - public static List promptValues(@This Prompter prompter, + public static List promptValuesFromList(@This Prompter prompter, + String message, Iterable elements, Function toStringMapper=String::valueOf, - String message, boolean includeNull, @Nullable TableDisplayer tableDisplayer=null, @Nullable Comparator sorter=null) { + Validator inputValidator = new Validator<>(v -> v == null || + v.split(",").stream().allMatch(n -> n.parseAsNumber(Integer::parseUnsignedInt).isPresent())); + Function toObjectsMapper = + v -> v.split(",").stream().mapToInt(Integer::parseUnsignedInt).toArray(); + int numberOfElements = elements.size(); + List> objectValidators = List.of( + new Validator<>(numbers -> numbers.stream().distinct().count() == numbers.stream().count(), + getText("Prompter.DistinctValues")), + new Validator<>(numbers -> numbers.stream().allMatch(number -> number > 0 && number <= numberOfElements), + getText("Prompter.ValueNotInRange", numberOfElements))); + + TriFunction>, List, List> promptFunction = (choicesMessage, + inputValidators, sortedElements) -> + // TODO use extension method + PrompterExt.promptValues(prompter, choicesMessage, inputValidators, toObjectsMapper, objectValidators) + .stream() + .map(idx -> sortedElements.get(idx - 1)).toList(); + + return promptFromList(message, elements, toStringMapper, includeNull, tableDisplayer, sorter, + List.of(inputValidator), promptFunction); + } + + private static R promptFromList( + String message, + Iterable elements, + Function toStringMapper=String::valueOf, + boolean includeNull, + @Nullable TableDisplayer tableDisplayer=null, + @Nullable Comparator sorter=null, + List> inputValidators=new ArrayList>(), + TriFunction>, List, R> promptFunction) { + List sortedElements = sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); - try { - String value = promptFromListIndices(prompter, sortedElements, tableDisplayer, toStringMapper, message); - - if (StringUtils.isBlank(value) && includeNull) { - return List.of(); - } - if (StringUtils.isBlank(value)) { - prompter.show("Enter a valid value, try again."); - return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); - } - int[] choices = value.split(",").stream().mapToInt(Integer::parseInt).map(i -> i - 1).toArray(); - if (choices.stream().distinct().count() != choices.length) { - prompter.show("Choose all distinct options, try again."); - return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); - } - if (choices.stream().anyMatch(number -> number < 0 || number > sortedElements.size() - 1)) { - prompter.show("The entered number(s) aren't in the range [1, %s], try again.", sortedElements.size()); - return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); - } - return choices.stream().map(sortedElements::get).collect(Collectors.toList()); - } catch (NumberFormatException e) { - prompter.show("Invalid number(s) encountered. Enter a comma separated list of the choices."); - return prompter.promptValues(elements, toStringMapper, message, includeNull, tableDisplayer, sorter); + String choicesMessage; + if (tableDisplayer != null) { + choicesMessage = tableDisplayer.getAsString(sortedElements); + } else { + choicesMessage = message + lineSeparator() + + sortedElements.stream() + .gather(zipWithIndex()) + .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) + .collect(Collectors.joining(lineSeparator())) + + lineSeparator(); + } + List> allInputValidators = new ArrayList<>(); + if (!includeNull) { + allInputValidators.add(NON_BLANK_VALIDATOR); } + allInputValidators.addAll(inputValidators); + + return promptFunction.apply(choicesMessage, allInputValidators, sortedElements); } - private static String promptFromListIndices(Prompter prompter, Iterable elements, - @Nullable TableDisplayer tableDisplayer, Function toStringMapper, - String message) { + private static Optional prompt(Prompter prompter, String message, + List> inputValidators=new ArrayList>(), + Function toObjectMapper, + List> objectValidators=new ArrayList>()) { try { - if (tableDisplayer != null) { - tableDisplayer.display(elements); - return prompter.prompt(message); - } else { - String choicesMessage = elements.stream() - .gather(zipWithIndex()) - .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) - .collect(Collectors.joining(lineSeparator())) + lineSeparator(); - return prompter.prompt( - StringUtils.isBlank(message) ? choicesMessage : message + lineSeparator() + choicesMessage); + String value = prompter.prompt(message + System.lineSeparator()); + for (Validator inputValidator : inputValidators) { + if (inputValidator.isInvalid(value)) { + prompter.showMessage(inputValidator.errorMessage); + return prompt(prompter, message, inputValidators, toObjectMapper, objectValidators); + } + } + T object = toObjectMapper.apply(value); + for (Validator objectValidator : objectValidators) { + if (objectValidator.isInvalid(object)) { + prompter.showMessage(objectValidator.errorMessage); + return prompt(prompter, message, inputValidators, toObjectMapper, objectValidators); + } } + return Optional.ofNullable(object); } catch (PrompterException e) { throw new IllegalStateException(e); } } - private static Optional prompt(Prompter prompter, Function toObjectMapper, - @Nullable Predicate validator=null, @Nullable Predicate objValidator=null, - @Nullable T defaultValue=null, @Nullable Supplier defaultValueSupplier=null, String message, - @Nullable String errorMessage=null) { + public static T promptValues(@This Prompter prompter, String message, + List> inputValidators=new ArrayList>(), + Function toObjectsMapper, + List> objectValidators=new ArrayList>()) { try { String value = prompter.prompt(message + System.lineSeparator()); - if (StringUtils.isEmpty(value)) { - if (defaultValue != null) { - return Optional.of(defaultValue); - } else if (defaultValueSupplier != null) { - return Optional.ofNullable(defaultValueSupplier.get()); - } else { - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, - message, errorMessage); - } - } else { - if (validator != null && !validator.test(value)) { - prompter.showMessage( - StringUtils.isNotBlank(errorMessage) ? errorMessage : getText("Prompter.ValueIsNotValid")); - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, - message, errorMessage); + for (Validator inputValidator : inputValidators) { + if (inputValidator.isInvalid(value)) { + prompter.showMessage(inputValidator.errorMessage); + // TODO use extension method + return PrompterExt.promptValues(prompter, message, inputValidators, toObjectsMapper, + objectValidators); } - T object = toObjectMapper.apply(value); - if (objValidator != null && !objValidator.test(object)) { - prompter.showMessage( - StringUtils.isNotBlank(errorMessage) ? errorMessage : getText("Prompter.ValueIsNotValid")); - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, - message, errorMessage); + } + T objects = toObjectsMapper.apply(value); + for (Validator objectValidator : objectValidators) { + if (objectValidator.isInvalid(objects)) { + prompter.showMessage(objectValidator.errorMessage); + // TODO use extension method + return PrompterExt.promptValues(prompter, message, inputValidators, toObjectsMapper, + objectValidators); } - return Optional.ofNullable(object); } + return objects; } catch (PrompterException e) { throw new IllegalStateException(e); } @@ -228,6 +322,17 @@ public final void display(T... tableElements) { } public void display(Iterable tableElements) { + writeToPrintStream(tableElements, System.out); + } + + public String getAsString(Iterable tableElements) { + LineReadingOutputStream lineReadingOutputStream = new LineReadingOutputStream(); + PrintStream printStream = new PrintStream(lineReadingOutputStream); + writeToPrintStream(tableElements, printStream); + return lineReadingOutputStream.toString(); + } + + private void writeToPrintStream(Iterable tableElements, PrintStream printStream) { String[] columnNames = columnDisplayers.stream().map(ColumnDisplayer::columnName).toArray(String[]::new); Object[][] dataTable = tableElements.stream() .map(tableElement -> columnDisplayers.stream() @@ -238,10 +343,32 @@ public void display(Iterable tableElements) { TextTable tt = new TextTable(columnNames, dataTable); // this adds the numbering on the left tt.setAddRowNumbering(true); - tt.printTable(); + tt.printTable(printStream, 0); } + + + } + + private static class LineReadingOutputStream extends OutputStream { + private final ByteArrayOutputStream byteArrayOutputStream; + + // Constructor + public LineReadingOutputStream() { + this.byteArrayOutputStream = new ByteArrayOutputStream(); + } + + // Write method, called when you write data to the stream + @Override + public void write(int b) throws IOException { + byteArrayOutputStream.write(b); + } + + public String toString() { + return byteArrayOutputStream.toString(); + } + } - public record ColumnDisplayer(String columnName, Function toStringMapper) { + public record ColumnDisplayer(String columnName, Function toStringMapper) { } } 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 fef7afc6..4649ed32 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 @@ -122,8 +122,7 @@ private OptionalInt getImdbIdCommon(String title, Integer year, } private OptionalInt promptUserToEnterImdbId(String title, int year) { - return userInteractionHandler.enterNumber(PROVIDER_NAME, - getText("Prompter.EnterImdbMatchForSerie", title), getText("Prompter.ValueIsNotValid")); + return userInteractionHandler.enterNumber(PROVIDER_NAME, getText("Prompter.EnterImdbMatchForSerie", title)); } public static synchronized ImdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { 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 a1bdee72..189ec583 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,6 +1,9 @@ package org.lodder.subtools.sublibrary.gui; +import static org.lodder.subtools.multisubdownloader.Messages.*; + import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; @@ -9,176 +12,156 @@ import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.Serial; +import java.util.ArrayList; +import java.util.List; 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 { - - private InputPane() { - // hide constructor - } - - public static InputPaneBuilderTitleIntf create() { - return create(null); - } - - public static InputPaneBuilderTitleIntf create(JFrame parent) { - return new InputPaneBuilder(parent); +import java.util.function.Function; + +import org.apache.commons.lang3.StringUtils; +import org.lodder.subtools.sublibrary.util.Validator; + +public class InputPane extends JDialog implements ActionListener, PropertyChangeListener { + + private static final long serialVersionUID = 1L; + private static final String OK = getText("App.OK"); + private static final String CANCEL = getText("App.Cancel"); + private final String message; + private final List> inputValidators; + private final Function toObjectMapper; + private final List> objectValidators; + private final String okText; + private final String cancelText; + + // + private JTextField textField; + private JOptionPane optionPane; + private T input; + + public InputPane(Frame owner=null, + String title, + String message, + List> inputValidators=new ArrayList>(), + Function toObjectMapper, + List> objectValidators=new ArrayList>(), + String okText=OK, + String cancelText=CANCEL) { + + super(owner, true); + setTitle(title); + this.message = message; + this.inputValidators = inputValidators; + this.toObjectMapper = toObjectMapper; + this.objectValidators = objectValidators; + this.okText = okText; + this.cancelText = cancelText; } - public interface InputPaneBuilderTitleIntf { - InputPaneBuilderMessageIntf title(String title); - } - - public interface InputPaneBuilderMessageIntf { - InputPaneBuilderErrorMessageIntf message(String message); - } + public Optional prompt() { + textField = new JTextField(10); + + // Create an array of the text and components to be displayed. + Object[] array = {message, textField}; + Object[] options = {okText, cancelText}; + // Create the JOptionPane. + optionPane = new JOptionPane(array, + JOptionPane.INFORMATION_MESSAGE, + JOptionPane.OK_CANCEL_OPTION, + null, options, options[0]); + + optionPane.selectInitialValue(); + + // Make this dialog display it. + setContentPane(optionPane); + + // Handle window closing correctly. + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent we) { + // Instead of directly closing the window, we're going to change the JOptionPane's value property. + optionPane.setValue(JOptionPane.CLOSED_OPTION); + } + }); - public interface InputPaneBuilderErrorMessageIntf { - InputPaneBuilderValidatorIntf errorMessage(String errorMessage); - } + // Ensure the text field always gets the first focus. + addComponentListener(new ComponentAdapter() { + @Override + public void componentShown(ComponentEvent ce) { + textField.requestFocusInWindow(); + } + }); - public interface InputPaneBuilderValidatorIntf extends InputPaneBuilderPromptIntf { - InputPaneBuilderPromptIntf validator(Predicate validator); - } + // Register an event handler that puts the text into the option pane. + textField.addActionListener(this); - public interface InputPaneBuilderPromptIntf { - InputPaneBuilderPromptIntf okText(String okText); + // Register an event handler that reacts to option pane state changes. + optionPane.addPropertyChangeListener(this); - InputPaneBuilderPromptIntf cancelText(String cancelText); + pack(); + setVisible(true); - Optional prompt(); + return Optional.ofNullable(input); } - @Setter - @Accessors(fluent = true) - public static class InputPaneBuilder extends JDialog implements ActionListener, PropertyChangeListener, - InputPaneBuilderPromptIntf, InputPaneBuilderErrorMessageIntf, InputPaneBuilderValidatorIntf, - InputPaneBuilderMessageIntf, - InputPaneBuilderTitleIntf { - @Serial - private static final long serialVersionUID = 1L; - 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; - private Predicate validator; - private String okText = OK; - private String cancelText = CANCEL; - - // - private Optional typedText = Optional.empty(); - private JTextField textField; - private JOptionPane optionPane; - - InputPaneBuilder(JFrame parent) { - super(parent, true); - } - - @Override - public Optional prompt() { - textField = new JTextField(10); - - // Create an array of the text and components to be displayed. - Object[] array = { message, textField }; - Object[] options = { okText, cancelText }; - // Create the JOptionPane. - optionPane = new JOptionPane(array, - JOptionPane.INFORMATION_MESSAGE, - JOptionPane.OK_CANCEL_OPTION, - null, options, options[0]); - - optionPane.selectInitialValue(); - - // Make this dialog display it. - setContentPane(optionPane); - - // Handle window closing correctly. - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent we) { - // Instead of directly closing the window, we're going to change the JOptionPane's value property. - optionPane.setValue(JOptionPane.CLOSED_OPTION); - } - }); - - // Ensure the text field always gets the first focus. - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent ce) { - textField.requestFocusInWindow(); - } - }); - - // Register an event handler that puts the text into the option pane. - textField.addActionListener(this); - - // Register an event handler that reacts to option pane state changes. - optionPane.addPropertyChangeListener(this); + @Override + public void propertyChange(PropertyChangeEvent e) { + String prop = e.getPropertyName(); - pack(); - setVisible(true); + if (isVisible() && e.getSource() == optionPane + && (JOptionPane.VALUE_PROPERTY.equals(prop) || JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) { - return typedText; - } + Object value = optionPane.getValue(); - @Override - public void propertyChange(PropertyChangeEvent e) { - String prop = e.getPropertyName(); + if (value == JOptionPane.UNINITIALIZED_VALUE) { + // ignore reset + return; + } - if (isVisible() - && e.getSource() == optionPane - && (JOptionPane.VALUE_PROPERTY.equals(prop) - || JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) { - Object value = optionPane.getValue(); + // Reset the JOptionPane's value. + // If this isn't done, no property change event will be fired when the button is pressed again. + optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE); - if (value == JOptionPane.UNINITIALIZED_VALUE) { - // ignore reset - return; + if (StringUtils.equals(okText, String.valueOf(value))) { + String text = textField.getText(); + for (Validator validator : inputValidators) { + if (validator.isInvalid(text)) { + invalidInputEncountered(validator.errorMessage); + return; + } } - - // Reset the JOptionPane's value. - // If you don't do this, then if the user presses the same button next time, - // no property change event will be fired. - optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE); - - if (String.valueOf(okText).equals(String.valueOf(value))) { - typedText = Optional.ofNullable(textField.getText()); - if (validator == null || typedText.map(validator::test).orElse(false)) { - exit(); - } else { - // text was invalid - textField.selectAll(); - Object[] array = { errorMessage, message, textField }; - optionPane.setMessage(array); - typedText = Optional.empty(); - textField.requestFocusInWindow(); - pack(); + input = toObjectMapper.apply(text); + for (Validator validator : objectValidators) { + if (validator.isInvalid(input)) { + invalidInputEncountered(validator.errorMessage); + return; } - } else { // user closed dialog or clicked cancel - typedText = Optional.empty(); - exit(); } + exit(); + } else { // user closed dialog or clicked cancel + input = null; + exit(); } } + } - @Override - public void actionPerformed(ActionEvent e) { - System.out.println(e); - } + private void invalidInputEncountered(String errorMessage) { + textField.selectAll(); + Object[] array = {errorMessage, message, textField}; + optionPane.setMessage(array); + input = null; + textField.requestFocusInWindow(); + pack(); + } - /** - * This method clears the dialog and hides it. - */ - public void exit() { - dispose(); - } + @Override + public void actionPerformed(ActionEvent e) { + System.out.println(e); + } + + /** + * Clears the dialog and hides it. + */ + public void exit() { + dispose(); } } 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 152a3a5f..74cb2f58 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 @@ -4,7 +4,6 @@ import java.awt.*; import java.util.Optional; import java.util.function.Function; -import java.util.stream.StreamSupport; import lombok.AllArgsConstructor; import manifold.ext.props.rt.api.val; @@ -17,15 +16,15 @@ public class OptionsPane { private final T[] options; private final @Nullable String title; - private final @Nullable String message; + private final String message; private final Option messageType; private final @Nullable Function toStringMapper; private final @Nullable Component parent; public OptionsPane(Iterable options, @Nullable Function toStringMapper=null, @Nullable String title=null, - @Nullable String message=null, Option messageType, @Nullable Component parent=null) { - this.options = (T[]) StreamSupport.stream(options.spliterator(), false).toArray(); + String message, Option messageType, @Nullable Component parent=null) { + this.options = (T[]) options.stream().toArray(); this.toStringMapper = toStringMapper; this.title = title; this.message = message; 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 4f09cfe5..ce500474 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 @@ -1,14 +1,14 @@ package org.lodder.subtools.sublibrary.userinteraction; +import java.util.List; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Function; -import java.util.function.Predicate; import manifold.ext.props.rt.api.val; -import org.apache.commons.lang3.StringUtils; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; +import org.lodder.subtools.sublibrary.util.Validator; public interface UserInteractionHandler { @@ -16,18 +16,15 @@ public interface UserInteractionHandler { boolean confirm(String message, String title); - Optional selectFromList(Iterable options, @Nullable String message=null, @Nullable String title=null, + Optional selectFromList(Iterable options, String message, @Nullable String title=null, @Nullable Function toStringMapper=null); - Optional choice(Iterable options, @Nullable String message=null, @Nullable String title=null, + Optional choice(Iterable options, String message, @Nullable String title=null, @Nullable Function toStringMapper=null); - Optional enter(String title, String message=null, @Nullable String errorMessage=null, - @Nullable Predicate validator=null); + Optional enter(String title, String message, @Nullable List> inputValidators=null); - default OptionalInt enterNumber(String title, String message, String errorMessage) { - return enter(title, message, errorMessage, StringUtils::isNumeric).mapToInt(Integer::parseInt); - } + OptionalInt enterNumber(String title, String message, @Nullable List> objectValidators=null); void showMessage(String message, String title, MessageSeverity messageSeverity); 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 b944367f..ea7718dd 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 @@ -1,9 +1,13 @@ package org.lodder.subtools.sublibrary.userinteraction; +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import java.util.List; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Function; -import java.util.function.Predicate; +import extensions.org.codehaus.plexus.components.interactivity.Prompter.PrompterExt; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.codehaus.plexus.components.interactivity.DefaultInputHandler; @@ -12,6 +16,7 @@ import org.codehaus.plexus.components.interactivity.Prompter; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; +import org.lodder.subtools.sublibrary.util.Validator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,44 +32,51 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @Override public Optional selectFromList(Iterable options, @Nullable String message, @Nullable String title, @Nullable Function toStringMapper) { - return prompter.promptValue( - elements:options, - toStringMapper:toStringMapper, - message:message, - includeNull:true); -// return new PrompterValueFromList<>( +// return prompter.promptValueFromList( +// message:message, // elements:options, // toStringMapper:toStringMapper, -// message:message, -// includeNull:true) -// .prompt(prompter); +// includeNull:true); + // TODO remove optional parameters + // TODO use extension method + return PrompterExt.promptValueFromList(prompter, + message, + options, + toStringMapper, + true); } @Override - public Optional choice(Iterable options, @Nullable String message, @Nullable String title, + public Optional choice(Iterable options, String message, @Nullable String title, @Nullable Function toStringMapper) { return selectFromList(options, message, title, toStringMapper); } @Override public boolean confirm(String message, String title) { - return prompter.promptBoolean(message:message + " (Y/N)"); -// return new PrompterBoolean(message:message + " (Y/N)").prompt(prompter); +// return prompter.promptBoolean(message + " (%s/%s)" +// .formatted(getText("Prompter.YesAbbreviation"), getText("Prompter.NoAbbreviation"))) +// .orElse(false); + // TODO remove optional parameters + return prompter.promptBoolean( + message.formatted(getText("Prompter.YesAbbreviation"), getText("Prompter.NoAbbreviation")), + null).orElse(false); } @Override - public Optional enter(String title, String message, @Nullable String errorMessage, - @Nullable Predicate validator) { - return prompter.promptString( - message:message, - errorMessage:errorMessage, - validator:validator); + public Optional enter(String title, String message, + @Nullable List> inputValidators) { +// return prompter.promptString(message, inputValidators:inputValidators); + // TODO use extension method + return PrompterExt.promptString(prompter, message, inputValidators:inputValidators); + } -// return new PrompterString( -// message:message, -// errorMessage:errorMessage, -// validator:validator) -// .prompt(prompter); + @Override + public OptionalInt enterNumber(String title, String message, + @Nullable List> objectValidators) { +// return prompter.promptInt(message, objectValidators:objectValidators); + // TODO use extension method + return PrompterExt.promptInt(prompter, message, objectValidators:objectValidators); } @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 de224ce6..9a3b4600 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 @@ -1,10 +1,11 @@ package org.lodder.subtools.sublibrary.userinteraction; import javax.swing.*; +import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Function; -import java.util.function.Predicate; import lombok.AllArgsConstructor; import manifold.ext.props.rt.api.override; @@ -13,6 +14,7 @@ import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.gui.InputPane; import org.lodder.subtools.sublibrary.gui.OptionsPane; +import org.lodder.subtools.sublibrary.util.Validator; @AllArgsConstructor public class UserInteractionHandlerGUI implements UserInteractionHandler { @@ -21,7 +23,7 @@ public class UserInteractionHandlerGUI implements UserInteractionHandler { @val JFrame frame; @Override - public Optional selectFromList(Iterable options, @Nullable String message, + public Optional selectFromList(Iterable options, String message, @Nullable String title, @Nullable Function toStringMapper) { if (!options.iterator().hasNext()) { return Optional.empty(); @@ -49,13 +51,25 @@ 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(); + public Optional enter(String title, String message, @Nullable List> inputValidators) { + return new InputPane<>( + title:title, + message:message, + inputValidators:inputValidators, + toObjectMapper:Function.identity()) + .prompt(); + } + + @Override + public OptionalInt enterNumber(String title, String message, + @Nullable List> objectValidators) { + + return new InputPane<>( + title:title, + message:message, + toObjectMapper:Integer::parseInt, + objectValidators:objectValidators) + .prompt().mapToInt(v -> v); } public void message(String message, String title) { diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Validator.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Validator.java new file mode 100644 index 00000000..b80304c5 --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Validator.java @@ -0,0 +1,15 @@ +package org.lodder.subtools.sublibrary.util; + +import static org.lodder.subtools.multisubdownloader.Messages.*; + +import java.util.function.Predicate; + +public record Validator(Predicate predicate, String errorMessage=getText("Prompter.ValueIsNotValid")) { + public boolean isValid(T value) { + return predicate.test(value); + } + + public boolean isInvalid(T value) { + return !isValid(value); + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java deleted file mode 100644 index 53315e4b..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/ColumnDisplayer.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.Function; - -@Deprecated -public record ColumnDisplayer(String columnName, Function toStringMapper) {} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java deleted file mode 100644 index 4c6ce254..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBoolean.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import org.codehaus.plexus.components.interactivity.Prompter; -import org.jspecify.annotations.Nullable; - -@Deprecated -public class PrompterBoolean { - - public final Function toObjectMapper; - public final Predicate validator; - private final Boolean defaultValue; - private final Supplier defaultValueSupplier; - private final String message; - private final String errorMessage; - - public PrompterBoolean( - Predicate toObjectMapper="y"::equalsIgnoreCase, - @Nullable Predicate validator=v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v), - @Nullable Boolean defaultValue=null, - @Nullable BooleanSupplier defaultValueSupplier=null, - String message, - @Nullable String errorMessage=null) { - this.toObjectMapper = toObjectMapper::test; - this.validator = validator; - this.defaultValue = defaultValue; - this.defaultValueSupplier = defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean; - this.message = message; - this.errorMessage = errorMessage; - } - - public boolean prompt(Prompter prompter) { - return PrompterBuilderCommon.prompt(prompter, toObjectMapper, validator, null, defaultValue, - defaultValueSupplier, message, errorMessage).get(); - } - -// -// private PrompterBoolean() { -// // util class -// } -// -// protected static boolean getValue(Prompter prompter) { -// return new ValueBuilder().prompt(prompter); -// } -// -// protected static ValueBuilderOtherMapperIntf getValue() { -// return new ValueBuilder(); -// } -// -// public static ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue) { -// return new ValueBuilder().defaultValue(defaultValue); -// } -// -// public static ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier) { -// return new ValueBuilder().defaultValueSupplier(defaultValueSupplier); -// } -// -// public static ValueBuilderOtherMapperIntf message(String message, Object... replacements) { -// return new ValueBuilder().message(message, replacements); -// } -// -// public interface ValueBuilderOtherMapperIntf { -// -// ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue); -// -// ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier); -// -// ValueBuilderOtherMapperIntf message(String message, Object... replacements); -// -// ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); -// -// boolean prompt(Prompter prompter); -// } -// -// // ------- \\ -// // Builder \\ -// // ------- \\ -// -// @Setter -// @Accessors(fluent = true, chain = true) -// public static class ValueBuilder implements ValueBuilderOtherMapperIntf { -// public static final Predicate TO_OBJECT_MAPPER = "y"::equalsIgnoreCase; -// public static final Predicate VALIDATOR = v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v); -// private Boolean defaultValue; -// private BooleanSupplier defaultValueSupplier; -// private String message; -// private String errorMessage; -// -// private ValueBuilder() { -// // hide constructor -// } -// -// @Override -// public ValueBuilder defaultValue(boolean defaultValue) { -// this.defaultValue = defaultValue; -// return this; -// } -// -// @Override -// public ValueBuilder message(String message, Object... replacements) { -// this.message = message.formatted(replacements); -// return this; -// } -// -// @Override -// public ValueBuilder errorMessage(String errorMessage, Object... replacements) { -// this.errorMessage = errorMessage.formatted(replacements); -// return this; -// } -// -// @Override -// public boolean prompt(Prompter prompter) { -// return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER::test, VALIDATOR, null, defaultValue, -// defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, errorMessage).get(); -// } -// } -} 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 deleted file mode 100644 index d4de6f82..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderCommon.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import org.apache.commons.lang3.StringUtils; -import org.codehaus.plexus.components.interactivity.Prompter; -import org.codehaus.plexus.components.interactivity.PrompterException; -import org.jspecify.annotations.Nullable; -import org.lodder.subtools.multisubdownloader.Messages; - -@Deprecated -class PrompterBuilderCommon { - - private PrompterBuilderCommon() { - // hide constructor - } - - protected static Optional prompt(Prompter prompter, Function toObjectMapper, - @Nullable Predicate validator=null, @Nullable Predicate objValidator=null, - @Nullable T defaultValue=null, @Nullable Supplier defaultValueSupplier=null, String message, - @Nullable String errorMessage=null) { - try { - String value = prompter.prompt(message + System.lineSeparator()); - if (StringUtils.isEmpty(value)) { - if (defaultValue != null) { - return Optional.of(defaultValue); - } else if (defaultValueSupplier != null) { - return Optional.ofNullable(defaultValueSupplier.get()); - } else { - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, - message, errorMessage); - } - } else { - if (validator != null && !validator.test(value)) { - 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.getText("Prompter.ValueIsNotValid")); - return prompt(prompter, toObjectMapper, validator, objValidator, defaultValue, defaultValueSupplier, - message, errorMessage); - } - return Optional.ofNullable(object); - } - } catch (PrompterException e) { - throw new IllegalStateException(e); - } - } - -} 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 deleted file mode 100644 index d7890c30..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterBuilderValue.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import lombok.Setter; -import lombok.experimental.Accessors; -import org.codehaus.plexus.components.interactivity.Prompter; -import org.jspecify.annotations.Nullable; - -@Deprecated -public class PrompterBuilderValue { - - private PrompterBuilderValue() { - // util class - } - - public static ValueBuilderToObjectMapperIntf getValue() { - return new ValueBuilder<>(); - } - - public static ValueBuilderValidatorMapperIntf toObjectMapper(Function toObjectMapper) { - return new ValueBuilder<>().toObjectMapper(toObjectMapper); - } - - public interface ValueBuilderToObjectMapperIntf { - ValueBuilderValidatorMapperIntf toObjectMapper(Function toObjectMapper); - } - - public interface ValueBuilderValidatorMapperIntf extends ValueBuilderOtherMapperIntf { - ValueBuilderOtherMapperIntf validator(Predicate validator); - } - - public interface ValueBuilderOtherMapperIntf extends ValueBuilderOther2MapperIntf { - ValueBuilderOtherMapperIntf objectValidator(Predicate validator); - - ValueBuilderOtherMapperIntf defaultValue(T defaultValue); - - ValueBuilderOtherMapperIntf defaultValueSupplier(Supplier defaultValueSupplier); - - @Override - ValueBuilderOtherMapperIntf message(String message, Object... replacements); - - @Override - Optional prompt(Prompter prompter); - } - - public interface ValueBuilderOther2MapperIntf { - - ValueBuilderOtherMapperIntf message(String message, Object... replacements); - - ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); - - Optional prompt(Prompter prompter); - } - - // ------- \\ - // Builder \\ - // ------- \\ - - @Setter - @Accessors(fluent = true, chain = true) - public static class ValueBuilder implements ValueBuilderToObjectMapperIntf, ValueBuilderValidatorMapperIntf, - ValueBuilderOtherMapperIntf, ValueBuilderOther2MapperIntf { - private Function toObjectMapper; - private Predicate validator; - private Predicate objectValidator; - private T defaultValue; - private Supplier defaultValueSupplier; - private String message; - private String errorMessage; - - private ValueBuilder() { - // hide constructor - } - - @SuppressWarnings("unchecked") - @Override - public ValueBuilderValidatorMapperIntf toObjectMapper(Function toObjectMapper) { - this.toObjectMapper = (Function) toObjectMapper; - return (ValueBuilderValidatorMapperIntf) this; - } - - @Override - public ValueBuilder message(String message, Object... replacements) { - this.message = message.formatted(replacements); - return this; - } - - @Override - public ValueBuilder errorMessage(@Nullable String errorMessage, Object... replacements) { - this.errorMessage = errorMessage == null ? null : errorMessage.formatted(replacements); - return this; - } - - @Override - public Optional prompt(Prompter prompter) { - return PrompterBuilderCommon.prompt(prompter, toObjectMapper, validator, objectValidator, defaultValue, - defaultValueSupplier, message, errorMessage); - } - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java deleted file mode 100644 index 8a93c691..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterInt.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.function.Function; -import java.util.function.IntPredicate; -import java.util.function.IntSupplier; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import org.codehaus.plexus.components.interactivity.Prompter; -import org.jspecify.annotations.Nullable; - -@Deprecated -public class PrompterInt { - - public static final Function TO_OBJECT_MAPPER = Integer::parseInt; - public static final Predicate VALIDATOR = v -> { - try { - Integer.parseInt(v); - return true; - } catch (NumberFormatException e) { - return false; - } - }; - - public final Predicate objValidator; - private final Integer defaultValue; - private final Supplier defaultValueSupplier; - private final String message; - private final String errorMessage; - - public PrompterInt( - @Nullable IntPredicate validator=_ -> true, - @Nullable Integer defaultValue=null, - @Nullable IntSupplier defaultValueSupplier=null, - String message, - @Nullable String errorMessage=null) { - this.objValidator = validator == null ? null : validator::test; - this.defaultValue = defaultValue; - this.defaultValueSupplier = defaultValueSupplier == null ? null : defaultValueSupplier::getAsInt; - this.message = message; - this.errorMessage = errorMessage; - } - - public int prompt(Prompter prompter) { - return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER, VALIDATOR, objValidator, - defaultValue, defaultValueSupplier, message, errorMessage).get(); - } - -// -// private PrompterBoolean() { -// // util class -// } -// -// protected static boolean getValue(Prompter prompter) { -// return new ValueBuilder().prompt(prompter); -// } -// -// protected static ValueBuilderOtherMapperIntf getValue() { -// return new ValueBuilder(); -// } -// -// public static ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue) { -// return new ValueBuilder().defaultValue(defaultValue); -// } -// -// public static ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier) { -// return new ValueBuilder().defaultValueSupplier(defaultValueSupplier); -// } -// -// public static ValueBuilderOtherMapperIntf message(String message, Object... replacements) { -// return new ValueBuilder().message(message, replacements); -// } -// -// public interface ValueBuilderOtherMapperIntf { -// -// ValueBuilderOtherMapperIntf defaultValue(boolean defaultValue); -// -// ValueBuilderOtherMapperIntf defaultValueSupplier(BooleanSupplier defaultValueSupplier); -// -// ValueBuilderOtherMapperIntf message(String message, Object... replacements); -// -// ValueBuilderOtherMapperIntf errorMessage(String errorMessage, Object... replacements); -// -// boolean prompt(Prompter prompter); -// } -// -// // ------- \\ -// // Builder \\ -// // ------- \\ -// -// @Setter -// @Accessors(fluent = true, chain = true) -// public static class ValueBuilder implements ValueBuilderOtherMapperIntf { -// public static final Predicate TO_OBJECT_MAPPER = "y"::equalsIgnoreCase; -// public static final Predicate VALIDATOR = v -> "y".equalsIgnoreCase(v) || "n".equalsIgnoreCase(v); -// private Boolean defaultValue; -// private BooleanSupplier defaultValueSupplier; -// private String message; -// private String errorMessage; -// -// private ValueBuilder() { -// // hide constructor -// } -// -// @Override -// public ValueBuilder defaultValue(boolean defaultValue) { -// this.defaultValue = defaultValue; -// return this; -// } -// -// @Override -// public ValueBuilder message(String message, Object... replacements) { -// this.message = message.formatted(replacements); -// return this; -// } -// -// @Override -// public ValueBuilder errorMessage(String errorMessage, Object... replacements) { -// this.errorMessage = errorMessage.formatted(replacements); -// return this; -// } -// -// @Override -// public boolean prompt(Prompter prompter) { -// return PrompterBuilderCommon.prompt(prompter, TO_OBJECT_MAPPER::test, VALIDATOR, null, defaultValue, -// defaultValueSupplier == null ? null : defaultValueSupplier::getAsBoolean, message, errorMessage).get(); -// } -// } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java deleted file mode 100644 index cf80633c..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterString.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import org.apache.commons.lang3.StringUtils; -import org.codehaus.plexus.components.interactivity.Prompter; -import org.jspecify.annotations.Nullable; - -@Deprecated -public class PrompterString { - - public final Predicate validator; - private final String defaultValue; - private final Supplier defaultValueSupplier; - private final String message; - private final String errorMessage; - - public PrompterString( - @Nullable Predicate validator=StringUtils::isNotBlank, - @Nullable String defaultValue=null, - @Nullable Supplier defaultValueSupplier=null, - String message, - @Nullable String errorMessage=null) { - this.validator = validator; - this.defaultValue = defaultValue; - this.defaultValueSupplier = defaultValueSupplier; - this.message = message; - this.errorMessage = errorMessage; - } - - public Optional prompt(Prompter prompter) { - return PrompterBuilderCommon.prompt(prompter, Function.identity(), validator, null, - defaultValue, defaultValueSupplier, message, errorMessage); - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java deleted file mode 100644 index 642b0679..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValueFromList.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import static com.pivovarit.gatherers.MoreGatherers.*; -import static java.lang.System.*; - -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.codehaus.plexus.components.interactivity.Prompter; -import org.codehaus.plexus.components.interactivity.PrompterException; -import org.jspecify.annotations.Nullable; - -@Deprecated -public class PrompterValueFromList { - - private final Iterable elements; - private final Function toStringMapper; - private final String message; - private final boolean includeNull; - private final T emptyValue; - @Nullable private final TableDisplayer tableDisplayer; - @Nullable private final Comparator comparator; - - public PrompterValueFromList( - Iterable elements, - Function toStringMapper, - String message, - boolean includeNull, - T emptyValue=null, - @Nullable TableDisplayer tableDisplayer=null, - @Nullable Comparator sort=null - ) { - this.elements = elements; - this.toStringMapper = toStringMapper; - this.message = message; - this.includeNull = includeNull; - this.emptyValue = emptyValue; - this.tableDisplayer = tableDisplayer; - this.comparator = sort; - } - - public Optional prompt(Prompter prompter) { - List sortedElements = - comparator == null ? elements.stream().toList() : elements.stream().sorted(comparator).toList(); - try { - String value; - if (tableDisplayer != null) { - tableDisplayer.display(sortedElements); - value = prompter.prompt(message); - } else { - String choicesMessage = sortedElements.stream() - .gather(zipWithIndex()) - .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) - .collect(Collectors.joining(lineSeparator())) + lineSeparator(); - value = prompter.prompt( - StringUtils.isBlank(message) ? choicesMessage : message + lineSeparator() + choicesMessage); - } - if (StringUtils.isBlank(value) && includeNull) { - return Optional.ofNullable(emptyValue); - } - int number = Integer.parseInt(value); - if (number < 1 || number > sortedElements.size()) { - prompter.show("The entered value isn't in the range [1, %s], try again.", sortedElements.size()); - return prompt(prompter); - } - return Optional.ofNullable(sortedElements.get(number - 1)); - } catch (PrompterException e) { - throw new IllegalStateException(e); - } catch (NumberFormatException e) { - prompter.show("Enter a valid number, try again."); - return prompt(prompter); - } - } -} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java deleted file mode 100644 index 307e1474..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/PrompterValuesFromList.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import static com.pivovarit.gatherers.MoreGatherers.*; -import static java.lang.System.*; - -import java.util.Comparator; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.codehaus.plexus.components.interactivity.Prompter; -import org.codehaus.plexus.components.interactivity.PrompterException; -import org.jspecify.annotations.Nullable; - -@Deprecated -public class PrompterValuesFromList { - - private final Iterable elements; - private final Function toStringMapper; - private final String message; - private final boolean includeNull; - @Nullable private final TableDisplayer tableDisplayer; - @Nullable private final Comparator comparator; - - // TODO: tableDisplayer or ToStingMapper - public PrompterValuesFromList( - Iterable elements, - Function toStringMapper = String::valueOf, - String message, - boolean includeNull, - @Nullable TableDisplayer tableDisplayer=null, - @Nullable Comparator sort=null - ) { - this.elements = elements; - this.toStringMapper = toStringMapper; - this.message = message; - this.includeNull = includeNull; - this.tableDisplayer = tableDisplayer; - this.comparator = sort; - } - - public List prompt(Prompter prompter) { - List sortedElements = - comparator == null ? elements.stream().toList() : elements.stream().sorted(comparator).toList(); - try { - String value; - if (tableDisplayer != null) { - tableDisplayer.display(sortedElements); - value = prompter.prompt(message); - } else { - String choicesMessage = sortedElements.stream() - .gather(zipWithIndex()) - .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) - .collect(Collectors.joining(lineSeparator())) + lineSeparator(); - value = prompter.prompt( - StringUtils.isBlank(message) ? choicesMessage : message + lineSeparator() + choicesMessage); - } - if (StringUtils.isBlank(value) && includeNull) { - return List.of(); - } - if (StringUtils.isBlank(value)) { - prompter.show("Enter a valid value, try again."); - return prompt(prompter); - } - int[] choices = value.split(",").stream().mapToInt(Integer::parseInt).map(i -> i - 1).toArray(); - if (choices.stream().distinct().count() != choices.length) { - prompter.show("Choose all distinct options, try again."); - return prompt(prompter); - } - if (choices.stream().anyMatch(number -> number < 0 || number > sortedElements.size() - 1)) { - prompter.show("The entered number(s) aren't in the range [1, %s], try again.", sortedElements.size()); - return prompt(prompter); - } - return choices.stream().map(sortedElements::get).collect(Collectors.toList()); - } catch (PrompterException e) { - throw new IllegalStateException(e); - } catch (NumberFormatException e) { - prompter.show("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/TableDisplayer.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java deleted file mode 100644 index 1fe2695a..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/prompter/TableDisplayer.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.lodder.subtools.sublibrary.util.prompter; - -import java.util.List; - -import dnl.utils.text.table.TextTable; -import lombok.RequiredArgsConstructor; - -@Deprecated -@RequiredArgsConstructor -public class TableDisplayer { - - private final List> columnDisplayers; - - @SafeVarargs - public final void display(T... 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)) - .toArray()) - .toArray(Object[][]::new); - - TextTable tt = new TextTable(columnNames, dataTable); - // this adds the numbering on the left - tt.setAddRowNumbering(true); - tt.printTable(); - } - - public void display(Iterable 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)) - .toArray()) - .toArray(Object[][]::new); - - TextTable tt = new TextTable(columnNames, dataTable); - // this adds the numbering on the left - tt.setAddRowNumbering(true); - tt.printTable(); - } -} diff --git a/SubLibrary/src/main/resources/resourcebundle/Message.properties b/SubLibrary/src/main/resources/resourcebundle/Message.properties index 5ad0d17b..2d870b75 100644 --- a/SubLibrary/src/main/resources/resourcebundle/Message.properties +++ b/SubLibrary/src/main/resources/resourcebundle/Message.properties @@ -251,6 +251,13 @@ Prompter.SpecialValue.SkipForever=-- Skip searching forever -- Prompter.SpecialValue.SkipOneWeek=-- Skip searching for one week -- Prompter.SpecialValue.SkipTwoWeek=-- Skip searching for two weeks -- Prompter.ValueIsNotValid=The entered value isn't valid, try again +Prompter.ValueNonBlank=The entered value shouldn't be blank, try again +Prompter.ValueNotInRange=The entered value isn't in the range [1, %s], try again +Prompter.DistinctValues=Choose all distinct options, try again. +Prompter.YesAbbreviation=Y +Prompter.Yes=Yes +Prompter.NoAbbreviation=N +Prompter.No=No RenameDialog.RecursiveSearch=Recursive search RenameDialog.Rename=Rename RenameDialog.SelectFolderForRenameReplace=Select folder for Rename/Move diff --git a/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties b/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties index c62b03d0..f124d4b4 100644 --- a/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties +++ b/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties @@ -251,6 +251,13 @@ 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 +Prompter.ValueNonBlank=De ingevoerde waarde kan niet leeg zijn, probeer opnieuw +Prompter.ValueNotInRange=De ingevoerde waarde ligt niet binnen het bereik [1, %s], probeer opnieuw +Prompter.DistinctValues=Voer niet dezelfde opties in, probeer opnieuw. +Prompter.YesAbbreviation=J +Prompter.Yes=Ja +Prompter.NoAbbreviation=N +Prompter.No=Nee RenameDialog.RecursiveSearch=Mappen in map doorzoeken RenameDialog.Rename=Hernoemen RenameDialog.SelectFolderForRenameReplace=Selecteer map voor Hernoemen/Verplaatsen diff --git a/pom.xml b/pom.xml index 45b8f335..f0cb61c9 100644 --- a/pom.xml +++ b/pom.xml @@ -316,6 +316,11 @@ joor 0.9.15 + + com.pivovarit + more-gatherers + 0.0.2 + org.mockito From 4c9bf80a08c258a7f69bb65716b18c1463c8442b Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 21:45:03 +0200 Subject: [PATCH 25/58] temp commit --- .../UserInteractionHandlerCLI.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 e0bc02cd..61ee0fa6 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java @@ -32,12 +32,12 @@ public List selectSubtitles(Release release) { System.out.printf("\n%s : %s%n", getText("SelectDialog.SelectCorrectSubtitleThisRelease"), release.fileName); return prompter.promptValuesFromList( - message:getText("SelectDialog.EnterListSelectedSubtitles"), - elements:release.getMatchingSubs(), - toStringMapper:Subtitle::getFileName, - includeNull:true, - tableDisplayer:createTableDisplayer(), - sorter:Comparator.comparing(Subtitle::getScore)); + getText("SelectDialog.EnterListSelectedSubtitles"), + release.getMatchingSubs(), + Subtitle::getFileName, + true, + createTableDisplayer(), + Comparator.comparing(Subtitle::getScore)); } private PrompterExt.ColumnDisplayer createSubtitleDisplayer(SubtitleTableColumnName column, From a0eee1f2d25291677321c8a0b015d06406b2069a Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 22:52:20 +0200 Subject: [PATCH 26/58] temp commit --- .../multisubdownloader/util/ExportImport.java | 2 +- .../interactivity/Prompter/PrompterExt.java | 84 ------------------- .../subtools/sublibrary/gui/OptionsPane.java | 69 --------------- .../UserInteractionHandler.java | 3 - .../UserInteractionHandlerCLI.java | 6 -- .../UserInteractionHandlerGUI.java | 10 --- 6 files changed, 1 insertion(+), 173 deletions(-) delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/OptionsPane.java 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 0751ca75..242c69ac 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 @@ -163,7 +163,7 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand } private static Optional getImportStyle(UserInteractionHandler userInteractionHandler) { - return userInteractionHandler.choice(Arrays.asList(ImportStyle.values()), + return userInteractionHandler.selectFromList(Arrays.asList(ImportStyle.values()), getText("ImportExport.OverwriteOrAdd"), getText("ImportExport.OverwriteOrAddTitle"), option -> switch (option) { diff --git a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java index 05067daf..53815a45 100644 --- a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java +++ b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java @@ -75,90 +75,6 @@ public static Optional promptBoolean(@This Prompter prompter, String me return prompt(prompter, message, inputValidators, Boolean::parseBoolean, List.of()); } -// public static Optional promptValueFromList(@This Prompter prompter, -// String message, -// Iterable elements, -// Function toStringMapper=String::valueOf, -// boolean includeNull, -// @Nullable TableDisplayer tableDisplayer=null, -// @Nullable Comparator sorter=null) { -// -// List sortedElements = -// sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); -// String choicesMessage; -// if (tableDisplayer != null) { -// choicesMessage = tableDisplayer.getAsString(sortedElements); -// } else { -// choicesMessage = message + lineSeparator() + -// sortedElements.stream() -// .gather(zipWithIndex()) -// .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) -// .collect(Collectors.joining(lineSeparator())) + -// lineSeparator(); -// } -// -// int numberOfElements = sortedElements.size(); -// List> inputValidators = new ArrayList<>(); -// if (!includeNull) { -// inputValidators.add(NON_BLANK_VALIDATOR); -// } -// inputValidators.add(new Validator<>(v -> v == null || v.parseAsNumber(Integer::parseUnsignedInt).isPresent())); -// -// Function toObjectMapper = v -> v == null ? null : Integer.parseUnsignedInt(v); -// -// List> objectValidators = -// List.of(new Validator<>(number -> number == null || (number > 0 && number <= numberOfElements), -// getText("Prompter.ValueNotInRange", numberOfElements))); -// -// return prompter.promptInt(choicesMessage, inputValidators, toObjectMapper, objectValidators) -// .mapToObj(idx -> sortedElements.get(idx - 1)); -// } -// -// -// public static List promptValuesFromList(@This Prompter prompter, -// String message, -// Iterable elements, -// Function toStringMapper=String::valueOf, -// boolean includeNull, -// @Nullable TableDisplayer tableDisplayer=null, -// @Nullable Comparator sorter=null) { -// -// List sortedElements = -// sorter == null ? elements.stream().toList() : elements.stream().sorted(sorter).toList(); -// String choicesMessage; -// if (tableDisplayer != null) { -// choicesMessage = tableDisplayer.getAsString(sortedElements); -// } else { -// choicesMessage = message + lineSeparator() + -// sortedElements.stream() -// .gather(zipWithIndex()) -// .map(entry -> " - " + (entry.getValue() + 1) + ": " + toStringMapper.apply(entry.getKey())) -// .collect(Collectors.joining(lineSeparator())) + -// lineSeparator(); -// } -// -// int numberOfElements = sortedElements.size(); -// List> inputValidators = new ArrayList<>(); -// if (!includeNull) { -// inputValidators.add(NON_BLANK_VALIDATOR); -// } -// inputValidators.add( -// new Validator<>(v -> v == null || -// v.split(",").stream().allMatch(n -> n.parseAsNumber(Integer::parseUnsignedInt).isPresent()))); -// -// Function toObjectsMapper = -// v -> v.split(",").stream().mapToInt(Integer::parseUnsignedInt).toArray(); -// -// List> objectValidators = List.of( -// new Validator<>(numbers -> numbers.stream().distinct().count() == numbers.stream().count(), -// getText("Prompter.DistinctValues")), -// new Validator<>(numbers -> numbers.stream().allMatch(number -> number > 0 && number <= numberOfElements), -// getText("Prompter.ValueNotInRange", numberOfElements))); -// -// return prompter.promptValues(choicesMessage, inputValidators, toObjectsMapper, objectValidators).stream() -// .map(idx -> sortedElements.get(idx - 1)).toList(); -// } - public static Optional promptValueFromList(@This Prompter prompter, String message, Iterable elements, 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 deleted file mode 100644 index 74cb2f58..00000000 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/gui/OptionsPane.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.lodder.subtools.sublibrary.gui; - -import javax.swing.*; -import java.awt.*; -import java.util.Optional; -import java.util.function.Function; - -import lombok.AllArgsConstructor; -import manifold.ext.props.rt.api.val; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; - -public class OptionsPane { - - public static final Object LOCK = new Object(); - - private final T[] options; - private final @Nullable String title; - private final String message; - private final Option messageType; - private final @Nullable Function toStringMapper; - private final @Nullable Component parent; - - public OptionsPane(Iterable options, @Nullable Function toStringMapper=null, - @Nullable String title=null, - String message, Option messageType, @Nullable Component parent=null) { - this.options = (T[]) options.stream().toArray(); - this.toStringMapper = toStringMapper; - this.title = title; - this.message = message; - this.messageType = messageType; - this.parent = parent; - } - - - public Optional prompt() { - synchronized (LOCK) { - if (toStringMapper == null) { - return Optional.ofNullable( - (T) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, options, "0")); - } else { - ElementWrapper[] optionsWrapper = - options.stream().map(option -> new ElementWrapper<>(option, toStringMapper)) - .toArray(ElementWrapper[]::new); - return Optional.ofNullable( - (ElementWrapper) JOptionPane.showInputDialog(parent, message, title, messageType.value, null, - optionsWrapper, "0")) - .map(ElementWrapper::element); - } - } - } - - private record ElementWrapper(T element, Function toStringMapper) { - @Override - public @NonNull String toString() { - return toStringMapper.apply(element); - } - } - - @AllArgsConstructor - public enum Option { - DEFAULT(JOptionPane.DEFAULT_OPTION), - YES_NO(JOptionPane.YES_NO_OPTION), - YES_NO_CANCEL(JOptionPane.YES_NO_CANCEL_OPTION), - OK_CANCEL(JOptionPane.OK_CANCEL_OPTION); - - @val int value; - } -} 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 ce500474..bb91b7ee 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 @@ -19,9 +19,6 @@ public interface UserInteractionHandler { Optional selectFromList(Iterable options, String message, @Nullable String title=null, @Nullable Function toStringMapper=null); - Optional choice(Iterable options, String message, @Nullable String title=null, - @Nullable Function toStringMapper=null); - Optional enter(String title, String message, @Nullable List> inputValidators=null); OptionalInt enterNumber(String title, String message, @Nullable List> objectValidators=null); 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 ea7718dd..4182415d 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 @@ -46,12 +46,6 @@ public Optional selectFromList(Iterable options, @Nullable String mess true); } - @Override - public Optional choice(Iterable options, String message, @Nullable String title, - @Nullable Function toStringMapper) { - return selectFromList(options, message, title, toStringMapper); - } - @Override public boolean confirm(String message, String title) { // return prompter.promptBoolean(message + " (%s/%s)" 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 9a3b4600..4558c953 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 @@ -13,7 +13,6 @@ import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.gui.InputPane; -import org.lodder.subtools.sublibrary.gui.OptionsPane; import org.lodder.subtools.sublibrary.util.Validator; @AllArgsConstructor @@ -25,15 +24,6 @@ public class UserInteractionHandlerGUI implements UserInteractionHandler { @Override public Optional selectFromList(Iterable options, String message, @Nullable String title, @Nullable Function toStringMapper) { - if (!options.iterator().hasNext()) { - return Optional.empty(); - } - return new OptionsPane<>(options, toStringMapper, title, message, OptionsPane.Option.DEFAULT, frame).prompt(); - } - - @Override - public Optional choice(Iterable options, @Nullable String message, @Nullable String title, - @Nullable Function toStringMapper) { String[] optionsAsStrings = options.stream() .map(Objects.requireNonNullElseGet(toStringMapper, () -> String::valueOf)) .toArray(String[]::new); From ae1097b48bd750193220de6b993e2c984eb1efc7 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 22:56:49 +0200 Subject: [PATCH 27/58] temp commit --- .../userinteraction/UserInteractionHandlerCLI.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 4182415d..a567d4e1 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 @@ -37,7 +37,6 @@ public Optional selectFromList(Iterable options, @Nullable String mess // elements:options, // toStringMapper:toStringMapper, // includeNull:true); - // TODO remove optional parameters // TODO use extension method return PrompterExt.promptValueFromList(prompter, message, @@ -53,8 +52,9 @@ public boolean confirm(String message, String title) { // .orElse(false); // TODO remove optional parameters return prompter.promptBoolean( - message.formatted(getText("Prompter.YesAbbreviation"), getText("Prompter.NoAbbreviation")), - null).orElse(false); + message + " (%s/%s)".formatted(getText("Prompter.YesAbbreviation"), + getText("Prompter.NoAbbreviation")), null) + .orElse(false); } @Override From 3f62e5668f94fc8c579d2820d4b9045ca4d40bc8 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 19 Apr 2025 23:01:34 +0200 Subject: [PATCH 28/58] temp commit --- .../UserInteractionHandlerCLI.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) 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 a567d4e1..4465ed88 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 @@ -32,11 +32,6 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @Override public Optional selectFromList(Iterable options, @Nullable String message, @Nullable String title, @Nullable Function toStringMapper) { -// return prompter.promptValueFromList( -// message:message, -// elements:options, -// toStringMapper:toStringMapper, -// includeNull:true); // TODO use extension method return PrompterExt.promptValueFromList(prompter, message, @@ -47,20 +42,16 @@ public Optional selectFromList(Iterable options, @Nullable String mess @Override public boolean confirm(String message, String title) { -// return prompter.promptBoolean(message + " (%s/%s)" -// .formatted(getText("Prompter.YesAbbreviation"), getText("Prompter.NoAbbreviation"))) -// .orElse(false); - // TODO remove optional parameters - return prompter.promptBoolean( + // TODO Use extension method + return PrompterExt.promptBoolean(prompter, message + " (%s/%s)".formatted(getText("Prompter.YesAbbreviation"), - getText("Prompter.NoAbbreviation")), null) + getText("Prompter.NoAbbreviation"))) .orElse(false); } @Override public Optional enter(String title, String message, @Nullable List> inputValidators) { -// return prompter.promptString(message, inputValidators:inputValidators); // TODO use extension method return PrompterExt.promptString(prompter, message, inputValidators:inputValidators); } @@ -68,7 +59,6 @@ public Optional enter(String title, String message, @Override public OptionalInt enterNumber(String title, String message, @Nullable List> objectValidators) { -// return prompter.promptInt(message, objectValidators:objectValidators); // TODO use extension method return PrompterExt.promptInt(prompter, message, objectValidators:objectValidators); } From 7996786ce3ea50e2dfd90bacd2914df307f728d4 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 10:47:00 +0200 Subject: [PATCH 29/58] fix --- .../multisubdownloader/gui/dialog/RenameDialog.java | 2 +- .../multisubdownloader/gui/extra/TitlePanel.java | 12 ++++++++---- .../gui/panels/preference/GeneralPanel.java | 2 +- .../gui/panels/preference/OptionsPanel.java | 2 +- .../gui/panels/preference/SerieProvidersPanel.java | 4 ++-- .../gui/panels/preference/StructureFilePanel.java | 2 +- .../gui/panels/preference/StructureFolderPanel.java | 2 +- .../gui/panels/preference/SubtitleBackupPanel.java | 2 +- .../gui/panels/preference/VideoLibraryPanel.java | 2 +- 9 files changed, 17 insertions(+), 13 deletions(-) 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 f7bc8d19..25ff2c78 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 @@ -58,7 +58,7 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String title:getText("PreferenceDialog.Settings"), padding:new BoxModelProperties(0, 20, 0, 0), fillContents:true) - .addTo(contentPane, "span, grow, wrap") + .addTo(contentPane, "span, grow, wrap").panel .addComponent("shrink", new JLabel(getText("PreferenceDialog.Location"))) .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().columns(20)) .addComponent("shrink, wrap", new JButton(getText("App.Browse")).actionListener( 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 e9917a84..364bab48 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 @@ -4,18 +4,22 @@ import java.awt.*; import java.io.Serial; -import manifold.ext.props.rt.api.var; +import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; public class TitlePanel extends JPanel { @Serial private static final long serialVersionUID = 1L; - @var JPanel panel; + @val JPanel panel; - public TitlePanel(String title, boolean useGrid=false, boolean fillContents=true, + public TitlePanel(String title, + boolean useGrid=false, + boolean fillContents=true, BoxModelProperties margin=new BoxModelProperties(), - BoxModelProperties padding=new BoxModelProperties(), LayoutManager panelLayout=null, + BoxModelProperties padding=new BoxModelProperties(), + LayoutManager panelLayout=null, String panelColumnConstraints="") { + super(new MigLayout("fillx, nogrid, " + margin.getInsets())); super.add(new JLabel(title)); super.add(new JSeparator(), "growx, gapy 6, wrap"); 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 eb03e53d..8e685e41 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 @@ -52,7 +52,7 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { padding:new BoxModelProperties(0, 20, 0, 0), useGrid:true, fillContents:false) - .addTo(this, "span, grow, wrap"); + .addTo(this, "span, grow, wrap").panel; { // Language \\ 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 c56c0013..66a3245a 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 @@ -38,7 +38,7 @@ public OptionsPanel(SettingsControl settingsCtrl) { title:getText("PreferenceDialog.DownloadOptions"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap") + .addTo(this, "span, grow, wrap").panel .addComponent(this.chkAlwaysConfirm = new JCheckBox(getText("PreferenceDialog.CheckBeforeDownloading")), "wrap") .addComponent("wrap, grow", PanelCheckBox.checkbox(this.chkMinScoreSelection = 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 57237160..676e58c7 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 @@ -44,8 +44,8 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { super(new MigLayout("insets 0, fill, nogrid")); this.settingsCtrl = settingsCtrl; - JPanel titlePanel = new TitlePanel(title:getText("PreferenceDialog.SelectPreferredSources")) - .addTo(this, "span, grow"); + JPanel titlePanel = new TitlePanel(getText("PreferenceDialog.SelectPreferredSources")) + .addTo(this, "span, grow").panel; { // ADDIC7ED 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 a30d80fa..8697e925 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 @@ -54,7 +54,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, title:getText("PreferenceDialog.RenameFiles"), margin:new BoxModelProperties(0, 20, 0, 0), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow"); + .addTo(this, "span, grow").panel; new JLabel(getText("PreferenceDialog.Structure")).addTo(titlePanel, "shrink"); this.txtFileStructure = 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 9efcfe63..bd52fea0 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 @@ -44,7 +44,7 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType padding:new BoxModelProperties(0, 20, 0, 0), useGrid:true, panelColumnConstraints:"[shrink][grow][shrink]") - .addTo(this, "span, grow"); + .addTo(this, "span, grow").panel; new JLabel(getText("PreferenceDialog.Location")).addTo(titlePanel, "shrink"); this.txtLibraryFolder = 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 9d8aadb5..07445db4 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 @@ -31,7 +31,7 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { title:Messages.getText("PreferenceDialog.SubtitlesBackup"), margin:new BoxModelProperties(0), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, growx"); + .addTo(this, "span, growx").panel; { this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); 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 24818453..a930923a 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 @@ -44,7 +44,7 @@ public abstract sealed class VideoLibraryPanel extends JPanel implements Prefere title:getText("PreferenceDialog.PerformActions"), margin:new BoxModelProperties(0), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, growx"); + .addTo(this, "span, growx").panel; { this.chkUseTVDBNaming = new JCheckBox(getText("PreferenceDialog.UseTvdbName")).visible( From 1949187c642d5a8c0e8f437e0d15ada57858b88b Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 11:03:56 +0200 Subject: [PATCH 30/58] use optional parameters (8) --- .../gui/extra/PanelCheckBox.java | 83 ++----------------- .../gui/panels/preference/GeneralPanel.java | 12 +-- .../gui/panels/preference/OptionsPanel.java | 63 +++++++------- .../preference/SerieProvidersPanel.java | 83 ++++++++++--------- .../panels/preference/StructureFilePanel.java | 14 ++-- .../preference/StructureFolderPanel.java | 8 +- .../preference/SubtitleBackupPanel.java | 8 +- 7 files changed, 108 insertions(+), 163 deletions(-) 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 4cc841a3..3f210cbd 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 @@ -6,9 +6,6 @@ import java.awt.event.ContainerListener; import java.io.Serial; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; @@ -19,8 +16,12 @@ public class PanelCheckBox extends JPanel { @val JPanel panel; - private PanelCheckBox(JCheckBox checkbox, boolean panelOnNewLine, LayoutManager panelLayout, - boolean addVerticalSeparator, int leftGap) { + public PanelCheckBox(JCheckBox checkbox, + boolean panelOnNewLine, + LayoutManager panelLayout=new MigLayout("insets 0, novisualpadding, fillx"), + boolean addVerticalSeparator=false, + int leftGap=0) { + super(new MigLayout("insets 0, novisualpadding, fillx")); this.checkbox = checkbox; this.panel = new JPanel(panelLayout) { @@ -68,78 +69,6 @@ public void componentAdded(ContainerEvent e) { } } - public static BuilderPanelNewLineIntf checkbox(JCheckBox checkbox) { - return new Builder(checkbox); - } - - public interface BuilderPanelNewLineIntf { - BuilderSeparatorIntf panelOnNewLine(); - - BuilderOtherIntf panelOnSameLine(); - } - - public interface BuilderSeparatorIntf extends BuilderOtherIntf { - BuilderOtherIntf addVerticalSeparator(); - } - - public interface BuilderOtherIntf { - BuilderOtherIntf leftGap(int leftGap); - - BuilderOtherIntf panelLayout(LayoutManager panelLayout); - - JPanel addTo(JComponent component); - - JPanel addTo(JComponent component, Object constraints); - - PanelCheckBox build(); - } - - @RequiredArgsConstructor - @Setter - @Accessors(chain = true, fluent = true) - public static class Builder implements - BuilderPanelNewLineIntf, - BuilderSeparatorIntf, - BuilderOtherIntf { - private final JCheckBox checkbox; - private boolean panelOnNewLine; - private LayoutManager panelLayout = new MigLayout("insets 0, novisualpadding, fillx"); - private boolean addVerticalSeparator; - private int leftGap = 20; - - @Override - public Builder panelOnNewLine() { - return panelOnNewLine(true); - } - - @Override - public Builder panelOnSameLine() { - return panelOnNewLine(false); - } - - @Override - public Builder addVerticalSeparator() { - return addVerticalSeparator(true); - } - - @Override - public JPanel addTo(JComponent component) { - return addTo(component, ""); - } - - @Override - public JPanel addTo(JComponent component, Object constraints) { - PanelCheckBox panelCheckBox = build(); - component.add(panelCheckBox, constraints); - return panelCheckBox.panel; - } - - @Override - public PanelCheckBox build() { - return new PanelCheckBox(checkbox, panelOnNewLine, panelLayout, addVerticalSeparator, leftGap); - } - } - @Override protected void addImpl(Component comp, Object constraints, int index) { panel.add(comp, constraints, index); 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 8e685e41..9a37852b 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 @@ -147,12 +147,12 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { 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) + new PanelCheckBox( + checkbox:this.chkUseProxy = new JCheckBox(getText("PreferenceDialog.UseProxyServer")), + panelOnNewLine:false, + panelLayout:new MigLayout("insets 0, fill") + ) + .addTo(proxyPanel).panel .addComponent(new JLabel(getText("PreferenceDialog.Hostname"))) .addComponent("wrap", this.txtProxyHost = MyTextFieldString.builder().requireValue().build().columns(30)) 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 66a3245a..50197a33 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 @@ -39,56 +39,59 @@ public OptionsPanel(SettingsControl settingsCtrl) { margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) .addTo(this, "span, grow, wrap").panel - .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))); + .addComponent(this.chkAlwaysConfirm = + new JCheckBox(getText("PreferenceDialog.CheckBeforeDownloading")), "wrap") + .addComponent("wrap, grow", + new PanelCheckBox( + checkbox:this.chkMinScoreSelection = + new JCheckBox(getText("PreferenceDialog.MinAutomaticScoreSelection")), + panelOnNewLine:false + ) + .addComponent(this.sldMinScoreSelection = new JSlider().minimum(0).maximum(100), "wrap")) + .addComponent("wrap, grow", + new PanelCheckBox( + checkbox:this.chkDefaultSelection = + new JCheckBox(getText("PreferenceDialog.DefaultSelection"), null, true), + panelOnNewLine:true + ) + .addComponent(this.pnlDefaultSelection = new DefaultSelectionPanel(settingsCtrl))); new TitlePanel( title:getText("PreferenceDialog.SearchFilter"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .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"))); + .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"))); new TitlePanel( title:getText("PreferenceDialog.TableOptions"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap") - .addComponent(this.chkOnlyFound = new JCheckBox(getText("PreferenceDialog.ShowOnlyFound"))); + .addTo(this, "span, grow, wrap") + .addComponent(this.chkOnlyFound = new JCheckBox(getText("PreferenceDialog.ShowOnlyFound"))); new TitlePanel( title:getText("PreferenceDialog.ErrorHandlingOption"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap") - .addComponent(this.chkStopOnSearchError = new JCheckBox(getText("PreferenceDialog.StopAfterError"))); + .addTo(this, "span, grow, wrap") + .addComponent(this.chkStopOnSearchError = new JCheckBox(getText("PreferenceDialog.StopAfterError"))); new TitlePanel( title:getText("PreferenceDialog.SerieDatabaseSource"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow") - .addComponent(this.cbxEpisodeProcessSource = new JComboBox<>(SettingsProcessEpisodeSource.values()), - "wrap") - .addComponent(this.chkConfirmProviderMapping = - new JCheckBox(getText("PreferenceDialog.ConfirmProviderMapping"))); + .addTo(this, "span, grow") + .addComponent(this.cbxEpisodeProcessSource = new JComboBox<>(SettingsProcessEpisodeSource.values()), + "wrap") + .addComponent(this.chkConfirmProviderMapping = + new JCheckBox(getText("PreferenceDialog.ConfirmProviderMapping"))); loadPreferenceSettings(); } 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 676e58c7..1743b2d9 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 @@ -53,16 +53,20 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { this.chkUserAddic7edLogin = new JCheckBox(getText("PreferenceDialog.UseAddic7edLogin")); this.chkSourceAddic7edProxy = new JCheckBox(getText("PreferenceDialog.Proxy")); - 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(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))); + new PanelCheckBox(checkbox:chkSourceAddic7ed, panelOnNewLine:true) + .addTo(titlePanel, "wrap").panel + .addComponent("wrap", chkSourceAddic7edProxy) + .addComponent(new PanelCheckBox( + checkbox:chkUserAddic7edLogin, + panelOnNewLine:true, + panelLayout:new MigLayout("insets 0, novisualpadding") + ) + .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(titlePanel, "wrap"); @@ -73,15 +77,19 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { // OPENSUBTITLES this.chkSourceOpenSubtitles = new JCheckBox("OpenSubtitles"); 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(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))); + new PanelCheckBox(checkbox:chkSourceOpenSubtitles, panelOnNewLine:true) + .addTo(titlePanel, "wrap").panel + .addComponent(new PanelCheckBox( + checkbox:chkUserOpenSubtitlesLogin, + panelOnNewLine:true, + panelLayout:new MigLayout("insets 0, novisualpadding") + ) + .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(titlePanel, "wrap"); @@ -91,21 +99,22 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { JScrollPane scrLocalSources = new JScrollPane().viewportView(this.localSourcesFoldersList = new JListWithImages<>()); 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.image, path))); + .actionListener(() -> MemoryFolderChooser.getInstance() + .selectDirectory(this, getText("PreferenceDialog.SelectFolder")) + .map(Path::toAbsolutePath).filter(not(localSourcesFoldersList::contains)) + .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)); + .actionListener(localSourcesFoldersList::removeSelectedItem); + + new PanelCheckBox(checkbox:chkSourceLocal, panelOnNewLine:true) + .addTo(titlePanel).panel + .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(); @@ -128,7 +137,7 @@ public void loadPreferenceSettings() { chkSourceSubscene.setSelected(settingsCtrl.settings.serieSourceSubscene); chkSourceLocal.setSelected(settingsCtrl.settings.serieSourceLocal); settingsCtrl.settings.localSourcesFolders.forEach( - path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.image, path)); + path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.image, path)); } public void savePreferenceSettings() { @@ -146,7 +155,7 @@ public void savePreferenceSettings() { settingsCtrl.settings.serieSourceSubscene = chkSourceSubscene.isSelected(); settingsCtrl.settings.serieSourceLocal = chkSourceLocal.isSelected(); settingsCtrl.settings.localSourcesFolders = - localSourcesFoldersList.stream().map(LabelPanel::getObject).toList(); + localSourcesFoldersList.stream().map(LabelPanel::getObject).toList(); } private boolean hasValidSettingsAddic7ed() { @@ -158,8 +167,8 @@ private boolean hasValidSettingsOpenSubtitles() { return false; } if (chkUserOpenSubtitlesLogin.isSelected() && - !OpenSubtitlesApi.isValidCredentials(txtOpenSubtitlesUsername.getText(), - new String(txtOpenSubtitlesPassword.getPassword()))) { + !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 8697e925..7a543043 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 @@ -74,9 +74,8 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, this.chkReplaceSpace = new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith")); - PanelCheckBox.checkbox(chkReplaceSpace) - .panelOnSameLine() - .addTo(titlePanel, "wrap") + new PanelCheckBox(checkbox:chkReplaceSpace, panelOnNewLine:false) + .addTo(titlePanel, "wrap").panel .addComponent("width pref+10px, wrap", this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); @@ -84,10 +83,11 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, 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"); + JPanel languagePanelRoot = new PanelCheckBox( + checkbox:chkIncludeLanguageCode, + panelOnNewLine:true, + panelLayout:new MigLayout("insets 0, novisualpadding", "[][][]")) + .addTo(titlePanel, "span, growx").panel; { JPanel languagePanel = new JPanel(new MigLayout("insets 0, novisualpadding", "[][][][20px]")); JScrollPane languageScrollPane = 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 bd52fea0..147738c3 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 @@ -75,9 +75,11 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType this.chkRemoveEmptyFolder = new JCheckBox(getText("PreferenceDialog.RemoveEmptyFolders")) .addTo(titlePanel, "span, wrap"); - PanelCheckBox.checkbox(this.chkReplaceSpace = - new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith"))) - .panelOnSameLine().addTo(titlePanel, "span") + new PanelCheckBox( + checkbox:this.chkReplaceSpace = new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith")), + panelOnNewLine:false + ) + .addTo(titlePanel, "span").panel .addComponent(this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); // behaviour 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 07445db4..9936d4c1 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 @@ -36,9 +36,11 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { { this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); - PanelCheckBox.checkbox(this.chkBackupSubtitle = new JCheckBox(getText("PreferenceDialog.BackupSubtitles"))) - .panelOnNewLine() - .addTo(titlePanel, "span, wrap, growx") + new PanelCheckBox( + checkbox:this.chkBackupSubtitle = new JCheckBox(getText("PreferenceDialog.BackupSubtitles")), + panelOnNewLine:true + ) + .addTo(titlePanel, "span, wrap, growx").panel .addComponent("split 3, shrink", new JLabel(getText("PreferenceDialog.Location"))) .addComponent("growx", txtBackupSubtitlePath) .addComponent("shrink", From ec31d9a242103083d57481b46857ec4947e227d9 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 11:11:26 +0200 Subject: [PATCH 31/58] small change --- .../preference/SerieProvidersPanel.java | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) 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 1743b2d9..2c67ee40 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 @@ -17,6 +17,7 @@ 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.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesApi; public class SerieProvidersPanel extends JPanel implements PreferencePanelIntf { @@ -121,41 +122,41 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { } public void loadPreferenceSettings() { - 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)); + Settings settings = settingsCtrl.settings; + chkSourceAddic7ed.setSelected(settings.serieSourceAddic7ed); + chkUserAddic7edLogin.setSelected(settings.loginAddic7edEnabled); + chkSourceAddic7edProxy.setSelected(settings.serieSourceAddic7edProxy); + // chkSourceAddic7edProxy.setEnabled(settings.serieSourceAddic7ed); + txtAddic7edUsername.setText(settings.loginAddic7edUsername); + txtAddic7edPassword.setText(settings.loginAddic7edPassword); + + chkSourceTvSubtitles.setSelected(settings.serieSourceTvSubtitles); + chkSourcePodnapisi.setSelected(settings.serieSourcePodnapisi); + chkSourceOpenSubtitles.setSelected(settings.serieSourceOpensubtitles); + chkUserOpenSubtitlesLogin.setSelected(settings.loginOpenSubtitlesEnabled); + txtOpenSubtitlesUsername.setText(settings.loginOpenSubtitlesUsername); + txtOpenSubtitlesPassword.setText(settings.loginOpenSubtitlesPassword); + chkSourceSubscene.setSelected(settings.serieSourceSubscene); + chkSourceLocal.setSelected(settings.serieSourceLocal); + settings.localSourcesFolders.forEach(path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.image, path)); } public void savePreferenceSettings() { - 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(); + Settings settings = settingsCtrl.settings; + settings.serieSourceAddic7ed = chkSourceAddic7ed.isSelected(); + settings.loginAddic7edEnabled = chkUserAddic7edLogin.isSelected(); + settings.serieSourceAddic7edProxy = chkSourceAddic7edProxy.isSelected(); + settings.loginAddic7edUsername = txtAddic7edUsername.getText(); + settings.loginAddic7edPassword = new String(txtAddic7edPassword.getPassword()); + settings.serieSourceTvSubtitles = chkSourceTvSubtitles.isSelected(); + settings.serieSourcePodnapisi = chkSourcePodnapisi.isSelected(); + settings.serieSourceOpensubtitles = chkSourceOpenSubtitles.isSelected(); + settings.loginOpenSubtitlesEnabled = chkUserOpenSubtitlesLogin.isSelected(); + settings.loginOpenSubtitlesUsername = txtOpenSubtitlesUsername.getText(); + settings.loginOpenSubtitlesPassword = new String(txtOpenSubtitlesPassword.getPassword()); + settings.serieSourceSubscene = chkSourceSubscene.isSelected(); + settings.serieSourceLocal = chkSourceLocal.isSelected(); + settings.localSourcesFolders = localSourcesFoldersList.stream().map(LabelPanel::getObject).toList(); } private boolean hasValidSettingsAddic7ed() { From 6853b6580e0f950cf99e2da0a95a458aa9dd1ddb Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 12:02:32 +0200 Subject: [PATCH 32/58] changes --- .../multisubdownloader/gui/dialog/RenameDialog.java | 2 +- .../multisubdownloader/gui/extra/PanelCheckBox.java | 5 +++++ .../multisubdownloader/gui/extra/TitlePanel.java | 5 +++++ .../gui/panels/preference/GeneralPanel.java | 8 ++++---- .../gui/panels/preference/OptionsPanel.java | 10 +++++----- .../gui/panels/preference/SerieProvidersPanel.java | 4 ++-- .../gui/panels/preference/StructureFilePanel.java | 6 +++--- .../gui/panels/preference/StructureFolderPanel.java | 4 ++-- .../gui/panels/preference/SubtitleBackupPanel.java | 4 ++-- .../gui/panels/preference/VideoLibraryPanel.java | 2 +- 10 files changed, 30 insertions(+), 20 deletions(-) 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 25ff2c78..e3814107 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 @@ -58,7 +58,7 @@ public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String title:getText("PreferenceDialog.Settings"), padding:new BoxModelProperties(0, 20, 0, 0), fillContents:true) - .addTo(contentPane, "span, grow, wrap").panel + .addToPanel(contentPane, "span, grow, wrap") .addComponent("shrink", new JLabel(getText("PreferenceDialog.Location"))) .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().columns(20)) .addComponent("shrink, wrap", new JButton(getText("App.Browse")).actionListener( 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 3f210cbd..53739983 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 @@ -94,4 +94,9 @@ public void setEnabled(boolean enabled) { public boolean isSelected() { return checkbox.isSelected(); } + + public JPanel addToPanel(Container parent, Object constraints=null) { + parent.addComponent(this, constraints); + return this.panel; + } } 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 364bab48..6aab4734 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 @@ -29,4 +29,9 @@ public TitlePanel(String title, panelColumnConstraints); super.add(this.panel = new JPanel(panelLayoutNew), "growx, span"); } + + public JPanel addToPanel(Container parent, Object constraints=null) { + parent.addComponent(this, constraints); + return this.panel; + } } 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 9a37852b..b62c45fb 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 @@ -52,7 +52,7 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { padding:new BoxModelProperties(0, 20, 0, 0), useGrid:true, fillContents:false) - .addTo(this, "span, grow, wrap").panel; + .addToPanel(this, "span, grow, wrap"); { // Language \\ @@ -127,7 +127,7 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { padding:new BoxModelProperties(0, 20, 0, 0), useGrid:true, fillContents:false) - .addTo(this, "span, grow, wrap"); + .addToPanel(this, "span, grow, wrap"); new JLabel(getText("PreferenceDialog.NewUpdateCheck")).addTo(updatePanel); this.cbxUpdateCheckPeriod = new JComboBox<>(UpdateCheckPeriod.values()) @@ -145,14 +145,14 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { title:getText("PreferenceDialog.ConfigureProxy"), padding:new BoxModelProperties(0, 20, 0, 0), fillContents:false) - .addTo(this, "span, grow"); + .addToPanel(this, "span, grow"); new PanelCheckBox( checkbox:this.chkUseProxy = new JCheckBox(getText("PreferenceDialog.UseProxyServer")), panelOnNewLine:false, panelLayout:new MigLayout("insets 0, fill") ) - .addTo(proxyPanel).panel + .addToPanel(proxyPanel) .addComponent(new JLabel(getText("PreferenceDialog.Hostname"))) .addComponent("wrap", this.txtProxyHost = MyTextFieldString.builder().requireValue().build().columns(30)) 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 50197a33..6ad90c7f 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 @@ -38,7 +38,7 @@ public OptionsPanel(SettingsControl settingsCtrl) { title:getText("PreferenceDialog.DownloadOptions"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap").panel + .addToPanel(this, "span, grow, wrap") .addComponent(this.chkAlwaysConfirm = new JCheckBox(getText("PreferenceDialog.CheckBeforeDownloading")), "wrap") .addComponent("wrap, grow", @@ -60,7 +60,7 @@ public OptionsPanel(SettingsControl settingsCtrl) { title:getText("PreferenceDialog.SearchFilter"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap") + .addToPanel(this, "span, grow, wrap") .addComponent( this.chkSubtitleExactMethod = new JCheckBox(getText("PreferenceDialog.SearchFilterExact")), "wrap") @@ -73,21 +73,21 @@ public OptionsPanel(SettingsControl settingsCtrl) { title:getText("PreferenceDialog.TableOptions"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap") + .addToPanel(this, "span, grow, wrap") .addComponent(this.chkOnlyFound = new JCheckBox(getText("PreferenceDialog.ShowOnlyFound"))); new TitlePanel( title:getText("PreferenceDialog.ErrorHandlingOption"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow, wrap") + .addToPanel(this, "span, grow, wrap") .addComponent(this.chkStopOnSearchError = new JCheckBox(getText("PreferenceDialog.StopAfterError"))); new TitlePanel( title:getText("PreferenceDialog.SerieDatabaseSource"), margin:new BoxModelProperties(null, null, 0, null), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow") + .addToPanel(this, "span, grow") .addComponent(this.cbxEpisodeProcessSource = new JComboBox<>(SettingsProcessEpisodeSource.values()), "wrap") .addComponent(this.chkConfirmProviderMapping = 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 2c67ee40..a1a7bb6e 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 @@ -46,7 +46,7 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { this.settingsCtrl = settingsCtrl; JPanel titlePanel = new TitlePanel(getText("PreferenceDialog.SelectPreferredSources")) - .addTo(this, "span, grow").panel; + .addToPanel(this, "span, grow"); { // ADDIC7ED @@ -55,7 +55,7 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { this.chkSourceAddic7edProxy = new JCheckBox(getText("PreferenceDialog.Proxy")); new PanelCheckBox(checkbox:chkSourceAddic7ed, panelOnNewLine:true) - .addTo(titlePanel, "wrap").panel + .addToPanel(titlePanel, "wrap") .addComponent("wrap", chkSourceAddic7edProxy) .addComponent(new PanelCheckBox( checkbox:chkUserAddic7edLogin, 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 7a543043..57879ceb 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 @@ -54,7 +54,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, title:getText("PreferenceDialog.RenameFiles"), margin:new BoxModelProperties(0, 20, 0, 0), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, grow").panel; + .addToPanel(this, "span, grow"); new JLabel(getText("PreferenceDialog.Structure")).addTo(titlePanel, "shrink"); this.txtFileStructure = @@ -75,7 +75,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, this.chkReplaceSpace = new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith")); new PanelCheckBox(checkbox:chkReplaceSpace, panelOnNewLine:false) - .addTo(titlePanel, "wrap").panel + .addToPanel(titlePanel, "wrap") .addComponent("width pref+10px, wrap", this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); @@ -87,7 +87,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, checkbox:chkIncludeLanguageCode, panelOnNewLine:true, panelLayout:new MigLayout("insets 0, novisualpadding", "[][][]")) - .addTo(titlePanel, "span, growx").panel; + .addToPanel(titlePanel, "span, growx"); { JPanel languagePanel = new JPanel(new MigLayout("insets 0, novisualpadding", "[][][][20px]")); JScrollPane languageScrollPane = 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 147738c3..c371ad53 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 @@ -44,7 +44,7 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType padding:new BoxModelProperties(0, 20, 0, 0), useGrid:true, panelColumnConstraints:"[shrink][grow][shrink]") - .addTo(this, "span, grow").panel; + .addToPanel(this, "span, grow"); new JLabel(getText("PreferenceDialog.Location")).addTo(titlePanel, "shrink"); this.txtLibraryFolder = @@ -79,7 +79,7 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType checkbox:this.chkReplaceSpace = new JCheckBox(getText("PreferenceDialog.ReplaceSpaceWith")), panelOnNewLine:false ) - .addTo(titlePanel, "span").panel + .addToPanel(titlePanel, "span") .addComponent(this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); // behaviour 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 9936d4c1..823c8606 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 @@ -31,7 +31,7 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { title:Messages.getText("PreferenceDialog.SubtitlesBackup"), margin:new BoxModelProperties(0), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, growx").panel; + .addToPanel(this, "span, growx"); { this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); @@ -40,7 +40,7 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { checkbox:this.chkBackupSubtitle = new JCheckBox(getText("PreferenceDialog.BackupSubtitles")), panelOnNewLine:true ) - .addTo(titlePanel, "span, wrap, growx").panel + .addToPanel(titlePanel, "span, wrap, growx") .addComponent("split 3, shrink", new JLabel(getText("PreferenceDialog.Location"))) .addComponent("growx", txtBackupSubtitlePath) .addComponent("shrink", 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 a930923a..b5ed414f 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 @@ -44,7 +44,7 @@ public abstract sealed class VideoLibraryPanel extends JPanel implements Prefere title:getText("PreferenceDialog.PerformActions"), margin:new BoxModelProperties(0), padding:new BoxModelProperties(0, 20, 0, 0)) - .addTo(this, "span, growx").panel; + .addToPanel(this, "span, growx"); { this.chkUseTVDBNaming = new JCheckBox(getText("PreferenceDialog.UseTvdbName")).visible( From 9aa4f0054ac33f068553f3f9522fc69c32d7f0d6 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 12:29:54 +0200 Subject: [PATCH 33/58] use optional parameters (9) --- .../actions/search/TextGuiSearchAction.java | 8 +- .../gui/dialog/MappingEpisodeNameDialog.java | 20 +-- .../gui/extra/table/VideoTableModel.java | 4 +- .../lib/control/TvReleaseControl.java | 8 +- .../lib/library/FilenameLibraryBuilder.java | 19 ++- .../lib/library/LibraryBuilder.java | 4 +- .../lib/library/PathLibraryBuilder.java | 13 +- .../subtitleproviders/Local.java | 2 +- .../subtitleproviders/adapters/Adapter.java | 4 +- .../adapters/JAddic7edAdapter.java | 2 +- .../adapters/JAddic7edViaProxyAdapter.java | 2 +- .../adapters/JOpenSubAdapter.java | 2 +- .../adapters/JPodnapisiAdapter.java | 2 +- .../adapters/JSubsceneAdapter.java | 4 +- .../adapters/JTVsubtitlesAdapter.java | 2 +- .../sublibrary/control/ReleaseParser.java | 21 ++-- .../subtools/sublibrary/model/TvRelease.java | 119 ++++-------------- .../assertions/TvReleaseAssert.java | 2 +- 18 files changed, 79 insertions(+), 159 deletions(-) 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 077a6769..082ebb5d 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 @@ -43,12 +43,8 @@ protected List createReleases() { // TODO: Redefine what a "release" is. Release release = switch (type) { - case EPISODE -> TvRelease.builder() - .name(name) - .season(inputPanel.season) - .episode(inputPanel.episode) - .quality(inputPanel.quality) - .build(); + case EPISODE -> + new TvRelease(name:name, season:inputPanel.season, episode:inputPanel.episode, quality:inputPanel.quality); case MOVIE -> new MovieRelease(name:name, quality:inputPanel.quality); default -> releaseFactory.createRelease(Path.of( name + (VideoExtensions.values().stream().anyMatch(ext -> name.endsWith("." + ext)) ? "" : ".")), 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 495b2671..0c4f6812 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 @@ -3,9 +3,10 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.RowSorter.*; -import javax.swing.border.*; -import javax.swing.table.*; +import javax.swing.RowSorter.SortKey; +import javax.swing.border.EmptyBorder; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableRowSorter; import java.awt.*; import java.io.Serial; import java.util.Arrays; @@ -102,13 +103,12 @@ public MappingEpisodeNameDialog(JFrame frame, Manager manager, SubtitleProviderS 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(); + TvRelease tvRelease = new TvRelease( + name:currentName, + season:row.serieMapping.season, + episode:1, + originalName:currentName, + customName:newName); try { provider.getProviderSerieId(tvRelease).ifPresentOrElse(serieId -> { row.serieMapping = 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 ab5bb654..06b5f89b 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 @@ -2,7 +2,7 @@ import static org.lodder.subtools.multisubdownloader.gui.extra.table.SearchColumnName.*; -import javax.swing.table.*; +import javax.swing.table.DefaultTableModel; import java.io.Serial; import java.util.ArrayList; import java.util.EnumMap; @@ -104,7 +104,7 @@ public Row(Release release, UserInteractionHandler userInteractionHandler) { case SELECT -> false; case OBJECT -> release; case SEASON -> release instanceof TvRelease tvRelease ? tvRelease.season : null; - case EPISODE -> release instanceof TvRelease tvRelease ? tvRelease.firstEpisodeNumber : null; + case EPISODE -> release instanceof TvRelease tvRelease ? tvRelease.firstEpisode : null; case TYPE -> release.videoType; case TITLE -> release instanceof TvRelease tvRelease ? tvRelease.title : null; default -> throw new IllegalArgumentException("Unexpected value: " + searchColumn); 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 830f38e9..faba0cc4 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 @@ -36,7 +36,7 @@ public void process() throws ReleaseControlException { throw new ReleaseControlException("Unable to extract episode details, check file", tvRelease); } else { LOGGER.debug("process: show name [{}], season [{}], episode [{}]", tvRelease.name, tvRelease.season, - tvRelease.episodeNumbers); + tvRelease.episodes); if (tvRelease.special) { processSpecial(); } else { @@ -49,10 +49,10 @@ private void processTvdb() throws ReleaseControlException { jtvdba.getSerie(tvRelease.name).useIfPresent(tvdbSerie -> { tvRelease.tvdbId = tvdbSerie.id; tvRelease.originalName = tvdbSerie.serieName; - jtvdba.getEpisode(tvdbSerie.id, tvRelease.season, tvRelease.firstEpisodeNumber) + jtvdba.getEpisode(tvdbSerie.id, tvRelease.season, tvRelease.firstEpisode) .useIfPresent(tvRelease::updateTvdbEpisodeInfo) .orElseThrow(() -> new ReleaseControlException( - "Season ${tvRelease.season} Episode ${tvRelease.episodeNumbers} not found, check file", + "Season ${tvRelease.season} Episode ${tvRelease.episodes} not found, check file", tvRelease)); }).orElseThrow(() -> new ReleaseControlException("Show not found, check file", tvRelease)); } @@ -62,7 +62,7 @@ private void processSpecial() throws ReleaseControlException { tvRelease.tvdbId = tvdbSerie.id; tvRelease.originalName = tvdbSerie.serieName; if (settings.processEpisodeSource == SettingsProcessEpisodeSource.TVDB) { - jtvdba.getEpisode(tvdbSerie.id, tvRelease.season, tvRelease.firstEpisodeNumber) + jtvdba.getEpisode(tvdbSerie.id, tvRelease.season, tvRelease.firstEpisode) .ifPresent(tvRelease::updateTvdbEpisodeInfo); } }).orElseThrow(() -> new ReleaseControlException("Show not found, check file", tvRelease)); 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 8decfd1d..4f76f0d8 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 @@ -58,17 +58,14 @@ public Path build(Release release) { // order is important! fName = replace(fName, SerieStructureTag.SHOW_NAME, getShowName(tvRelease.name)); fName = - replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_LONG, tvRelease.episodeNumbers, - true); + replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_LONG, tvRelease.episodes, true); + fName = replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_SHORT, tvRelease.episodes, + false); + fName = replace(fName, SerieStructureTag.SEASON_LONG, formatNumber(tvRelease.season, true)); + fName = replace(fName, SerieStructureTag.SEASON_SHORT, formatNumber(tvRelease.season, false)); + fName = replace(fName, SerieStructureTag.EPISODE_LONG, formatNumber(tvRelease.firstEpisode, true)); fName = - replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_SHORT, tvRelease.episodeNumbers, - false); - fName = replace(fName, SerieStructureTag.SEASON_LONG, formattedNumber(tvRelease.season, true)); - fName = replace(fName, SerieStructureTag.SEASON_SHORT, formattedNumber(tvRelease.season, false)); - fName = replace(fName, SerieStructureTag.EPISODE_LONG, - formattedNumber(tvRelease.firstEpisodeNumber, true)); - fName = replace(fName, SerieStructureTag.EPISODE_SHORT, - formattedNumber(tvRelease.firstEpisodeNumber, false)); + replace(fName, SerieStructureTag.EPISODE_SHORT, formatNumber(tvRelease.firstEpisode, false)); fName = replace(fName, SerieStructureTag.TITLE, tvRelease.title); fName = replace(fName, SerieStructureTag.QUALITY, release.quality); fName = replace(fName, SerieStructureTag.RELEASE_GROUP, release.releaseGroup); @@ -80,7 +77,7 @@ public Path build(Release release) { String fName = structure; // order is important! fName = replace(fName, MovieStructureTag.MOVIE_TITLE, getShowName(movieRelease.name)); - fName = replace(fName, MovieStructureTag.YEAR, formattedNumber(movieRelease.year, false)); + fName = replace(fName, MovieStructureTag.YEAR, formatNumber(movieRelease.year, false)); fName = replace(fName, MovieStructureTag.QUALITY, release.quality); fName = replace(fName, MovieStructureTag.RELEASE_GROUP, release.releaseGroup); 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 548be19a..98eba8d0 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 @@ -36,7 +36,7 @@ protected String replaceFormattedEpisodeNumber(String structure, StructureTag ta separator = ""; } String formattedEpisodeNumber = episodeNumbers.stream() - .map(episode -> formattedNumber(episode, leadingZero)) + .map(episode -> formatNumber(episode, leadingZero)) .collect(Collectors.joining(separator)); return structure.replace(tag.label, formattedEpisodeNumber); } @@ -44,7 +44,7 @@ protected String replaceFormattedEpisodeNumber(String structure, StructureTag ta } - protected String formattedNumber(int number, boolean leadingZero) { + protected String formatNumber(int number, boolean leadingZero) { return number < 10 && leadingZero ? "0" + number : Integer.toString(number); } } 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 c2c51963..7c1ca043 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 @@ -62,13 +62,12 @@ private Path buildEpisode(TvRelease tvRelease) { folder = folder.replace(SerieStructureTag.SHOW_NAME.label, getShowName(tvRelease.name)) .removeIllegalWindowsChars(); // order is important! - 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 = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_LONG, tvRelease.episodes, true); + folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_SHORT, tvRelease.episodes, false); + folder = replace(folder, SerieStructureTag.SEASON_LONG, formatNumber(tvRelease.season, true)); + folder = replace(folder, SerieStructureTag.SEASON_SHORT, formatNumber(tvRelease.season, false)); + folder = replace(folder, SerieStructureTag.EPISODE_LONG, formatNumber(tvRelease.firstEpisode, true)); + folder = replace(folder, SerieStructureTag.EPISODE_SHORT, formatNumber(tvRelease.firstEpisode, false)); folder = replace(folder, SerieStructureTag.TITLE, tvRelease.title); folder = replace(folder, SerieStructureTag.QUALITY, tvRelease.quality); folder = replace(folder, SerieStructureTag.RELEASE_GROUP, tvRelease.releaseGroup); 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 d0ee5f1d..b989747c 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 @@ -68,7 +68,7 @@ public Set searchSubtitles(TvRelease tvRelease, Language language) { Release release = vfp.parse(fileSub); if ((release.videoType == VideoType.EPISODE) && (((TvRelease) release).season == tvRelease.season && - new HashSet<>(((TvRelease) release).episodeNumbers).containsAll(tvRelease.episodeNumbers))) { + new HashSet<>(((TvRelease) release).episodes).containsAll(tvRelease.episodes))) { TvReleaseControl epCtrl = new TvReleaseControl((TvRelease) release, settings, manager, userInteractionHandler); 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 34d16c1b..9acebeaf 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 @@ -98,9 +98,7 @@ default Set searchSubtitles(TvRelease tvRelease, Language language) { } catch (Exception 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); + TvRelease.formatName(displayName, tvRelease.season, tvRelease.firstEpisode), e.getMessage()), e); return Set.of(); } } 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 3aa82ccb..8278d5eb 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 @@ -89,7 +89,7 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws Addic7edException { return getProviderSerieId(tvRelease).map( - providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { try { return getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); } catch (Addic7edException e) { 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 ae4b7a32..912a9619 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 @@ -73,7 +73,7 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set @Override public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws ApiException { return getProviderSerieId(tvRelease).map( - providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { try { return new ExecuteCall<>( () -> getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language)) 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 93a55559..299ae2b4 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 @@ -96,7 +96,7 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws OpenSubtitlesException { return getProviderSerieId(tvRelease).map( - providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { try { return getApi().searchSubtitles() .query(providerSerieId.name) 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 991fc98e..4e317ee8 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 @@ -80,7 +80,7 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws PodnapisiException { return getProviderSerieId(tvRelease).map( - providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { try { return api.getSerieSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); } catch (PodnapisiException e) { 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 40a08e78..f2fd6c2c 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 @@ -89,7 +89,7 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws SubsceneException { return getProviderSerieId(tvRelease).map( - providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { try { return getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); } catch (SubsceneException e) { @@ -133,7 +133,7 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection language == sub.getLanguage()) .filter(sub -> sub.getName() - .contains(getSeasonEpisodeString(tvRelease.season, tvRelease.firstEpisodeNumber))) + .contains(getSeasonEpisodeString(tvRelease.season, tvRelease.firstEpisode))) .map(sub -> Subtitle.downloadSource(sub.getUrlSupplier()) .subtitleSource(subtitleSource) .fileName(sub.getName().removeIllegalFilenameChars()) 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 8c5c2df2..4a304726 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 @@ -86,7 +86,7 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws TvSubtitlesException { return getProviderSerieId(tvRelease).map( - providerSerieId -> tvRelease.episodeNumbers.stream().flatMap(episode -> { + providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { try { return getApi().getSubtitles(providerSerieId, tvRelease.season, episode, language).stream(); } catch (TvSubtitlesException e) { 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 7364700a..44ca6d56 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 @@ -170,17 +170,16 @@ private Release parsePatternResult(Path file, String fileParseName, String exten throw new ReleaseParseException("Could not find a season and/or episodes" + fileParseName); } - return TvRelease.builder() - .name(cleanUnwantedChars(name)) - .season(season) - .episodes(episodes) - .file(file) - .title(cleanUnwantedChars(parserResults.getNamedMatchValue(TITLE))) - .releaseGroup(releaseGroup) - .special(isSpecialEpisode(season, episodes)) - .quality(StringUtils.toRootLowerCase(quality)) - .extension(extension) - .build(); + return new TvRelease( + name:cleanUnwantedChars(name), + season:season, + episodes:episodes, + file:file, + title:cleanUnwantedChars(parserResults.getNamedMatchValue(TITLE)), + releaseGroup:releaseGroup, + special:isSpecialEpisode(season, episodes), + quality:StringUtils.toRootLowerCase(quality), + extension:extension); } @AllArgsConstructor 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 9156cdc9..a83d085f 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,8 +5,6 @@ import java.util.List; import java.util.OptionalInt; -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; @@ -16,7 +14,7 @@ public final class TvRelease extends Release { // parsed from the filename @val String name; - @val List episodeNumbers; + @val List episodes; @val int season; @var String title; @var int tvdbId; @@ -26,105 +24,38 @@ public final class TvRelease extends Release { // custom name which can be used to search subtitle providers @val String customName; - public String getNameWithSeasonEpisode() { - return formatName(name, season, episodeNumbers.isEmpty() ? -1 : firstEpisodeNumber); - } - - public static String formatName(String serieName, int season, int episode) { - return serieName + " " + formatSeasonEpisode(season, episode); - } - - public static String formatSeasonEpisode(int season, int episode) { - return "S%02dE%02d".formatted(season, episode); - } - - public interface TvReleaseBuilderShowName { - TvReleaseBuilderSeason name(String name); - } - - public interface TvReleaseBuilderSeason { - TvReleaseBuilderEpisode season(int season); - } - - public interface TvReleaseBuilderEpisode { - TvReleaseBuilderOther episode(int episode); - - TvReleaseBuilderOther episodes(List episodes); - } - - public interface TvReleaseBuilderOther { - TvReleaseBuilderOther file(Path file); - - TvReleaseBuilderOther quality(String quality); - - TvReleaseBuilderOther special(boolean special); - - TvReleaseBuilderOther releaseGroup(String releaseGroup); - - TvReleaseBuilderOther title(String title); - - TvReleaseBuilderOther customName(String customName); - - TvReleaseBuilderOther originalName(String originalName); - - TvReleaseBuilderOther extension(String extension); - - TvRelease build(); - } - - public static TvReleaseBuilderShowName builder() { - return new TvReleaseBuilder(); - } - - @Setter - @Accessors(chain = true, fluent = true) - public static class TvReleaseBuilder - implements TvReleaseBuilderOther, TvReleaseBuilderEpisode, TvReleaseBuilderSeason, - TvReleaseBuilderShowName { - private String name; - private String title; - private int season; - private List episodes; - private boolean special; - private String quality; - private String extension; - private Path file; - private String releaseGroup; - private String customName; - private String originalName; - - @Override - public TvReleaseBuilder episode(int episode) { - this.episodes = List.of(episode); - return this; - } - - @Override - public TvReleaseBuilder episodes(List episodes) { - this.episodes = Collections.unmodifiableList(episodes); - return this; - } - - @Override - public TvRelease build() { - return new TvRelease(file, releaseGroup, quality, extension, name, originalName, customName, title, season, - episodes, special); - } + public TvRelease(Path file=null, String releaseGroup=null, String quality=null, String extension=null, String name, + String originalName=null, String customName=null, String title=null, int season, int episode, + boolean special=false) { + this(file, releaseGroup, quality, extension, name, originalName, customName, title, season, List.of(episode), + special); } - private TvRelease(Path file, String releaseGroup, String quality, String extension, String name, - String originalName, String customName, String title, int season, List episodeNumbers, - boolean special) { + public TvRelease(Path file=null, String releaseGroup=null, String quality=null, String extension=null, String name, + String originalName=null, String customName=null, String title=null, int season, List episodes, + boolean special=false) { super(VideoType.EPISODE, file, releaseGroup, quality, extension); this.name = name; this.title = title; this.season = season; - this.episodeNumbers = episodeNumbers; + this.episodes = Collections.unmodifiableList(episodes); this.special = special; this.originalName = originalName; this.customName = customName; } + public String getNameWithSeasonEpisode() { + return formatName(name, season, episodes.isEmpty() ? -1 : firstEpisode); + } + + public static String formatName(String serieName, int season, int episode) { + return serieName + " " + formatSeasonEpisode(season, episode); + } + + public static String formatSeasonEpisode(int season, int episode) { + return "S%02dE%02d".formatted(season, episode); + } + public void updateTvdbEpisodeInfo(TheTvdbEpisode tvdbEpisode) { this.title = tvdbEpisode.episodeName; // update to reflect correct episode title } @@ -133,13 +64,13 @@ public OptionalInt getTvdbIdOptional() { return tvdbId == 0 ? OptionalInt.empty() : OptionalInt.of(tvdbId); } - public int getFirstEpisodeNumber() { - return episodeNumbers.first; + public int getFirstEpisode() { + return episodes.first; } @Override public String toString() { - return "${getClass().getSimpleName()}: $name s$season e$episodeNumbers $quality $releaseGroup"; + return "${getClass().getSimpleName()}: $name s$season e$episodes $quality $releaseGroup"; } @Override diff --git a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java index a7ae9f62..3f4e2e92 100644 --- a/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java +++ b/SubLibrary/src/test/java/org/lodder/subtools/sublibrary/assertions/TvReleaseAssert.java @@ -20,7 +20,7 @@ public TvReleaseAssert(TvRelease actual) { public @Self TvReleaseAssert hasEpisodes(Integer... episodeNumbers) { isNotNull(); - assertThat(actual.episodeNumbers).containsExactly(episodeNumbers); + assertThat(actual.episodes).containsExactly(episodeNumbers); return this; } From 113d0756c0f188d9673873b8de44847c222a4972 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 12:45:44 +0200 Subject: [PATCH 34/58] replace lombok with normal java code --- .../opensubtitles/DownloadSubtitle.java | 9 ++--- .../subscene/SubsceneApi.java | 28 ++++++++------- .../model/SubsceneSubtitleDescriptor.java | 34 +++++++++---------- 3 files changed, 36 insertions(+), 35 deletions(-) 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 ff66ff16..0a84aa18 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,16 +1,12 @@ 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; -@Accessors(fluent = true, chain = true) -@Setter @RequiredArgsConstructor public final class DownloadSubtitle extends OpenSubtitlesExecuter { private final ApiClient apiClient; @@ -25,4 +21,9 @@ public Download200Response download() throws OpenSubtitlesException { throw new OpenSubtitlesException(e); } } + + public DownloadSubtitle fileId(int fileId) { + this.fileId = fileId; + return this; + } } 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 0863f03e..a7f3b3bf 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 @@ -13,6 +13,7 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import com.pivovarit.function.ThrowingSupplier; import lombok.Getter; import lombok.RequiredArgsConstructor; import manifold.ext.props.rt.api.override; @@ -100,20 +101,21 @@ public List getSubtitles(SerieMapping providerSerieI return getJsoupDocument(DOMAIN + providerSerieId.providerId) .selectAllByCss("td.a1") .stream() -// .map(Element::parent) .map(el -> (Element) el.parent()) - .map(row -> new SubsceneSubtitleDescriptor().setLanguage( - Language.fromValueOptional(row.selectAllByCss(".a1 span.l").text().trim()) - .orElse(null)) - .setUrlSupplier(() -> getDownloadUrl( - DOMAIN + row.selectAllByCss(".a1 > a").attr("href").trim())) - .setName(row.selectAllByCss(".a1 span:not(.l)").text().trim()) - .setHearingImpaired(row.selectFirstByCss(".a41") != null) - .setUploader(row.selectFirstByCss(".a5 > a").text().trim()) - .setComment(row.selectFirstByCss(".a6 > div").text().trim())) - .filter(subDescriptor -> subDescriptor.getSeasonEpisode() != null && - subDescriptor.getSeasonEpisode().episodes.stream() - .anyMatch(ep -> ep == episode)) + .map(row -> { + Language lang = Language.fromValueOptional(row.selectAllByCss(".a1 span.l").text().trim()) + .orElse(null); + String name = row.selectAllByCss(".a1 span:not(.l)").text().trim(); + boolean hearingImpaired = row.selectFirstByCss(".a41") != null; + String uploader = row.selectFirstByCss(".a5 > a").text().trim(); + String comment = row.selectFirstByCss(".a6 > div").text().trim(); + ThrowingSupplier urlSupplier = () -> getDownloadUrl( + DOMAIN + row.selectAllByCss(".a1 > a").attr("href").trim()); + return new SubsceneSubtitleDescriptor(lang, name, hearingImpaired, uploader, comment, + urlSupplier); + }) + .filter(subDescriptor -> subDescriptor.seasonEpisode != null && + subDescriptor.seasonEpisode.episodes.stream().anyMatch(ep -> ep == episode)) .toList(); } catch (Exception e) { throw new SubsceneException(e); 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 fe0ecaa6..3e01dd14 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 @@ -2,33 +2,31 @@ import com.pivovarit.function.ThrowingSupplier; import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; +import manifold.ext.props.rt.api.val; 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) -@NoArgsConstructor -@Getter -@Setter public class SubsceneSubtitleDescriptor { - private Language language; - private String name; - private boolean hearingImpaired; - private String uploader; - private String comment; - private SeasonEpisode seasonEpisode; - @EqualsAndHashCode.Exclude - private ThrowingSupplier urlSupplier; + @val Language language; + @val String name; + @val boolean hearingImpaired; + @val String uploader; + @val String comment; + @val SeasonEpisode seasonEpisode; + @EqualsAndHashCode.Exclude @val ThrowingSupplier urlSupplier; - public SubsceneSubtitleDescriptor setName(String name) { + public SubsceneSubtitleDescriptor(Language language, + String name, boolean hearingImpaired, String uploader, String comment, + ThrowingSupplier urlSupplier) { + this.language = language; this.name = name; + this.hearingImpaired = hearingImpaired; + this.uploader = uploader; + this.comment = comment; this.seasonEpisode = SeasonEpisode.fromText(name).orElse(null); - return this; + this.urlSupplier = urlSupplier; } } From 25d230f2d8979704c3997dbde3b8c24086430c19 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 16:56:05 +0200 Subject: [PATCH 35/58] use optional parameters (10) --- .../subtools/multisubdownloader/GUI.java | 10 +- .../actions/DownloadAction.java | 32 ++-- .../subtitleproviders/Local.java | 39 ++--- .../adapters/JAddic7edAdapter.java | 19 +-- .../adapters/JOpenSubAdapter.java | 21 +-- .../adapters/JPodnapisiAdapter.java | 21 +-- .../adapters/JSubsceneAdapter.java | 25 +-- .../adapters/JTVsubtitlesAdapter.java | 21 +-- .../gestdown/JAddic7edProxyGestdownApi.java | 22 +-- .../subscene/SubsceneApi.java | 2 +- .../sublibrary/model/SeasonEpisode.java | 4 + .../subtools/sublibrary/model/Subtitle.java | 149 ++++++++---------- 12 files changed, 181 insertions(+), 184 deletions(-) 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 47ed3621..edf2059f 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -426,12 +426,10 @@ private void downloadText() { } try { - if (subtitle.sourceLocation == Subtitle.SourceLocation.FILE) { - subtitle.file.copyToDir(path); - } else { - String url = subtitle.sourceLocation == Subtitle.SourceLocation.URL ? subtitle.url : - subtitle.urlSupplier.get(); - app.makeManager().store(url, path.resolve(filename)); + switch (subtitle.downloadSource.sourceLocation) { + case FILE -> subtitle.downloadSource.file.copyToDir(path); + case URL, URL_SUPPLIER -> app.makeManager() + .store(subtitle.downloadSource.getValue(), path.resolve(filename)); } } catch (IOException | ManagerException e) { LOGGER.error("downloadText", e); 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 d8db919b..a64b3453 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 @@ -64,22 +64,24 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar String subFileName = filenameLibraryBuilder.buildSubtitle(release, subtitle, videoFileName, version); Path subFile = path.resolve(subFileName); - boolean success; - if (subtitle.sourceLocation == Subtitle.SourceLocation.FILE) { - subtitle.file.copyToDir(path); - success = true; - } else { - try { - String 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 [${release.releaseDescription}] " + - "for subtitle provider [${e.subtitleProvider}] (${e.getMessage()})", e); - throw new RuntimeException(e); + boolean success = switch (subtitle.downloadSource.sourceLocation) { + case FILE -> { + subtitle.downloadSource.file.copyToDir(path); + yield true; } - } + case URL, URL_SUPPLIER -> { + try { + String url = subtitle.downloadSource.getValue(); + boolean result = manager.store(url, subFile); + LOGGER.debug("doDownload file was [{}] ", result); + yield result; + } catch (SubtitlesProviderException 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)) { 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 b989747c..b9e9812b 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 @@ -77,16 +77,16 @@ public Set searchSubtitles(TvRelease tvRelease, Language language) { Language detectedLang = DetectLanguage.execute(fileSub); if (detectedLang == language) { LOGGER.debug("Local Sub found, adding [{}]", fileSub); - listFoundSubtitles.add( - Subtitle.downloadSource(fileSub) - .subtitleSource(subtitleSource) - .fileName(fileSub.fileNameAsString) - .language(language) - .quality(ReleaseParser.getQualityKeyword(fileSub.fileNameAsString)) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true)) - .uploader(fileSub.toAbsolutePath().toString()) - .hearingImpaired(false)); + listFoundSubtitles.add(new Subtitle( + downloadSource:Subtitle.DownloadSource.of(fileSub), + subtitleSource:subtitleSource, + fileName:fileSub.fileNameAsString, + language:language, + quality:ReleaseParser.getQualityKeyword(fileSub.fileNameAsString), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true), + uploader:fileSub.toAbsolutePath().toString(), + hearingImpaired:false)); } } } @@ -119,15 +119,16 @@ public Set searchSubtitles(MovieRelease movieRelease, Language languag if (release.getImdbId().equals(movieRelease.getImdbId()) && DetectLanguage.execute(fileSub) == language) { LOGGER.debug("Local Sub found, adding {}", fileSub); - listFoundSubtitles.add(Subtitle.downloadSource(fileSub) - .subtitleSource(subtitleSource) - .fileName(fileSub.fileNameAsString) - .language(language) // TODO previously: language(""). This was not correct? - .quality(ReleaseParser.getQualityKeyword(fileSub.fileNameAsString)) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true)) - .uploader(fileSub.toAbsolutePath().toString()) - .hearingImpaired(false)); + listFoundSubtitles.add(new Subtitle( + downloadSource:Subtitle.DownloadSource.of(fileSub), + subtitleSource:subtitleSource, + fileName:fileSub.fileNameAsString, + language:language,// TODO previously: language(""). This was not correct? + quality:ReleaseParser.getQualityKeyword(fileSub.fileNameAsString), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true), + uploader:fileSub.toAbsolutePath().toString(), + hearingImpaired:false)); } } default -> { 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 8278d5eb..0b257664 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 @@ -106,15 +106,16 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection language == sub.language) - .map(sub -> Subtitle.downloadSource(sub.url) - .subtitleSource(subtitleSource) - .fileName(StringExt.removeIllegalFilenameChars(sub.title + " " + sub.version)) - .language(sub.language) - .quality(ReleaseParser.getQualityKeyword(sub.title + " " + sub.version)) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(sub.title, sub.title.endsWith(".srt"))) - .uploader(sub.uploader) - .hearingImpaired(false)) + .map(sub -> new Subtitle( + downloadSource:Subtitle.DownloadSource.of(sub.url), + subtitleSource:subtitleSource, + fileName:StringExt.removeIllegalFilenameChars(sub.title + " " + sub.version), + language:sub.language, + quality:ReleaseParser.getQualityKeyword(sub.title + " " + sub.version), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:ReleaseParser.extractReleaseGroup(sub.title, sub.title.endsWith(".srt")), + uploader:sub.uploader, + hearingImpaired:false)) .collect(Collectors.toSet()); } 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 299ae2b4..90c7e416 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 @@ -131,16 +131,17 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection 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.fileName, file.fileName.endsWith(".srt"))) - .uploader(attributes.getUploader() != null ? attributes.getUploader().getName() : null) - .hearingImpaired(Boolean.TRUE == attributes.isHearingImpaired()); + return new Subtitle( + downloadSource:Subtitle.DownloadSource.of( + () -> 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.fileName, file.fileName.endsWith(".srt")), + uploader:attributes.getUploader() != null ? attributes.getUploader().getName() : null, + hearingImpaired:Boolean.TRUE == attributes.isHearingImpaired()); } @Override 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 4e317ee8..420d2d50 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 @@ -101,16 +101,17 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection buildListSubtitles(Language language, Collection lSubtitles) { return lSubtitles.stream() .filter(ossd -> StringUtils.isNotBlank(ossd.releaseString)) - .map(ossd -> Subtitle.downloadSource(ossd.url) - .subtitleSource(subtitleSource) - .fileName(ossd.releaseString) - .language(language) - .quality(ReleaseParser.getQualityKeyword(ossd.releaseString)) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(ossd.releaseString, - StringUtils.endsWith(ossd.releaseString, ".srt"))) - .uploader(ossd.uploaderName) - .hearingImpaired(ossd.hearingImpaired)) + .map(ossd -> new Subtitle( + downloadSource:Subtitle.DownloadSource.of(ossd.url), + subtitleSource:subtitleSource, + fileName:ossd.releaseString, + language:language, + quality:ReleaseParser.getQualityKeyword(ossd.releaseString), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:ReleaseParser.extractReleaseGroup(ossd.releaseString, + StringUtils.endsWith(ossd.releaseString, ".srt")), + uploader:ossd.uploaderName, + hearingImpaired:ossd.hearingImpaired)) .collect(Collectors.toSet()); } 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 f2fd6c2c..eee50bc8 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.adapters; +import static org.lodder.subtools.sublibrary.model.Subtitle.*; + import java.util.Collection; import java.util.Comparator; import java.util.List; @@ -131,18 +133,19 @@ public List getSortedProviderSerieIds(OptionalInt tvdbIdOptiona public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { return subtitles.stream() - .filter(sub -> language == sub.getLanguage()) - .filter(sub -> sub.getName() + .filter(sub -> language == sub.language) + .filter(sub -> sub.name .contains(getSeasonEpisodeString(tvRelease.season, tvRelease.firstEpisode))) - .map(sub -> Subtitle.downloadSource(sub.getUrlSupplier()) - .subtitleSource(subtitleSource) - .fileName(sub.getName().removeIllegalFilenameChars()) - .language(sub.getLanguage()) - .quality(ReleaseParser.getQualityKeyword(sub.getName())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(sub.getName(), false)) - .uploader(sub.getUploader()) - .hearingImpaired(sub.isHearingImpaired())) + .map(sub -> new Subtitle( + downloadSource:DownloadSource.of(sub.urlSupplier), + subtitleSource:subtitleSource, + fileName:sub.name.removeIllegalFilenameChars(), + language:sub.language, + quality:ReleaseParser.getQualityKeyword(sub.name), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:ReleaseParser.extractReleaseGroup(sub.name, false), + uploader:sub.uploader, + hearingImpaired:sub.hearingImpaired)) .collect(Collectors.toSet()); } 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 4a304726..a1576634 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 @@ -102,16 +102,17 @@ public Set searchSerieSubtitles(TvRelease tvRelea public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { return subtitles.stream() - .map(sub -> Subtitle.downloadSource(sub.url) - .subtitleSource(subtitleSource) - .fileName(sub.filename) - .language(language) - .quality(ReleaseParser.getQualityKeyword(sub.filename + " " + sub.rip)) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(ReleaseParser.extractReleaseGroup(sub.filename, - StringUtils.endsWith(sub.filename, ".srt"))) - .uploader(sub.author) - .hearingImpaired(false)) + .map(sub -> new Subtitle( + downloadSource:Subtitle.DownloadSource.of(sub.url), + subtitleSource:subtitleSource, + fileName:sub.filename, + language:language, + quality:ReleaseParser.getQualityKeyword(sub.filename + " " + sub.rip), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:ReleaseParser.extractReleaseGroup(sub.filename, + StringUtils.endsWith(sub.filename, ".srt")), + uploader:sub.author, + hearingImpaired:false)) .collect(Collectors.toSet()); } 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 f22c136b..22a59724 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 @@ -64,23 +64,23 @@ public Set getSubtitles(SerieMapping providerSerieId, int season, int response.getMatchingSubtitles() .stream() .filter(SubtitleDto::isCompleted) - .map(sub -> mapToSubtitle(sub, response.getEpisode(), language)) + .map(sub -> mapToSubtitle(sub, response.episode, language)) .forEach(results::add); return results; }).getCollection(); } private Subtitle mapToSubtitle(SubtitleDto sub, EpisodeDto episodedto, Language language) { - return Subtitle.downloadSource(getDownloadUrl(sub.getDownloadUri())) - .subtitleSource(subtitleSource) - .fileName(StringExt.removeIllegalFilenameChars( - "${episodedto.show} - ${episodedto.title} - ${sub.version}")) - .language(language) - .quality(ReleaseParser.getQualityKeyword(episodedto.getTitle() + " " + sub.getVersion())) - .subtitleMatchType(SubtitleMatchType.EVERYTHING) - .releaseGroup(sub.getVersion()) - .uploader("") - .hearingImpaired(false); + return new Subtitle( + downloadSource:Subtitle.DownloadSource.of(getDownloadUrl(sub.getDownloadUri())), + subtitleSource:subtitleSource, + fileName:StringExt.removeIllegalFilenameChars("${episodedto.show} - ${episodedto.title} - ${sub.version}"), + language:language, + quality:ReleaseParser.getQualityKeyword(episodedto.getTitle() + " " + sub.getVersion()), + subtitleMatchType:SubtitleMatchType.EVERYTHING, + releaseGroup:sub.getVersion(), + uploader:"", + hearingImpaired:false); } public String getDownloadUrl(String subtitleId) { 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 a7f3b3bf..54acacf3 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 @@ -115,7 +115,7 @@ public List getSubtitles(SerieMapping providerSerieI urlSupplier); }) .filter(subDescriptor -> subDescriptor.seasonEpisode != null && - subDescriptor.seasonEpisode.episodes.stream().anyMatch(ep -> ep == episode)) + subDescriptor.seasonEpisode.containsEpisode(episode)) .toList(); } catch (Exception e) { throw new SubsceneException(e); 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 f93db1cd..6bb18c69 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 @@ -42,4 +42,8 @@ public static Optional fromText(String text) { } return Optional.empty(); } + + public boolean containsEpisode(int episode) { + return episodes.contains(episode); + } } 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 c4099768..1d5d2486 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 @@ -3,24 +3,17 @@ import java.nio.file.Path; import com.pivovarit.function.ThrowingSupplier; -import lombok.AccessLevel; import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; 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; -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @EqualsAndHashCode public class Subtitle { - @EqualsExclude - @val ThrowingSupplier urlSupplier; - @val String url; - @val Path file; - @val SourceLocation sourceLocation; + @val DownloadSource downloadSource; @var String fileName; @var Language language; @var String releaseGroup; @@ -31,91 +24,83 @@ public class Subtitle { @var String quality; @var int score; - public enum SourceLocation { - URL, URL_SUPPLIER, FILE - } - - private Subtitle(ThrowingSupplier urlSupplier) { - this.urlSupplier = urlSupplier; - this.url = null; - this.file = null; - this.sourceLocation = SourceLocation.URL_SUPPLIER; - } - - private Subtitle(String url) { - this.urlSupplier = null; - this.url = url; - this.file = null; - this.sourceLocation = SourceLocation.URL; - } - - private Subtitle(Path file) { - this.urlSupplier = null; - this.url = null; - this.file = file; - this.sourceLocation = SourceLocation.FILE; - } - - public static Subtitle downloadSource(ThrowingSupplier urlSupplier) { - return new Subtitle(urlSupplier); - } - - public static Subtitle downloadSource(String url) { - return new Subtitle(url); - } - - public static Subtitle downloadSource(Path file) { - return new Subtitle(file); - } - - @Override - public String toString() { - return "${getClass().getSimpleName()}: $fileName $quality"; - } - - public Subtitle fileName(String fileName) { + public Subtitle(DownloadSource downloadSource, + String fileName=null, + Language language=null, + String releaseGroup=null, + String uploader=null, + SubtitleMatchType subtitleMatchType=null, + SubtitleSource subtitleSource=null, + boolean hearingImpaired=false, + String quality=null, + int score=0) { + this.downloadSource = downloadSource; this.fileName = fileName; - return this; - } - - public Subtitle language(Language language) { this.language = language; - return this; - } - - public Subtitle releaseGroup(String releaseGroup) { this.releaseGroup = releaseGroup; - return this; - } - - public Subtitle uploader(String uploader) { this.uploader = uploader; - return this; - } - - public Subtitle subtitleMatchType(SubtitleMatchType subtitleMatchType) { this.subtitleMatchType = subtitleMatchType; - return this; - } - - public Subtitle subtitleSource(SubtitleSource subtitleSource) { this.subtitleSource = subtitleSource; - return this; - } - - public Subtitle hearingImpaired(boolean hearingImpaired) { this.hearingImpaired = hearingImpaired; - return this; + this.quality = quality; + this.score = score; } - public Subtitle quality(String quality) { - this.quality = quality; - return this; + @EqualsAndHashCode + public static class DownloadSource { + @val SourceLocation sourceLocation; + @EqualsExclude @val ThrowingSupplier urlSupplier; + @val String url; + @val Path file; + + private DownloadSource( + SourceLocation sourceLocation, + ThrowingSupplier urlSupplier=null, + String url=null, + Path file=null) { + + this.urlSupplier = urlSupplier; + this.url = url; + this.file = file; + this.sourceLocation = sourceLocation; + } + + public static DownloadSource of( + ThrowingSupplier urlSupplier) { + return new DownloadSource(SourceLocation.URL_SUPPLIER, urlSupplier:urlSupplier); + } + + public static DownloadSource of(String url) { + return new DownloadSource(SourceLocation.URL, url:url); + } + + public static DownloadSource of(Path file) { + return new DownloadSource(SourceLocation.FILE, file:file); + } + + public String getValue() throws SubtitlesProviderException { + return switch (sourceLocation) { + case FILE -> file.toString(); + case URL -> url; + case URL_SUPPLIER -> urlSupplier.get(); + }; + } + + @Override public String toString() { + return "DownloadSource: " + sourceLocation + " " + switch (sourceLocation) { + case FILE -> file; + case URL -> url; + case URL_SUPPLIER -> ""; + }; + } } - public Subtitle score(int score) { - this.score = score; - return this; + public enum SourceLocation { + URL, URL_SUPPLIER, FILE } + @Override + public String toString() { + return "${getClass().getSimpleName()}: $fileName $quality"; + } } From 3baf729c8431df16e56f60e1a13b5ee380ab986a Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sun, 20 Apr 2025 17:58:15 +0200 Subject: [PATCH 36/58] use optional parameters (11) --- .../actions/SearchAction.java | 10 ++-- .../addic7ed/JAddic7edApi.java | 3 +- .../tvsubtitles/JTVSubtitlesApi.java | 3 +- .../workers/SearchManager.java | 45 ++--------------- .../lodder/subtools/sublibrary/Manager.java | 50 +++++-------------- pom.xml | 2 +- 6 files changed, 24 insertions(+), 89 deletions(-) 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 3c4edefa..9ebef8f9 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 @@ -82,15 +82,15 @@ private void search() throws ActionException { /* Create a new SearchManager. */ this.searchManager = - SearchManager.createWithSettings(this.settings) + new SearchManager(settings, /* Tell the manager which language we want */ - .language(language) + language, /* Tell the manager where to push progressUpdates */ - .progressListener(searchProgressListener) + searchProgressListener, /* Tell the manager how to handle user interactions */ - .userInteractionHandler(userInteractionHandler) + userInteractionHandler, /* Listen for when the manager tells us Subtitles are found */ - .onFound(this); + this); /* Tell the manager which providers to use */ this.subtitleProviderStore.getAllProviders().stream() 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 7a1fecb8..12f12a84 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 @@ -53,8 +53,7 @@ public JAddic7edApi(String username, String password, boolean speedy, Manager ma public void login(String username, String password) throws Addic7edException { try { - manager.postBuilder() - .url(DOMAIN + "/dologin.php") + manager.postBuilder("$DOMAIN/dologin.php") .addData("username", username) .addData("password", password) .addData("remember", "false") 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 8d45c6a2..5fb68e7f 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 @@ -38,8 +38,7 @@ public JTVSubtitlesApi(Manager manager) { public List getUrisForSerieName(String serieName) throws TvSubtitlesException { try { - return manager.postBuilder() - .url(DOMAIN + "/search.php") + return manager.postBuilder("$DOMAIN/search.php") .addData("qs", serieName) .postAsJsoupDocument() .select(".left_articles > ul > li a") 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 9fa1eb4a..379d2f68 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 @@ -10,9 +10,6 @@ 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; @@ -29,38 +26,6 @@ public class SearchManager implements Cancelable { - public interface SearchManagerLanguage { - SearchManagerProgressListener language(@NonNull Language language); - } - - public interface SearchManagerProgressListener { - SearchManagerUserInteractionHandler progressListener(@NonNull SearchProgressListener progressListener); - } - - public interface SearchManagerUserInteractionHandler { - SearchManagerOnFound userInteractionHandler(@NonNull UserInteractionHandler userInteractionHandler); - } - - public interface SearchManagerOnFound { - SearchManager onFound(@NonNull SearchHandler onFound); - } - - @Setter - @Accessors(fluent = true) - public static class SearchManagerBuilder - implements SearchManagerOnFound, SearchManagerUserInteractionHandler, SearchManagerProgressListener, - SearchManagerLanguage { - private Settings settings; - private Language language; - private SearchProgressListener progressListener; - private UserInteractionHandler userInteractionHandler; - - @Override - public SearchManager onFound(SearchHandler onFound) { - return new SearchManager(settings, onFound, language, progressListener, userInteractionHandler); - } - } - private final Map> queue = new HashMap<>(); private final Map workers = new HashMap<>(); private final Map scoreCalculators = new HashMap<>(); @@ -73,17 +38,13 @@ public SearchManager onFound(SearchHandler onFound) { private final SearchProgressListener progressListener; @val UserInteractionHandler userInteractionHandler; - public SearchManager(Settings settings, SearchHandler onFound, Language language, - SearchProgressListener progressListener, UserInteractionHandler userInteractionHandler) { + public SearchManager(Settings settings, Language language, SearchProgressListener progressListener, + UserInteractionHandler userInteractionHandler, SearchHandler onFound) { 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); + this.onFound = onFound; } public void addProvider(SubtitleProvider provider) { 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 258ba3ec..bea3619b 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -67,45 +67,23 @@ public void storeCookies(String domain, Map cookieMap) { // POST \\ // ==== \\ - public PostBuilderUrlIntf postBuilder() { - return new PostBuilder(httpClient); + public PostBuilder postBuilder(String url, String userAgent=null) { + return new PostBuilder(httpClient, url, userAgent); } - public interface PostBuilderUrlIntf { - PostBuilderUserAgentIntf url(String url); - } - - public interface PostBuilderUserAgentIntf extends PostBuilderDataMapIntf { - PostBuilderDataMapIntf userAgent(String userAgent); - } - - public interface PostBuilderDataMapIntf extends PostBuilderDataIntf { - PostBuilderPostIntf data(Map data); - } - - public interface PostBuilderDataIntf extends PostBuilderPostIntf { - PostBuilderDataIntf addData(String key, String value); - } - - public interface PostBuilderPostIntf { - String post() throws ManagerException; - - org.jsoup.nodes.Document postAsJsoupDocument() throws ManagerException; - } - - @Setter - @Accessors(chain = true, fluent = true) - @RequiredArgsConstructor - public static class PostBuilder - implements PostBuilderUrlIntf, PostBuilderUserAgentIntf, PostBuilderDataMapIntf, PostBuilderDataIntf, - PostBuilderPostIntf { - private final HttpClient httpClient; - private String url; - private String userAgent; + public static class PostBuilder { + @val HttpClient httpClient; + @val String url; + @val String userAgent; private Map data; - @Override - public PostBuilderDataIntf addData(String key, String value) { + public PostBuilder(HttpClient httpClient, String url, String userAgent=null) { + this.httpClient = httpClient; + this.url = url; + this.userAgent = userAgent; + } + + public PostBuilder addData(String key, String value) { if (data == null) { data = new HashMap<>(); } @@ -113,7 +91,6 @@ public PostBuilderDataIntf addData(String key, String value) { return this; } - @Override public String post() throws ManagerException { try { return httpClient.doPost(new URI(url).toURL(), userAgent, data == null ? new HashMap<>() : data); @@ -124,7 +101,6 @@ public String post() throws ManagerException { } } - @Override public org.jsoup.nodes.Document postAsJsoupDocument() throws ManagerException { return Jsoup.parse(post()); } diff --git a/pom.xml b/pom.xml index f0cb61c9..9484ba2d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ UTF-8 - 2025.1.10 + 2025.1.11 1.18.38 24 24 From ddfc4f3d4aaf3d7b1229f2cb404dbcb62bdb4b7f Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 23 Apr 2025 21:29:52 +0200 Subject: [PATCH 37/58] Gui fixes --- .../subtools/multisubdownloader/GUI.java | 4 +- .../gui/workers/DownloadWorker.java | 2 +- .../java/util/stream/Stream/StreamExt.java | 9 +++ .../interactivity/Prompter/PrompterExt.java | 2 +- .../UserInteractionHandlerGUI.java | 71 ++++++++++++------- 5 files changed, 58 insertions(+), 30 deletions(-) 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 edf2059f..007f7eca 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -382,7 +382,7 @@ private void showAbout() { text += " ($buildTimestamp)"; } JOptionPane.showConfirmDialog(this, text, ConfigProperties.getProperty(Property.NAME), - JOptionPane.CLOSED_OPTION); + JOptionPane.DEFAULT_OPTION); } protected void rename() { @@ -449,7 +449,7 @@ protected GUI self() { public void showErrorMessage(String message) { JOptionPane.showConfirmDialog(this, message, ConfigProperties.getProperty(Property.NAME), - JOptionPane.CLOSED_OPTION, JOptionPane.ERROR_MESSAGE); + JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE); } private void selectIncomingFolder() { 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 33864619..d07100dd 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 @@ -87,7 +87,7 @@ protected void process(List data) { } private void showErrorMessage(String message) { - JOptionPane.showConfirmDialog(null, message, "JBierSubDownloader", JOptionPane.CLOSED_OPTION, + JOptionPane.showConfirmDialog(null, message, "JBierSubDownloader", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE); } 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 index 34ca28c6..4abcf49b 100644 --- a/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java @@ -1,5 +1,6 @@ package extensions.java.util.stream.Stream; +import java.util.function.IntFunction; import java.util.stream.Stream; import lombok.experimental.UtilityClass; @@ -14,4 +15,12 @@ public class StreamExt { public static ThrowingStream asThrowingStream(@This Stream stream, Class exceptionType) { return ThrowingStream.of(stream, exceptionType); } + + public static T[] toTypedArray(@This Stream stream, IntFunction generator) { + return stream.toArray(generator); + } + + public static T[] toTypedArray(@This Stream stream) { + return (T[]) stream.toArray(Object[]::new); + } } diff --git a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java index 53815a45..02d2b922 100644 --- a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java +++ b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java @@ -229,7 +229,7 @@ public final void display(T... tableElements) { .map(tableElement -> columnDisplayers.stream() .map(columnDisplayer -> columnDisplayer.toStringMapper().apply(tableElement)) .toArray()) - .toArray(Object[][]::new); + .toTypedArray(Object[][]::new); TextTable tt = new TextTable(columnNames, dataTable); // this adds the numbering on the left 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 4558c953..38f95884 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 @@ -2,7 +2,6 @@ import javax.swing.*; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Function; @@ -10,6 +9,7 @@ import lombok.AllArgsConstructor; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.gui.InputPane; @@ -18,52 +18,62 @@ @AllArgsConstructor public class UserInteractionHandlerGUI implements UserInteractionHandler { + private static final Object LOCK = new Object(); @val @override UserInteractionSettingsIntf settings; @val JFrame frame; @Override public Optional selectFromList(Iterable options, String message, @Nullable String title, @Nullable 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, - optionsAsStrings[0]); - return selection == JOptionPane.CLOSED_OPTION ? Optional.empty() : options.stream().skip(selection).findFirst(); + synchronized (LOCK) { + ElementWrapper[] wrappedOptions = options.stream() + .map(option -> new ElementWrapper<>(option, toStringMapper == null ? String::valueOf : toStringMapper)) + .toTypedArray(); + return Optional.ofNullable( + (ElementWrapper) JOptionPane.showInputDialog(frame, message, title, JOptionPane.DEFAULT_OPTION, + null, wrappedOptions, wrappedOptions[0])) + .map(ElementWrapper::element); + } } @Override public boolean confirm(String message, String title) { - int choice = Integer.parseInt(JOptionPane.showInputDialog(frame, message, title, JOptionPane.YES_NO_OPTION)); - return choice == JOptionPane.YES_OPTION; + synchronized (LOCK) { + int choice = + Integer.parseInt(JOptionPane.showInputDialog(frame, message, title, JOptionPane.QUESTION_MESSAGE)); + return choice == JOptionPane.YES_OPTION; + } } @Override public Optional enter(String title, String message, @Nullable List> inputValidators) { - return new InputPane<>( - title:title, - message:message, - inputValidators:inputValidators, - toObjectMapper:Function.identity()) - .prompt(); + synchronized (LOCK) { + return new InputPane<>( + title:title, + message:message, + inputValidators:inputValidators, + toObjectMapper:Function.identity()) + .prompt(); + } } @Override public OptionalInt enterNumber(String title, String message, @Nullable List> objectValidators) { - - return new InputPane<>( - title:title, - message:message, - toObjectMapper:Integer::parseInt, - objectValidators:objectValidators) - .prompt().mapToInt(v -> v); + synchronized (LOCK) { + return new InputPane<>( + title:title, + message:message, + toObjectMapper:Integer::parseInt, + objectValidators:objectValidators) + .prompt().mapToInt(v -> v); + } } public void message(String message, String title) { - JOptionPane.showMessageDialog(frame, message, title, JOptionPane.OK_OPTION); + synchronized (LOCK) { + JOptionPane.showMessageDialog(frame, message, title, JOptionPane.INFORMATION_MESSAGE); + } } @Override @@ -73,6 +83,15 @@ public void showMessage(String message, String title, MessageSeverity messageSev case WARNING -> JOptionPane.WARNING_MESSAGE; case ERROR -> JOptionPane.ERROR_MESSAGE; }; - JOptionPane.showMessageDialog(frame, message, title, messageType); + synchronized (LOCK) { + JOptionPane.showMessageDialog(frame, message, title, messageType); + } + } + + private record ElementWrapper(T element, Function toStringMapper) { + @Override + public @NonNull String toString() { + return toStringMapper.apply(element); + } } } From 2d9311ba1040b093e1d27bde62e85f75ce937849 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 23 Apr 2025 22:00:17 +0200 Subject: [PATCH 38/58] fix generics for cache --- .../main/java/org/lodder/subtools/multisubdownloader/App.java | 4 ++-- .../src/main/java/org/lodder/subtools/sublibrary/Manager.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 3a2170c6..256cdce1 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java @@ -163,8 +163,8 @@ private static Manager createManager(boolean useGui) { .maxItems(2500) .build(); - InMemoryCache inMemoryCache = - InMemoryCache.builder().keyType(String.class).valueType(Serializable.class) + InMemoryCache inMemoryCache = + InMemoryCache.builder().keyType(String.class).valueType(String.class) .timeToLive(SECONDS.convert(10, MINUTES)) .timerInterval(100L) .maxItems(500) 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 bea3619b..fc2bfbef 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -48,8 +48,8 @@ public class Manager { @val HttpClient httpClient; - @val InMemoryCache inMemoryCache; - @val DiskCache diskCache; + @val InMemoryCache inMemoryCache; + @val DiskCache diskCache; public boolean store(String downloadLink, Path file) throws ManagerException { try { From 3cca2538ce3e08c02a9a5b29bafc547fafc4a809 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 23 Apr 2025 22:11:42 +0200 Subject: [PATCH 39/58] use optional parameters (12) --- .../UpdateAvailableGithub.java | 30 ++- .../addic7ed/JAddic7edApi.java | 3 +- .../opensubtitles/OpenSubtitlesApi.java | 21 +- .../podnapisi/JPodnapisiApi.java | 18 +- .../subscene/SubsceneApi.java | 18 +- .../tvsubtitles/JTVSubtitlesApi.java | 188 ++++++++-------- .../lodder/subtools/sublibrary/Manager.java | 203 +++++++----------- .../sublibrary/PageContentParams.java | 20 ++ .../lodder/subtools/sublibrary/data/Html.java | 5 - .../sublibrary/data/imdb/ImdbApi.java | 34 +-- .../sublibrary/data/imdb/ImdbSearchIdApi.java | 166 +++++++------- .../sublibrary/data/omdb/OmdbApi.java | 28 +-- .../sublibrary/data/tvdb/TheTvdbMirrors.java | 24 +-- 13 files changed, 357 insertions(+), 401 deletions(-) create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java 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 fd3aca81..56615ce2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java @@ -1,6 +1,7 @@ package org.lodder.subtools.multisubdownloader; import static java.time.temporal.ChronoUnit.*; +import static org.lodder.subtools.sublibrary.PageContentParams.*; import java.time.Instant; import java.time.LocalDate; @@ -21,6 +22,7 @@ import org.lodder.subtools.sublibrary.ConfigProperties.Property; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.Manager.ValueBuilderIsPresentIntf; +import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.cache.CacheType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,10 +75,11 @@ private Optional getUrlLatestNewStableGithubRelease() { .optionalSupplier(() -> { try { String currentVersion = getVersion(); - Element element = manager.getPageContentBuilder().url(REPO_URL + "/releases") - .userAgent(null) - .cacheType(CacheType.NONE) - .getAsJsoupDocument() + Element element = + manager.getAsJsoupDocument(PageContentParams.params( + url:"$REPO_URL/releases", + cacheType:CacheType.NONE, + userAgent:null)) .selectFirstByCss("#repo-content-turbo-frame .box a[href='$REPO_URI/releases/latest']"); Pattern versionPattern = Pattern.compile("\\d*\\.\\d\\.\\d"); String versionText = element.parent().selectFirstByTag("a").text(); @@ -87,10 +90,8 @@ private Optional getUrlLatestNewStableGithubRelease() { return Optional.empty(); } String versionBlockUrl = REPO_URL + "/releases/expanded_assets/" + versionText; - Element artifactElement = manager.getPageContentBuilder().url(versionBlockUrl) - .userAgent(null) - .cacheType(CacheType.NONE) - .getAsJsoupDocument() + Element artifactElement = manager.getAsJsoupDocument( + PageContentParams.params(url:versionBlockUrl, userAgent:null)) .selectFirstByCss(".Box-row a[href$='.jar']"); String url = DOMAIN + artifactElement.attr("href"); updateLastUpdateCheck(); @@ -115,10 +116,10 @@ private Optional getUrlLatestNewNightlyGithubRelease() { LocalDateTime buildTista = getBuildTista(); Element rowElement = - manager.getPageContentBuilder().url(REPO_URL + "/actions?query=branch%3Amaster") - .userAgent(null) - .cacheType(CacheType.MEMORY) - .getAsJsoupDocument() + manager.getAsJsoupDocument(PageContentParams.params( + url:"$REPO_URL/actions?query=branch%3Amaster", + cacheType:CacheType.MEMORY, + userAgent:null)) .selectFirstByCss("#partial-actions-workflow-runs .Box-row"); LocalDateTime nightlyBuildTista = zonedDateTimeStringToLocalDateTime( rowElement.selectFirstByCss(".d-inline relative-time").attr("datetime")); @@ -127,10 +128,7 @@ private Optional getUrlLatestNewNightlyGithubRelease() { } String url = "https://nightly.link" + rowElement.selectFirstByCss(".Link--primary").attr("href"); - String downloadUrl = manager.getPageContentBuilder() - .url(url) - .cacheType(CacheType.MEMORY) - .getAsJsoupDocument() + String downloadUrl = manager.getAsJsoupDocument(params(url, CacheType.MEMORY)) .selectFirstByCss("table td a") .attr("href"); updateLastUpdateCheck(); 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 12f12a84..fd9588c3 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 @@ -23,6 +23,7 @@ import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.ManagerException; +import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.Html; import org.lodder.subtools.sublibrary.data.ProviderSerieId; @@ -206,7 +207,7 @@ private Document getContent(String url) throws Addic7edException { } lastRequest = LocalDateTime.now(); } - return this.getHtml(url).cacheType(CacheType.NONE).getAsJsoupDocument(); + return manager.getAsJsoupDocument(PageContentParams.params(url:url, userAgent:"")); } catch (Exception e) { throw new Addic7edException(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 f32be572..e5995d9c 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 @@ -10,6 +10,8 @@ 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.Manager.Retry; +import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.util.http.HttpClientException; @@ -72,15 +74,16 @@ public DownloadSubtitle downloadSubtitle() { public List getProviderSerieIds(String serieName) throws OpenSubtitlesException { try { - 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(exc -> exc instanceof HttpClientException e && e.responseCode == 429) - .retryWait(5) - .getAsJsonArray() + return manager.getAsJsonArray(PageContentParams.params( + url:"https://www.opensubtitles.org/libs/suggest.php?format=json3&MovieName=" + + URLEncoder.encode(serieName.toLowerCase(), StandardCharsets.UTF_8), + cacheType:CacheType.MEMORY, + userAgent:"", + retry:new Retry( + 1, + exc -> exc instanceof HttpClientException e && e.responseCode == 429, + 5) + )) .streamJsonObjects() .filter(show -> "tv".equals(show.getString("kind"))) .map(show -> new OpensubtitleSerieId(show.getString("name"), show.getInt("id"), 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 a148e83a..d1e7aea5 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi; +import static org.lodder.subtools.sublibrary.PageContentParams.*; + import java.io.Serial; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -23,6 +25,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.model.PodnapisiSubtitleDescriptor; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; +import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; @@ -97,15 +100,12 @@ private List getSubtitles(SerieMapping providerSeri protected @Nullable Document getXml(String url) throws PodnapisiException { try { - return manager.getPageContentBuilder() - .url(url) - .userAgent(userAgent) - .cacheType(CacheType.MEMORY) - .retries(1) - .retryPredicate(e -> e instanceof HttpClientException httpClientException && - httpClientException.responseCode >= 500 && httpClientException.responseCode < 600) - .retryWait(5) - .getAsJsoupDocument(); + return manager.getAsJsoupDocument(params(url, CacheType.MEMORY, userAgent, + new Retry( + 1, + ex -> ex instanceof HttpClientException e && e.responseCode >= 500 && + e.responseCode < 600, + 5))); } catch (Exception e) { throw new PodnapisiException(e); } 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 54acacf3..d0492152 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 @@ -27,8 +27,9 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.model.SubsceneSubtitleDescriptor; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.Manager.PageContentBuilderCacheTypeIntf; +import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.ManagerException; +import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.data.Html; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; @@ -140,20 +141,15 @@ private Document getJsoupDocument(String url) throws ManagerException { while (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATE_DURATION_SHORT) { sleepSeconds(1); } - Document document = super.getHtml(url) - .retries(1) - .retryPredicate(RETRY_PREDICATE) - .retryWait(RATE_DURATION_LONG) - .getAsJsoupDocument(); + Document document = + manager.getAsJsoupDocument(PageContentParams.params( + url:url, + userAgent:"", + retry:new Retry(1, RETRY_PREDICATE, RATE_DURATION_LONG))); lastRequest = LocalDateTime.now(); return document; } - @Override - public PageContentBuilderCacheTypeIntf getHtml(String url) { - throw new IllegalStateException("Should not be used, use getJsoupDocument"); - } - private void setLanguageWithCookie(Language language) { int languageId = SUBSCENE_LANGS.get(language); if (selectedLanguage != languageId) { 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 5fb68e7f..3f2cc550 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 @@ -20,7 +20,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.model.TVsubtitlesSubtitleDescriptor; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.cache.CacheType; +import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.data.Html; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; @@ -39,118 +39,120 @@ public JTVSubtitlesApi(Manager manager) { public List getUrisForSerieName(String serieName) throws TvSubtitlesException { try { return manager.postBuilder("$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"), "/"))) - .toList(); + .addData("qs", serieName) + .postAsJsoupDocument() + .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 { + Language language) throws TvSubtitlesException { return getEpisodeUrl(SERIE_URL_PREFIX + providerSerieId.providerId, season, episode).mapThrowing( - (String episodeUrl) -> getSubtitles(episodeUrl, language)).orElseGet(Set::of); + (String episodeUrl) -> getSubtitles(episodeUrl, language)).orElseGet(Set::of); } private Set getSubtitles(String episodeUrl, Language language) - throws TvSubtitlesException { + throws TvSubtitlesException { return manager.valueBuilder() - .memoryCache() - .key("%s-subtitles-%s-%s".formatted(subtitleSource.name(), episodeUrl, language)) - .collectionSupplier(TVsubtitlesSubtitleDescriptor.class, () -> { - Set lSubtitles = new HashSet<>(); - try { - 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-")) { + .memoryCache() + .key("%s-subtitles-%s-%s".formatted(subtitleSource.name(), episodeUrl, language)) + .collectionSupplier(TVsubtitlesSubtitleDescriptor.class, () -> { + Set lSubtitles = new HashSet<>(); + try { + Elements searchEpisodes = + manager.getAsJsoupDocument(PageContentParams.params( + url:episodeUrl.replace(".html", "-" + language.langCode + ".html"), + userAgent:"")) + .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-")) { + continue; + } + Document subtitlePageDoc = manager.getAsJsoupDocument( + PageContentParams.params(url:DOMAIN + url, userAgent:"")); + String filename = null; + String rip = null; + String title = null; + String author = null; + Elements subtitlePageTableDoc = subtitlePageDoc.selectAllByClass("subtitle1"); + if (subtitlePageTableDoc.size() != 1) { + continue; + } + for (Element item : subtitlePageTableDoc.getFirst().selectAllByTag("tr")) { + Elements row = item.getElementsByTag("td"); + if (row.size() != 3) { 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.size() != 1) { - 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); } - for (Element item : subtitlePageTableDoc.getFirst().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; - } + 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; } } - return lSubtitles; - } catch (Exception e) { - throw new TvSubtitlesException(e); } - }) - .getCollection(); + return lSubtitles; + } catch (Exception e) { + throw new TvSubtitlesException(e); + } + }) + .getCollection(); } private Optional getEpisodeUrl(String showUrl, int season, int episode) throws TvSubtitlesException { return manager.valueBuilder() - .memoryCache() - .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")) - .getAsJsoupDocument() - .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.selectNthByTag("td", 2).selectFirstByTag("a").attr("href")) - .findAny(); - } catch (Exception e) { - throw new TvSubtitlesException(e); - } - }) - .getOptional(); + .memoryCache() + .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 manager.getAsJsoupDocument(PageContentParams.params( + url:showUrl.replace(".html", "-$season.html"), + userAgent:"")) + .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.selectNthByTag("td", 2).selectFirstByTag("a").attr("href")) + .findAny(); + } catch (Exception e) { + throw new TvSubtitlesException(e); + } + }) + .getOptional(); } } 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 fc2bfbef..da793b3c 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -35,6 +35,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.jsoup.Jsoup; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.cache.Cache; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.cache.DiskCache; @@ -111,164 +112,108 @@ public org.jsoup.nodes.Document postAsJsoupDocument() throws ManagerException { // GET PAGE CONTENT \\ // ================ \\ - public PageContentBuilderUrlIntf getPageContentBuilder() { - return new PageContentBuilder(httpClient, inMemoryCache); + public String get(PageContentParams params) throws ManagerException { + ThrowingSupplier supplier = () -> getContentWithoutCache(params); + return switch (params.cacheType) { + case NONE -> getContentWithoutCache(params); + case MEMORY -> inMemoryCache.getOrPut(params.url, supplier); + case DISK -> throw new IllegalArgumentException("Unexpected value: " + params.cacheType); + }; } - public interface PageContentBuilderUrlIntf { - PageContentBuilderUserAgentIntf url(String url); + public InputStream getAsInputStream(PageContentParams params) throws ManagerException { + return get(params).toInputStream(StandardCharsets.UTF_8); } - public interface PageContentBuilderUserAgentIntf extends PageContentBuilderCacheTypeIntf { - PageContentBuilderCacheTypeIntf userAgent(String userAgent); + public @Nullable Document getAsDocument(PageContentParams params, Predicate emptyResultPredicate=null) + throws ParserConfigurationException, ManagerException, IOException { + Optional asStringDocument = getAsStringDocument(params, emptyResultPredicate); + return asStringDocument.isPresent() ? XMLHelper.getDocument(asStringDocument.get()) : null; } - public interface PageContentBuilderCacheTypeIntf extends PageContentBuilderRetryIntf { - PageContentBuilderRetryIntf cacheType(CacheType cacheType); + public org.jsoup.nodes.Document getAsJsoupDocument(PageContentParams params, + Predicate emptyResultPredicate=null) throws ManagerException { + return getAsStringDocument(params, emptyResultPredicate).map(Jsoup::parse).orElse(null); } - public interface PageContentBuilderRetryIntf extends PageContentBuilderGetIntf { - PageContentBuilderRetryConditionIntf retries(int retries); + private Optional getAsStringDocument(PageContentParams params, + Predicate emptyResultPredicate) throws ManagerException { + if (emptyResultPredicate == null) { + return Optional.of(get(params)); + } + String html = get(params); + return StringUtils.isBlank(html) || emptyResultPredicate.test(html) ? Optional.empty() : Optional.of(html); } - public interface PageContentBuilderRetryConditionIntf { - PageContentBuilderRetryWaitIntf retryPredicate(Predicate retryPredicate); + public JSONObject getAsJsonObject(PageContentParams params) throws ManagerException { + return new JSONObject(getAsJsonString(params)); } - public interface PageContentBuilderRetryWaitIntf { - PageContentBuilderGetIntf retryWait(int retryWait); + public JSONArray getAsJsonArray(PageContentParams params) throws ManagerException { + return new JSONArray(getAsJsonString(params)); } - public interface PageContentBuilderGetIntf { - String get() throws ManagerException; - - InputStream getAsInputStream() throws ManagerException; - - Document getAsDocument() throws ParserConfigurationException, ManagerException, IOException; - - Document getAsDocument(Predicate emptyResultPredicate) - throws ParserConfigurationException, ManagerException, IOException; - - org.jsoup.nodes.Document getAsJsoupDocument() throws ManagerException; - - org.jsoup.nodes.Document getAsJsoupDocument(Predicate emptyResultPredicate) throws ManagerException; - - JSONObject getAsJsonObject() throws ManagerException; - - JSONArray getAsJsonArray() throws ManagerException; + private String getAsJsonString(PageContentParams params) throws ManagerException { + try { + return new String(getAsInputStream(params).readAllBytes(), StandardCharsets.UTF_8); + } catch (JSONException | IOException | ManagerException e) { + throw new ManagerException(e); + } } - @Setter - @Accessors(chain = true, fluent = true) - @RequiredArgsConstructor - public static class PageContentBuilder - implements PageContentBuilderGetIntf, PageContentBuilderCacheTypeIntf, PageContentBuilderUserAgentIntf, - PageContentBuilderUrlIntf, PageContentBuilderRetryIntf, PageContentBuilderRetryConditionIntf, - PageContentBuilderRetryWaitIntf { - private final HttpClient httpClient; - private final InMemoryCache inMemoryCache; - private String url; - private String userAgent = "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"; - private CacheType cacheType; - private int retries; - private Predicate retryPredicate; - private int retryWait; - - @Override - public PageContentBuilder retries(int retries) { - if (retries < 0) { - throw new IllegalStateException("Number of retries cannot be less than 0"); + private String getContentWithoutCache(PageContentParams params) throws ManagerException { + String url = params.url; + String userAgent = params.userAgent; + Retry retry = params.retry; + try { + return httpClient.doGet(new URI(url).toURL(), userAgent); + } catch (HttpClientException e) { + if (retry.canRetry() && retry.predicate.test(e)) { + retry.decreaseRetries().sleep(); + return getContentWithoutCache(params); } - this.retries = retries; - return this; - } - - @Override - public String get() throws ManagerException { - if (cacheType == null) { - cacheType = CacheType.NONE; + throw new ManagerException( + "Error occurred with httpclient response: %s %s".formatted(e.responseCode, e.responseMessage), e); + } catch (IOException e) { + if (retry.canRetry() && retry.predicate.test(e)) { + retry.decreaseRetries(); + return getContentWithoutCache(params); } - return switch (cacheType) { - case NONE -> getContentWithoutCache(url, userAgent); - case MEMORY -> inMemoryCache.getOrPut(url, () -> getContentWithoutCache(url, userAgent)); - case DISK -> throw new IllegalArgumentException("Unexpected value: " + cacheType); - }; - } - - @Override - public InputStream getAsInputStream() throws ManagerException { - return get().toInputStream(StandardCharsets.UTF_8); - } - - @Override - public Document getAsDocument() throws ParserConfigurationException, ManagerException, IOException { - return XMLHelper.getDocument(get()); + throw new ManagerException(e); + } catch (URISyntaxException e) { + throw new ManagerException("Invalid url [%s]".formatted(url), e); } + } - @Override - public Document getAsDocument(Predicate emptyResultPredicate) - throws ParserConfigurationException, ManagerException, IOException { - String html = get(); - return StringUtils.isBlank(html) || (emptyResultPredicate != null && emptyResultPredicate.test(html)) ? - null : XMLHelper.getDocument(html); - } + public record Retry(int retries, Predicate predicate, int waitSeconds) { - @Override - public org.jsoup.nodes.Document getAsJsoupDocument() throws ManagerException { - return Jsoup.parse(get()); - } + public static final Retry DEFAULT = new Retry(0, null, 0); - @Override - public org.jsoup.nodes.Document getAsJsoupDocument(Predicate emptyResultPredicate) - throws ManagerException { - String html = get(); - return StringUtils.isBlank(html) || (emptyResultPredicate != null && emptyResultPredicate.test(html)) ? - null : Jsoup.parse(html); + public Retry { + if (retries < 0) { + throw new IllegalStateException("Number of retries cannot be less than 0"); + } } - @Override - public JSONObject getAsJsonObject() throws ManagerException { - try { - return new JSONObject(new String(getAsInputStream().readAllBytes(), StandardCharsets.UTF_8)); - } catch (JSONException | IOException | ManagerException e) { - throw new ManagerException(e); - } + public Retry decreaseRetries() { + return new Retry(retries - 1, predicate, waitSeconds); } - @Override - public JSONArray getAsJsonArray() throws ManagerException { - try { - return new JSONArray(new String(getAsInputStream().readAllBytes(), StandardCharsets.UTF_8)); - } catch (JSONException | IOException | ManagerException e) { - throw new ManagerException(e); - } + public boolean canRetry() { + return retries > 0; } - private String getContentWithoutCache(String urlString, String userAgent) throws ManagerException { + public Retry sleep() { try { - return httpClient.doGet(new URI(urlString).toURL(), userAgent); - } catch (HttpClientException e) { - if (retries-- > 0 && retryPredicate.test(e)) { - try { - Thread.sleep(retryWait * 1000L); - } catch (InterruptedException e1) { - // continue - } - return getContentWithoutCache(urlString, userAgent); - } - throw new ManagerException( - "Error occurred with httpclient response: %s %s".formatted(e.responseCode, e.responseMessage), e); - } catch (IOException e) { - if (retries-- > 0 && retryPredicate.test(e)) { - return getContentWithoutCache(urlString, userAgent); - } - throw new ManagerException(e); - } catch (URISyntaxException e) { - throw new ManagerException("Invalid url [%s]".formatted(urlString), e); + Thread.sleep(waitSeconds * 1000L); + } catch (InterruptedException e1) { + // continue } + return this; } } + // =========== \\ // CLEAR CACHE \\ // =========== \\ @@ -284,10 +229,10 @@ public interface ClearExpiredCacheBuilderCacheTypeIntf { public interface ClearExpiredCacheBuilderKeyFilterIntf extends ClearExpiredCacheBuilderClearIntf { - ClearExpiredCacheBuilderClearIntf keyFilter(Predicate keyFilter); + ClearExpiredCacheBuilderClearIntf keyFilter(Predicate keyFilter); } - public interface ClearExpiredCacheBuilderClearIntf { + public interface ClearExpiredCacheBuilderClearIntf { void clear(); } @@ -295,7 +240,7 @@ public interface ClearExpiredCacheBuilderClearIntf { @Setter @Accessors(chain = true, fluent = true) @RequiredArgsConstructor - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) public static class ClearExpiredCacheBuilder implements ClearExpiredCacheBuilderCacheTypeIntf, ClearExpiredCacheBuilderKeyFilterIntf, ClearExpiredCacheBuilderClearIntf { @@ -474,7 +419,7 @@ public interface ValueBuilderStoreIntf { @Setter @Accessors(chain = true, fluent = true) @RequiredArgsConstructor - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) public static class ValueBuilder, T, X extends Exception> implements ValueBuilderGetOptionalIntf, ValueBuilderCacheTypeIntf, ValueBuilderValueSupplierIntf, ValueBuilderKeyIntf, ValueBuilderGetCollectionIntf, ValueBuilderGetOptionalIntIntf, diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java new file mode 100644 index 00000000..e0ae96dd --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java @@ -0,0 +1,20 @@ +package org.lodder.subtools.sublibrary; + +import org.lodder.subtools.sublibrary.cache.CacheType; + +public record PageContentParams(String url, + CacheType cacheType, + String userAgent, + Manager.Retry retry) { + + public static PageContentParams params(String url, + CacheType cacheType=CacheType.NONE, + String userAgent="Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)", + Manager.Retry retry=Manager.Retry.DEFAULT) { + return new PageContentParams(url, cacheType, userAgent, retry); + } + + public static PageContentParams url(String url) { + return params(url); + } +} 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 c5eadda7..8ef0f5b0 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 @@ -6,7 +6,6 @@ import lombok.AllArgsConstructor; import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.Manager.PageContentBuilderCacheTypeIntf; @AllArgsConstructor public class Html { @@ -22,10 +21,6 @@ public void storeCookies(String domain, Map cookieMap) { manager.storeCookies(domain, cookieMap); } - public PageContentBuilderCacheTypeIntf getHtml(String url) { - return manager.getPageContentBuilder().url(url).userAgent(userAgent); - } - public void sleepSeconds(long seconds) { try { TimeUnit.SECONDS.sleep(seconds); 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 53f29bcc..01d95c9c 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.sublibrary.data.imdb; +import static org.lodder.subtools.sublibrary.PageContentParams.*; + import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -15,22 +17,20 @@ public class ImdbApi { public Optional getMovieDetails(int imdbId) throws ImdbException { return manager.valueBuilder() - .memoryCache() - .key("IMDB-moviedetails-$imdbId") - .optionalSupplier(() -> { - final String url = "$DOMAIN/title/tt${%07d/releaseinfo".formatted(imdbId); - try { - org.jsoup.nodes.Element element = manager.getPageContentBuilder() - .url(url) - .getAsJsoupDocument() - .selectFirstByCss(".article .subpage_title_block .subpage_title_block__right-column"); - String imdbName = element.selectFirstByCss("a[itemprop='url']").text(); - int year = Integer.parseInt( - element.selectFirstByCss("span.nobr").text().replaceAll("[^0-9]", "")); - return Optional.of(new ImdbDetails(imdbName, year)); - } catch (Exception e) { - throw new ImdbException("Error IMDB API", url, e); - } - }).getOptional(); + .memoryCache() + .key("IMDB-moviedetails-$imdbId") + .optionalSupplier(() -> { + final String url = "$DOMAIN/title/tt${%07d/releaseinfo".formatted(imdbId); + try { + org.jsoup.nodes.Element element = manager.getAsJsoupDocument(url(url)) + .selectFirstByCss(".article .subpage_title_block .subpage_title_block__right-column"); + String imdbName = element.selectFirstByCss("a[itemprop='url']").text(); + int year = Integer.parseInt( + element.selectFirstByCss("span.nobr").text().replaceAll("[^0-9]", "")); + return Optional.of(new ImdbDetails(imdbName, year)); + } catch (Exception 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 30925681..6d184410 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.sublibrary.data.imdb; +import static org.lodder.subtools.sublibrary.PageContentParams.*; + import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -23,105 +25,99 @@ record ImdbSearchIdApi(Manager manager) { public Set getImdbIdOnImdb(String title, Integer year) throws ImdbSearchIdException { return manager.valueBuilder() - .memoryCache() - .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) { - sb.append("+%28").append(year).append("%29"); - } - String url = sb.toString(); - try { - Elements searchResults = manager.getPageContentBuilder() - .url(url) - .getAsJsoupDocument() - .selectAllByCss("#main .findList .findResult .result_text"); - return getImdbIdCommon(searchResults, - e -> e.selectFirstByTag("a").text() + " " + e.text(), - e -> e.selectFirst("a").attr("href")); - } catch (Exception e) { - throw new ImdbSearchIdException("Error getImdbIdOnImdb", url, e); - } - }) - .getCollection(); + .memoryCache() + .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) { + sb.append("+%28").append(year).append("%29"); + } + String url = sb.toString(); + try { + Elements searchResults = manager.getAsJsoupDocument(url(url)) + .selectAllByCss("#main .findList .findResult .result_text"); + return getImdbIdCommon(searchResults, + e -> e.selectFirstByTag("a").text() + " " + e.text(), + e -> e.selectFirst("a").attr("href")); + } catch (Exception e) { + throw new ImdbSearchIdException("Error getImdbIdOnImdb", url, e); + } + }) + .getCollection(); } public Set getImdbIdOnYahoo(String title, Integer year) throws ImdbSearchIdException { return manager.valueBuilder() - .memoryCache() - .key("IMDB-imdbid-yahoo-$title-$year") - .collectionSupplier(ProviderSerieId.class, () -> { - 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"); - } + .memoryCache() + .key("IMDB-imdbid-yahoo-$title-$year") + .collectionSupplier(ProviderSerieId.class, () -> { + 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"); + } - sb.append("+site%3Aimdb.com&fr=yfp-t-501&ei=UTF-8&rd=r1"); - String url = sb.toString(); + sb.append("+site%3Aimdb.com&fr=yfp-t-501&ei=UTF-8&rd=r1"); + String url = sb.toString(); - try { - Elements searchResults = manager.getPageContentBuilder() - .url(url) - .getAsJsoupDocument() - .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(); + try { + Elements searchResults = manager.getAsJsoupDocument(url(url)) + .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(); } public Set getImdbIdOnGoogle(String title, Integer year) throws ImdbSearchIdException { return manager.valueBuilder() - .memoryCache() - .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)); - if (year != null) { - sb.append("+%28").append(year).append("%29"); - } - sb.append("+site%3Awww.imdb.com&meta="); - String url = sb.toString(); - try { - Elements searchResults = manager.getPageContentBuilder() - .url(url) - .getAsJsoupDocument() - .selectAllByCss("a[href*='https://www.imdb.com/title/tt']"); - Function toStringMapper = - e -> e.selectFirstByTag("span").text().replace(" - IMDb", ""); - Function toHrefMapper = e -> e.attr("href"); - return getImdbIdCommon(searchResults, toStringMapper, toHrefMapper); - } catch (Exception e) { - throw new ImdbSearchIdException("Error getImdbIdOnGoogle", url, e); - } - }) - .getCollection(); + .memoryCache() + .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)); + if (year != null) { + sb.append("+%28").append(year).append("%29"); + } + sb.append("+site%3Awww.imdb.com&meta="); + String url = sb.toString(); + try { + Elements searchResults = manager.getAsJsoupDocument(url(url)) + .selectAllByCss("a[href*='https://www.imdb.com/title/tt']"); + Function toStringMapper = + e -> e.selectFirstByTag("span").text().replace(" - IMDb", ""); + Function toHrefMapper = e -> e.attr("href"); + return getImdbIdCommon(searchResults, toStringMapper, toHrefMapper); + } catch (Exception e) { + throw new ImdbSearchIdException("Error getImdbIdOnGoogle", url, e); + } + }) + .getCollection(); } private Set getImdbIdCommon(Elements searchResults, Function toStringMapper, - Function toHrefMapper) { + Function toHrefMapper) { 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", ""))); - } - })); + (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/omdb/OmdbApi.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/omdb/OmdbApi.java index 34865012..0ee65a40 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.sublibrary.data.omdb; +import static org.lodder.subtools.sublibrary.PageContentParams.*; + import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -16,20 +18,18 @@ class OmdbApi { public Optional getMovieDetails(int imdbId) throws OmdbException { return manager.valueBuilder() - .memoryCache() - .key("OMDB-moviedetails-$imdbId") - .optionalSupplier(() -> { - final String url = "$DOMAIN/?i=tt$%07d&plot=short&r=xml".formatted(imdbId); - try { - return manager.getPageContentBuilder() - .url(url) - .getAsDocument() - .getElementsByTagName("movie").stream() - .map(this::parseOMDBDetails).findFirst(); - } catch (Exception e) { - throw new OmdbException("Error OMDB API", url, e); - } - }).getOptional(); + .memoryCache() + .key("OMDB-moviedetails-$imdbId") + .optionalSupplier(() -> { + final String url = "$DOMAIN/?i=tt$%07d&plot=short&r=xml".formatted(imdbId); + try { + return manager.getAsDocument(url(url)) + .getElementsByTagName("movie").stream() + .map(this::parseOMDBDetails).findFirst(); + } catch (Exception e) { + throw new OmdbException("Error OMDB API", url, e); + } + }).getOptional(); } private OmdbDetails parseOMDBDetails(Node node) { 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 f44f5fd9..daf89ee8 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,5 +1,7 @@ package org.lodder.subtools.sublibrary.data.tvdb; +import static org.lodder.subtools.sublibrary.PageContentParams.*; + import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.util.ArrayList; @@ -14,7 +16,7 @@ /** * @author lodder - * Source + * Source */ public class TheTvdbMirrors { @@ -33,19 +35,17 @@ public class TheTvdbMirrors { private final List zipList = new ArrayList<>(); public TheTvdbMirrors(String apikey, Manager manager) throws ManagerException, ParserConfigurationException, - IOException { + IOException { synchronized (this) { - manager.getPageContentBuilder() - .url("http://www.thetvdb.com/api/" + apikey + "/mirrors.xml") - .getAsDocument() + manager.getAsDocument(url("http://www.thetvdb.com/api/$apikey/mirrors.xml")) .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); - }); + .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); + }); } } From f207f0cfd27f057f3c7e2b88d6e0734f4c004ac5 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 23 Apr 2025 22:22:37 +0200 Subject: [PATCH 40/58] use optional parameters (13) --- .../subtools/multisubdownloader/App.java | 5 +- .../subtitleproviders/SubtitleProvider.java | 5 +- .../multisubdownloader/util/ExportImport.java | 7 ++- .../lodder/subtools/sublibrary/Manager.java | 49 ++----------------- 4 files changed, 10 insertions(+), 56 deletions(-) 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 256cdce1..3d46f420 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java @@ -118,10 +118,7 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu app.makeSubtitleProviderStore().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)) - .clear(); + manager.clearExpiredCache(CacheType.DISK, key -> providerNames.stream().noneMatch(key::startsWith)); }).start(); } 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 529c342c..deb1680f 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 @@ -53,10 +53,7 @@ default Set search(Release release, Language language) { } default void clearCache() { - manager.clearExpiredCacheBuilder() - .cacheType(CacheType.DISK) - .keyFilter((String k) -> k.startsWith(providerName + "-")) - .clear(); + manager.clearExpiredCache(CacheType.DISK, k -> k.startsWith(providerName + "-")); } Optional getProviderSerieId(TvRelease tvRelease) throws X; 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 242c69ac..6160551a 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 @@ -149,10 +149,9 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand MappingType.values().stream() .map(MappingType::getSelectionForKeyPrefixList) .flatMap(Arrays::stream) - .forEach(selectionForKeyPrefix -> manager.clearExpiredCacheBuilder() - .cacheType(CacheType.DISK) - .keyFilter((String k) -> k.startsWith(selectionForKeyPrefix.keyPrefix)) - .clear()); + .forEach(selectionForKeyPrefix -> + manager.clearExpiredCache(CacheType.DISK, + k -> k.startsWith(selectionForKeyPrefix.keyPrefix))); } serieMappings.forEach(serieMapping -> manager.valueBuilder() .cacheType(CacheType.DISK) 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 da793b3c..29d0eae4 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -218,50 +218,11 @@ public Retry sleep() { // CLEAR CACHE \\ // =========== \\ - public ClearExpiredCacheBuilderCacheTypeIntf clearExpiredCacheBuilder() { - return new ClearExpiredCacheBuilder<>(inMemoryCache, diskCache); - } - - public interface ClearExpiredCacheBuilderCacheTypeIntf { - - ClearExpiredCacheBuilderKeyFilterIntf cacheType(CacheType cacheType); - } - - public interface ClearExpiredCacheBuilderKeyFilterIntf extends ClearExpiredCacheBuilderClearIntf { - - ClearExpiredCacheBuilderClearIntf keyFilter(Predicate keyFilter); - } - - public interface ClearExpiredCacheBuilderClearIntf { - - void clear(); - } - - @Setter - @Accessors(chain = true, fluent = true) - @RequiredArgsConstructor - @SuppressWarnings({"rawtypes", "unchecked"}) - public static class ClearExpiredCacheBuilder - implements ClearExpiredCacheBuilderCacheTypeIntf, ClearExpiredCacheBuilderKeyFilterIntf, - ClearExpiredCacheBuilderClearIntf { - private final InMemoryCache inMemoryCache; - private final DiskCache diskCache; - private CacheType cacheType; - private Predicate keyFilter; - - @Override - public ClearExpiredCacheBuilder keyFilter(Predicate keyFilter) { - this.keyFilter = (Predicate) keyFilter; - return (ClearExpiredCacheBuilder) this; - } - - @Override - public void clear() { - switch (cacheType) { - case MEMORY -> inMemoryCache.cleanup(keyFilter); - case DISK -> diskCache.cleanup(keyFilter); - default -> throw new IllegalArgumentException("Unexpected value: " + cacheType); - } + public void clearExpiredCache(CacheType cacheType, Predicate keyFilter=null) { + switch (cacheType) { + case MEMORY -> inMemoryCache.cleanup(keyFilter); + case DISK -> diskCache.cleanup(keyFilter); + default -> throw new IllegalArgumentException("Unexpected value: " + cacheType); } } From c655628b5e7d3252a5a3612e7f169ca09bf98af1 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 26 Apr 2025 16:31:12 +0200 Subject: [PATCH 41/58] use optional parameters (14) + use manifold science (temp) --- MultiSubDownloader/pom.xml | 4 + .../AbstractButton/AbstractButtonExt.java | 2 +- .../javax/swing/JCheckBox/JCheckBoxExt.java | 2 +- .../subtools/multisubdownloader/App.java | 28 +- .../subtools/multisubdownloader/GUI.java | 2 +- .../UpdateAvailableGithub.java | 28 +- .../gui/dialog/MappingEpisodeNameDialog.java | 25 +- .../jtextfield/MyPasswordField.java | 8 +- .../jtextfield/MyPasswordFieldOthersIntf.java | 2 +- .../jtextfield/MyTextFieldCommon.java | 8 +- .../jtextfield/MyTextFieldOthersIntf.java | 2 +- .../Addic7edServiceProvider.java | 17 +- .../OpenSubtitlesServiceProvider.java | 15 +- .../settings/SettingValue.java | 2 +- .../settings/SettingsControl.java | 63 +- .../subtitleproviders/SubtitleProvider.java | 2 +- .../adapters/AbstractAdapter.java | 9 +- .../subtitleproviders/adapters/Adapter.java | 95 +-- .../adapters/JAddic7edAdapter.java | 6 +- .../adapters/JOpenSubAdapter.java | 10 +- .../addic7ed/JAddic7edApi.java | 73 +- .../model/Addic7edSubtitleDescriptor.java | 4 +- .../gestdown/JAddic7edProxyGestdownApi.java | 17 +- .../opensubtitles/OpenSubtitlesApi.java | 19 +- .../opensubtitles/OpenSubtitlesExecuter.java | 9 +- .../opensubtitles/SearchSubtitles.java | 9 +- .../podnapisi/JPodnapisiApi.java | 12 +- .../subscene/SubsceneApi.java | 31 +- .../tvsubtitles/JTVSubtitlesApi.java | 25 +- .../multisubdownloader/util/ExportImport.java | 19 +- SubLibrary/pom.xml | 4 + .../java/util/Optional/OptionalExt.java | 7 + .../science/measures/Time/TimeExt.java | 26 + .../interactivity/Prompter/PrompterExt.java | 3 +- .../subtools/sublibrary/Credentials.java | 4 + .../lodder/subtools/sublibrary/Manager.java | 783 +++++------------- .../sublibrary/PageContentParams.java | 2 +- .../subtools/sublibrary/cache/Cache.java | 53 +- .../sublibrary/cache/CacheObject.java | 10 +- .../subtools/sublibrary/cache/DiskCache.java | 72 +- .../sublibrary/cache/ExpiringCacheObject.java | 26 +- .../ExpiringSerializableCacheObject.java | 6 +- .../sublibrary/cache/InMemoryCache.java | 80 +- .../cache/SerializableDiskCache.java | 110 --- .../cache/TemporaryCacheObject.java | 24 +- .../TemporarySerializableCacheObject.java | 6 +- .../sublibrary/cache/TypedDiskCache.java | 132 --- .../lodder/subtools/sublibrary/data/Html.java | 32 - .../sublibrary/data/ReleaseDBIntf.java | 4 +- .../sublibrary/data/imdb/ImdbAdapter.java | 23 +- .../sublibrary/data/imdb/ImdbApi.java | 9 +- .../sublibrary/data/imdb/ImdbSearchIdApi.java | 29 +- .../data/imdb/model/ImdbDetails.java | 4 +- .../sublibrary/data/omdb/OmdbAdapter.java | 11 +- .../sublibrary/data/omdb/OmdbApi.java | 9 +- .../data/omdb/model/OmdbDetails.java | 3 +- .../sublibrary/data/tvdb/TheTvdbAdapter.java | 64 +- .../sublibrary/data/tvdb/TheTvdbApi.java | 89 +- .../subtools/sublibrary/util/Sleep.java | 18 + .../util/{ => function}/BooleanConsumer.java | 2 +- .../util/function/QuadFunction.java | 14 + .../util/{ => function}/TriConsumer.java | 2 +- .../util/lazy/LazyQuadFunction.java | 11 + .../util/lazy/LazyThrowingBiFunction.java | 10 +- .../util/lazy/LazyThrowingQuadFunction.java | 36 + .../ThrowingQuadFunction.java | 52 ++ .../sublibrary/cache/InMemoryCacheTest.java | 54 +- pom.xml | 5 + 68 files changed, 911 insertions(+), 1466 deletions(-) create mode 100644 SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/SerializableDiskCache.java delete mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/cache/TypedDiskCache.java delete mode 100755 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/data/Html.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/Sleep.java rename SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/{ => function}/BooleanConsumer.java (97%) create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/function/QuadFunction.java rename SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/{ => function}/TriConsumer.java (87%) create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyQuadFunction.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/lazy/LazyThrowingQuadFunction.java create mode 100644 SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/throwingfunction/ThrowingQuadFunction.java diff --git a/MultiSubDownloader/pom.xml b/MultiSubDownloader/pom.xml index ffb93632..423373a2 100644 --- a/MultiSubDownloader/pom.xml +++ b/MultiSubDownloader/pom.xml @@ -67,6 +67,10 @@ systems.manifold manifold-params-rt + + systems.manifold + manifold-science + org.projectlombok lombok diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java index a1c2d9f1..76fea9c5 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java @@ -9,7 +9,7 @@ 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; +import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; @UtilityClass @Extension diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java index bbef78db..93d69f59 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java @@ -6,7 +6,7 @@ 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; +import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; @Extension @UtilityClass 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 3d46f420..e25a693a 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java @@ -1,6 +1,6 @@ package org.lodder.subtools.multisubdownloader; -import static java.util.concurrent.TimeUnit.*; +import static manifold.science.util.UnitConstants.*; import javax.swing.*; import java.awt.*; @@ -31,12 +31,11 @@ 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.cache.SerializableDiskCache; import org.lodder.subtools.sublibrary.util.http.HttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({ Files.class }) +@ExtensionMethod({Files.class}) public class App { private static final Logger LOGGER = LoggerFactory.getLogger(App.class); @@ -118,7 +117,8 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu app.makeSubtitleProviderStore().getAllProviders().stream().map(SubtitleProvider::getProviderName) .map(providerName -> providerName.contains("-") ? providerName.split("-")[0] : providerName) .map(providerName -> providerName + "-").toList(); - manager.clearExpiredCache(CacheType.DISK, key -> providerNames.stream().noneMatch(key::startsWith)); + manager.getCache(CacheType.DISK, key -> providerNames.stream().noneMatch(key::startsWith)) + .clearExpiredCache(); }).start(); } @@ -155,17 +155,19 @@ private static Manager createManager(boolean useGui) { splash.progressMsg = Messages.getText("App.Starting"); } DiskCache diskCache = - SerializableDiskCache.cacheBuilder().keyType(String.class).valueType(Serializable.class) - .timeToLive(SECONDS.convert(500, DAYS)) - .maxItems(2500) - .build(); + new DiskCache<>( + String.class, + Serializable.class, + 500 day, + 2500); InMemoryCache inMemoryCache = - InMemoryCache.builder().keyType(String.class).valueType(String.class) - .timeToLive(SECONDS.convert(10, MINUTES)) - .timerInterval(100L) - .maxItems(500) - .build(); + new InMemoryCache<>( + String.class, + String.class, + 10 min, + 100 ms, + 500); return new Manager(new HttpClient(), inMemoryCache, diskCache); } 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 007f7eca..c7bd5418 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -65,7 +65,7 @@ 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.TriConsumer; +import org.lodder.subtools.sublibrary.util.function.TriConsumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 56615ce2..4c45fb1f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java @@ -21,7 +21,8 @@ import org.lodder.subtools.sublibrary.ConfigProperties; import org.lodder.subtools.sublibrary.ConfigProperties.Property; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.Manager.ValueBuilderIsPresentIntf; +import org.lodder.subtools.sublibrary.Manager.CacheKey; +import org.lodder.subtools.sublibrary.Manager.Value; import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.cache.CacheType; import org.slf4j.Logger; @@ -69,10 +70,8 @@ public boolean isNewVersionAvailable() { } private Optional getUrlLatestNewStableGithubRelease() { - return manager.valueBuilder() - .cacheType(CacheType.MEMORY) - .key("GitHub-update") - .optionalSupplier(() -> { + return manager.getCache(CacheType.MEMORY, "GitHub-update") + .getOptional(() -> { try { String currentVersion = getVersion(); Element element = @@ -104,14 +103,12 @@ private Optional getUrlLatestNewStableGithubRelease() { } return Optional.empty(); } - }).getOptional(); + }); } private Optional getUrlLatestNewNightlyGithubRelease() { - return manager.valueBuilder() - .cacheType(CacheType.MEMORY) - .key("GitHub-update-nightly") - .optionalSupplier(() -> { + return manager.getCache(CacheType.MEMORY, "GitHub-update-nightly") + .getOptional(() -> { try { LocalDateTime buildTista = getBuildTista(); @@ -141,7 +138,7 @@ private Optional getUrlLatestNewNightlyGithubRelease() { } return Optional.empty(); } - }).getOptional(); + }); } private LocalDateTime getBuildTista() { @@ -157,17 +154,16 @@ private boolean isFinalVersion(String version) { return !version.contains("-SNAPSHOT"); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static ValueBuilderIsPresentIntf getUpdateLastUpdateCheckBuilder(Manager manager) { - return (ValueBuilderIsPresentIntf) manager.valueBuilder().cacheType(CacheType.DISK).key("LastUpdateCheck"); + private CacheKey getUpdateLastUpdateCheckCache() { + return manager.getCache(CacheType.DISK, "LastUpdateCheck"); } private void updateLastUpdateCheck() { - getUpdateLastUpdateCheckBuilder(manager).value(LocalDate.now()).store(); + getUpdateLastUpdateCheckCache().store(Value.of(LocalDate.now())); } private LocalDate getLastUpdateCheck() { - return getUpdateLastUpdateCheckBuilder(manager).valueSupplier(() -> LocalDate.MIN).get(); + return getUpdateLastUpdateCheckCache().get(() -> LocalDate.MIN); } private LocalDateTime zonedDateTimeStringToLocalDateTime(String dateString) { 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 0c4f6812..4e97fffe 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 @@ -57,10 +57,10 @@ public MappingEpisodeNameDialog(JFrame frame, Manager manager, SubtitleProviderS .addComponent(BorderLayout.CENTER, new JPanel() .border(new EmptyBorder(5, 5, 5, 5)) .layout(new GridBagLayout() - .columnWidths(new int[]{ 0, 0 }) - .rowHeights(new int[]{ 0, 40, 0 }) - .columnWeights(new double[]{ 1.0, Double.MIN_VALUE }) - .rowWeights(new double[]{ 0.0, 1.0, Double.MIN_VALUE })) + .columnWidths(new int[]{0, 0}) + .rowHeights(new int[]{0, 40, 0}) + .columnWeights(new double[]{1.0, Double.MIN_VALUE}) + .rowWeights(new double[]{0.0, 1.0, Double.MIN_VALUE})) // select provider panel .addComponent(new JPanel() .addComponent(new JLabel(getText("MappingEpisodeNameDialog.SelectProvider"))) @@ -81,12 +81,10 @@ public MappingEpisodeNameDialog(JFrame frame, Manager manager, SubtitleProviderS int rowNbr = table.convertRowIndexToModel(table.getSelectedRow()); MappingTableModel model = (MappingTableModel) table.getModel(); Row row = (Row) model.getDataVector().get(rowNbr); - manager.valueBuilder().cacheType(CacheType.DISK).key(row.key).remove(); + manager.getCache(CacheType.DISK, row.key).remove(); if (row.selectionForKeyPrefix.deleteOtherFunction() != null) { - manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(row.selectionForKeyPrefix.deleteOtherFunction().apply(row.key)) - .remove(); + manager.getCache(CacheType.DISK, + row.selectionForKeyPrefix.deleteOtherFunction().apply(row.key)).remove(); } model.removeRow(rowNbr); })) @@ -178,11 +176,8 @@ public String toString() { } static { - MAPPING_SUPPLIER = (manager, selectionForKeyPrefix) -> manager.valueBuilder() - .cacheType(CacheType.DISK) - .keyFilter(k -> k.startsWith(selectionForKeyPrefix.keyPrefix)) - .returnType(SerieMapping.class) - .getEntries(); + MAPPING_SUPPLIER = (manager, selectionForKeyPrefix) -> + manager.getCache(CacheType.DISK, k -> k.startsWith(selectionForKeyPrefix.keyPrefix)).getEntries(); } MappingType(String name, SubtitleSource subtitleSource, SelectionForKeyPrefix... selectionForKeyPrefixList) { @@ -229,7 +224,7 @@ private static class MappingTableModel extends DefaultTableModel { void setMappingType(MappingType mappingType) { setDataVector(null, - new String[]{ mappingType.nameColumn, mappingType.mappingColumn, mappingType.providerNameColumn }); + new String[]{mappingType.nameColumn, mappingType.mappingColumn, mappingType.providerNameColumn}); Arrays.stream(mappingType.selectionForKeyPrefixList) .flatMap(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix) .stream() 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 00f54eed..88433e42 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 @@ -1,8 +1,10 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import java.awt.*; import java.io.Serial; import java.util.Objects; @@ -11,7 +13,7 @@ import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; -import org.lodder.subtools.sublibrary.util.BooleanConsumer; +import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; public class MyPasswordField extends JPasswordField implements MyPasswordFieldOthersIntf { 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 index 18f7d0f3..83bed150 100644 --- 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 @@ -3,7 +3,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; -import org.lodder.subtools.sublibrary.util.BooleanConsumer; +import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; public interface MyPasswordFieldOthersIntf { MyPasswordFieldOthersIntf withValueVerifier(Predicate verifier); 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 485b3c8f..d0cc82f0 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 @@ -1,8 +1,10 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import java.awt.*; import java.io.Serial; import java.util.Arrays; @@ -12,7 +14,7 @@ import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; -import org.lodder.subtools.sublibrary.util.BooleanConsumer; +import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; public abstract sealed class MyTextFieldCommon> extends JTextField implements MyTextFieldToStringMapperIntf, 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 47fdf823..81bbf3a8 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 @@ -3,7 +3,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; -import org.lodder.subtools.sublibrary.util.BooleanConsumer; +import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; public interface MyTextFieldOthersIntf> { MyTextFieldOthersIntf withValueVerifier(Predicate verifier); 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 d0410a4d..729b4802 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 @@ -12,6 +12,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JAddic7edAdapter; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JAddic7edViaProxyAdapter; +import org.lodder.subtools.sublibrary.Credentials; import org.lodder.subtools.sublibrary.Manager; public class Addic7edServiceProvider implements ServiceProvider { @@ -43,21 +44,21 @@ private SubtitleProvider createProvider(UserInteractionHandler userInteractionHa Manager manager = app.makeManager(); boolean loginEnabled = false; - String username = ""; - String password = ""; + Credentials credentials = null; if (settings.loginAddic7edEnabled) { - username = StringUtils.trim(settings.loginAddic7edUsername); - password = StringUtils.trim(settings.loginAddic7edPassword); + String username = StringUtils.trim(settings.loginAddic7edUsername); + String password = StringUtils.trim(settings.loginAddic7edPassword); /* Protect against empty login */ - loginEnabled = !username.isEmpty() && !password.isEmpty(); + if (!username.isEmpty() && !password.isEmpty()) { + credentials = new Credentials(username, password); + } } if (settings.serieSourceAddic7edProxy) { return new JAddic7edViaProxyAdapter(manager, userInteractionHandler); } else { - return new JAddic7edAdapter(loginEnabled, username, password, - app.makePreferences().getBoolean(CliOption.SPEEDY.value, false), - manager, userInteractionHandler); + boolean speedy = app.makePreferences().getBoolean(CliOption.SPEEDY.value, false); + return new JAddic7edAdapter(manager, speedy, credentials, 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 f0e2809a..3e88fe88 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 @@ -10,6 +10,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.adapters.JOpenSubAdapter; +import org.lodder.subtools.sublibrary.Credentials; public class OpenSubtitlesServiceProvider implements ServiceProvider { @@ -38,16 +39,16 @@ public void register(Container app, UserInteractionHandler userInteractionHandle private SubtitleProvider createProvider(UserInteractionHandler userInteractionHandler) { Settings settings = app.makeSettings(); - boolean loginEnabled = false; - String username = ""; - String password = ""; + Credentials credentials = null; if (settings.loginOpenSubtitlesEnabled) { - username = StringUtils.trim(settings.loginOpenSubtitlesUsername); - password = StringUtils.trim(settings.loginOpenSubtitlesPassword); + String username = StringUtils.trim(settings.loginOpenSubtitlesUsername); + String password = StringUtils.trim(settings.loginOpenSubtitlesPassword); /* Protect against empty login */ - loginEnabled = !username.isEmpty() && !password.isEmpty(); + if (!username.isEmpty() && !password.isEmpty()) { + credentials = new Credentials(username, password); + } } - return new JOpenSubAdapter(loginEnabled, username, password, app.makeManager(), userInteractionHandler); + return new JOpenSubAdapter(app.makeManager(), credentials, userInteractionHandler); } private void registerListener(SubtitleProviderStore subtitleProviderStore, 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 790618c8..31bb43d6 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 @@ -29,7 +29,7 @@ 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.TriConsumer; +import org.lodder.subtools.sublibrary.util.function.TriConsumer; public enum SettingValue { 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 37553335..ecfd8264 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 @@ -2,6 +2,7 @@ import static manifold.ext.props.rt.api.PropOption.*; import static org.lodder.subtools.multisubdownloader.settings.SettingValue.*; +import static org.lodder.subtools.sublibrary.cache.CacheType.*; import java.io.BufferedInputStream; import java.io.IOException; @@ -29,15 +30,15 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesApi; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.cache.CacheType; +import org.lodder.subtools.sublibrary.Manager.Value; import org.lodder.subtools.sublibrary.control.VideoPatterns; import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -import org.lodder.subtools.sublibrary.util.TriConsumer; +import org.lodder.subtools.sublibrary.util.function.TriConsumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ExtensionMethod({ Files.class }) +@ExtensionMethod({Files.class}) public class SettingsControl { private static final Logger LOGGER = LoggerFactory.getLogger(SettingsControl.class); @@ -278,10 +279,7 @@ public void migrateSettingsV3ToV4() { public void migrateSettingsV4ToV5() { MappingType.ADDIC7ED_PROXY.selectionForKeyPrefixList .forEach(selectionForKeyPrefix -> MappingType.MAPPING_SUPPLIER.apply(manager, selectionForKeyPrefix) - .forEach(serieMappingPair -> manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(serieMappingPair.getKey()) - .remove())); + .forEach(serieMappingPair -> manager.getCache(DISK, serieMappingPair.getKey()).remove())); settings.settingsVersion = 5; SETTINGS_VERSION.store(this, preferences); } @@ -383,8 +381,7 @@ private static String migrateLibraryStructureV0(String oldStructure) { } private void migrateDatabase() { - int version = - manager.valueBuilder().cacheType(CacheType.DISK).key(DATABASE_VERSION_KEY).valueSupplier(() -> 0).get(); + int version = manager.getCache(DISK, DATABASE_VERSION_KEY).get(() -> 0); if (version == 0) { migrateDatabaseV0ToV1(); } @@ -394,35 +391,33 @@ private void migrateDatabase() { } private void migrateDatabaseV0ToV1() { - manager.valueBuilder().cacheType(CacheType.DISK).keyFilter(k -> k.startsWith("TVDB-SerieMapping-")).remove(); - manager.valueBuilder().cacheType(CacheType.DISK).keyFilter(k -> k.startsWith("TVDB-SerieId-")).remove(); - manager.valueBuilder().cacheType(CacheType.DISK).key(DATABASE_VERSION_KEY).value(1).store(); + manager.getCache(DISK, k -> k.startsWith("TVDB-SerieMapping-")).remove(); + manager.getCache(DISK, k -> k.startsWith("TVDB-SerieId-")).remove(); + manager.getCache(DISK, DATABASE_VERSION_KEY).store(Value.of(1)); } private void migrateDatabaseV1ToV2() { - 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; - try { - season = Integer.parseInt(pair.getKey().substring(lastIndexOfDash + 1)); - } catch (NumberFormatException e) { - season = -1; - } - String name = pair.getValue().name; - String providerId = pair.getValue().providerId; - String providerName = pair.getValue().providerName; - SerieMapping serieMapping = new SerieMapping(name, providerId, providerName, season); - return Pair.of(pair.getKey(), serieMapping); - }).toList(); + List> editedEntries = + manager.getCache(DISK, k -> k.startsWith("SUBSCENE-serieName-")) + .getEntries(SerieMapping.class) + .stream().map(pair -> { + int lastIndexOfDash = pair.getKey().lastIndexOf("-"); + int season; + try { + season = Integer.parseInt(pair.getKey().substring(lastIndexOfDash + 1)); + } catch (NumberFormatException e) { + season = -1; + } + String name = pair.getValue().name; + String providerId = pair.getValue().providerId; + String providerName = pair.getValue().providerName; + SerieMapping serieMapping = new SerieMapping(name, providerId, providerName, season); + return Pair.of(pair.getKey(), serieMapping); + }).toList(); editedEntries.forEach(entry -> { - manager.valueBuilder().cacheType(CacheType.DISK).key(entry.getKey()).remove(); - manager.valueBuilder().cacheType(CacheType.DISK).key(entry.getKey()).value(entry.getValue()).store(); + manager.getCache(DISK, entry.key).remove(); + manager.getCache(DISK, entry.key).store(Value.of(entry.getValue())); }); - manager.valueBuilder().cacheType(CacheType.DISK).key(DATABASE_VERSION_KEY).value(2).store(); + manager.getCache(DISK, DATABASE_VERSION_KEY).store(Value.of(2)); } } 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 deb1680f..c04d3e1e 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 @@ -53,7 +53,7 @@ default Set search(Release release, Language language) { } default void clearCache() { - manager.clearExpiredCache(CacheType.DISK, k -> k.startsWith(providerName + "-")); + manager.getCache(CacheType.DISK, k -> k.startsWith(providerName + "-")).clearExpiredCache(); } 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 83bc2901..459c5192 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 @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.adapters; +import static manifold.science.measures.TimeUnit.*; +import static org.lodder.subtools.sublibrary.util.Sleep.*; + import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -89,11 +92,7 @@ public T execute() throws X { if (retries-- == 0) { throw new RuntimeException("Max retries reached when calling %s".formatted(message)); } - try { - Thread.sleep(5000); - } catch (InterruptedException e1) { - // continue - } + sleep(5 Second); return execute(); } else { try { 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 9acebeaf..9a9fce75 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,9 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.adapters; +import static manifold.science.util.UnitConstants.*; import static org.lodder.subtools.multisubdownloader.Messages.*; import java.io.IOException; -import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; @@ -12,9 +12,7 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.function.Function; -import java.util.function.Supplier; import lombok.experimental.ExtensionMethod; import org.apache.commons.lang3.StringUtils; @@ -22,7 +20,9 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesHasher; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.Manager.ValueBuilderIsPresentIntf; +import org.lodder.subtools.sublibrary.Manager.CacheKey; +import org.lodder.subtools.sublibrary.Manager.CollectionValue; +import org.lodder.subtools.sublibrary.Manager.Value; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; @@ -38,7 +38,7 @@ * @param type of the ProviderSerieId * @param type of the exception thrown by the api */ -@ExtensionMethod({ Files.class }) +@ExtensionMethod({Files.class}) public interface Adapter extends SubtitleProvider { Logger LOGGER = LoggerFactory.getLogger(Adapter.class); @@ -137,48 +137,46 @@ default Optional getProviderSerieId(String serieName, String displ 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 - return tvdbIdValueBuilder.get().returnType(SerieMapping.class).getOptional(); + + Function tvdbIdCacheFunction = tvdbId -> manager.getCache(CacheType.DISK, + "%s-serieName-tvdbId:%s-%s".formatted(providerName, tvdbId, useSeasonForSerieId() ? season : -1)); + if (tvdbIdOptional.isPresent()) { + CacheKey tvdbIdCache = tvdbIdCacheFunction.apply(tvdbIdOptional.orElseThrow()); + if (tvdbIdCache.isPresent()) { + // if value using the tvdbId is present, return it + return tvdbIdCache.getOptional(); + } } if (StringUtils.isBlank(serieNameToSearchFor)) { return Optional.empty(); } - int seasonToUse = useSeasonForSerieId() ? season : 0; - ValueBuilderIsPresentIntf serieNameValueBuilder = manager.valueBuilder() - .cacheType(CacheType.DISK) - .key("%s-serieName-name:%s-%s".formatted(providerName, serieName.toLowerCase(), seasonToUse)); - if (StringUtils.equals(serieNameToSearchFor, serieName) && serieNameValueBuilder.isPresent()) { - if (serieNameValueBuilder.isTemporaryObject()) { - if (!serieNameValueBuilder.isExpiredTemporary()) { + int seasonToUse = useSeasonForSerieId() ? season : 0; + CacheKey serieNameCache = manager.getCache(CacheType.DISK, + "%s-serieName-name:%s-%s".formatted(providerName, serieName.toLowerCase(), seasonToUse)); + if (StringUtils.equals(serieNameToSearchFor, serieName) && serieNameCache.isPresent()) { + if (serieNameCache.isTemporaryObject()) { + if (!serieNameCache.isExpiredTemporary()) { return Optional.empty(); } } else { - Optional serieMapping = - serieNameValueBuilder.returnType(SerieMapping.class).getOptional(); + Optional serieMapping = serieNameCache.getOptional(); serieMapping.ifPresent(providerSerieName -> tvdbIdOptional.ifPresent( - tvdbId -> tvdbIdValueBuilder.get().value(providerSerieName).store())); + tvdbId -> tvdbIdCacheFunction.apply(tvdbId).store(Value.of(providerSerieName)))); return serieMapping; } } List providerSerieIds = getSortedProviderSerieIds(tvdbIdOptional, serieNameToSearchFor, seasonToUse); if (providerSerieIds.isEmpty()) { - // 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)) - .storeTempNullValue() - .timeToLive(serieNameValueBuilder.getTemporaryTimeToLive() - .map(v -> v * 2) - .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) - .storeAsTempValue(); + // 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. + serieNameCache.store( + value:Value.of(new SerieMapping(serieName, null, null, seasonToUse)), + timeToLive:serieNameCache.getTemporaryTimeToLive().map(v -> v * 2).orElse(1 day), + storeAsTempValue:true, + storeTempNullValue:true); return Optional.empty(); } @@ -187,17 +185,13 @@ default Optional getProviderSerieId(String serieName, String serie serieMapping = new SerieMapping(serieName, providerSerieIds.first.id, providerSerieIds.first.name, seasonToUse); } else { - ValueBuilderIsPresentIntf previousResultsValueBuilder = manager.valueBuilder() - .cacheType(CacheType.MEMORY) - .key("%s-serieName-prev-results:%s-%s".formatted(providerName, displayName.toLowerCase(), - seasonToUse)); + CacheKey previousResultsCache = manager.getCache(CacheType.MEMORY, + "%s-serieName-prev-results:%s-%s".formatted(providerName, displayName.toLowerCase(), seasonToUse)); - boolean previousResultsPresent = previousResultsValueBuilder.isPresent(); + boolean previousResultsPresent = previousResultsCache.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 && providerSerieIds.equals(previousResultsCache.getCollection(null))) { uriForSerie = Optional.empty(); } else { // let the user select the correct provider serie id @@ -213,24 +207,21 @@ default Optional getProviderSerieId(String serieName, String serie 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)) - .storeTempNullValue() - .timeToLive(serieNameValueBuilder.getTemporaryTimeToLive() - .map(v -> v * 2) - .orElseGet(() -> TimeUnit.SECONDS.convert(1, TimeUnit.DAYS))) - .storeAsTempValue(); - previousResultsValueBuilder.collectionValue(providerSerieIds).store(); + serieNameCache.store( + value:Value.of(new SerieMapping(serieNameToSearchFor, null, null, seasonToUse)), + timeToLive:serieNameCache.getTemporaryTimeToLive().map(v -> v * 2).orElse(1 day), + storeAsTempValue:true, + storeTempNullValue:true); + previousResultsCache.store(CollectionValue.of(providerSerieIds)); } return Optional.empty(); } // create a serieMapping for the selected value serieMapping = new SerieMapping(serieName, uriForSerie.get().id, uriForSerie.get().name, seasonToUse); } - if (tvdbIdOptional.isPresent()) { - tvdbIdValueBuilder.get().value(serieMapping).store(); - } else { - serieNameValueBuilder.value(serieMapping).store(); - } + tvdbIdOptional.ifPresentOrElse( + tvdbId -> tvdbIdCacheFunction.apply(tvdbId).store(Value.of(serieMapping)), + () -> serieNameCache.store(Value.of(serieMapping))); return Optional.of(serieMapping); } 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 0b257664..e4bb7de1 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 @@ -16,6 +16,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.JAddic7edApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.exception.Addic7edException; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.model.Addic7edSubtitleDescriptor; +import org.lodder.subtools.sublibrary.Credentials; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.control.ReleaseParser; @@ -40,14 +41,13 @@ public final class JAddic7edAdapter extends AbstractAdapter(() -> { try { - return isLoginEnabled ? new JAddic7edApi(username, password, speedy, manager) : - new JAddic7edApi(speedy, manager); + return new JAddic7edApi(manager, speedy, credentials); } catch (Exception e) { throw new SubtitlesProviderInitException(providerName, e); } 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 90c7e416..647b5ad1 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 @@ -17,6 +17,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.exception.OpenSubtitlesException; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.model.OpensubtitleSerieId; +import org.lodder.subtools.sublibrary.Credentials; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.control.ReleaseParser; @@ -42,17 +43,12 @@ public final class JOpenSubAdapter @val @override SubtitleSource subtitleSource = SubtitleSource.OPENSUBTITLES; @val @override String providerName = subtitleSource.name(); - public JOpenSubAdapter(boolean isLoginEnabled, String username, String password, Manager manager, - UserInteractionHandler userInteractionHandler) { + public JOpenSubAdapter(Manager manager, Credentials credentials, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); if (osApi == null) { osApi = new LazySupplier<>(() -> { try { - if (isLoginEnabled) { - return new OpenSubtitlesApi(manager, username, password); - } else { - return new OpenSubtitlesApi(manager); - } + return new OpenSubtitlesApi(manager, credentials); } catch (OpenSubtitlesException e) { throw new SubtitlesProviderInitException(providerName, e); } 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 fd9588c3..5d657ee5 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 @@ -1,18 +1,18 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed; import static java.nio.charset.StandardCharsets.*; +import static manifold.science.util.UnitConstants.*; +import static org.lodder.subtools.sublibrary.util.Sleep.*; import java.net.URLEncoder; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; 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 manifold.science.measures.Time; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -20,43 +20,42 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.exception.Addic7edException; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.model.Addic7edSubtitleDescriptor; +import org.lodder.subtools.sublibrary.Credentials; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.ManagerException; import org.lodder.subtools.sublibrary.PageContentParams; import org.lodder.subtools.sublibrary.cache.CacheType; -import org.lodder.subtools.sublibrary.data.Html; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -public class JAddic7edApi extends Html implements SubtitleApi { +public class JAddic7edApi implements SubtitleApi { - private static final long RATEDURATION = 1; // seconds + private static final Time RATE_DURATION = 1 s; // seconds private static final String DOMAIN = "https://www.addic7ed.com"; 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 Manager manager; private final boolean speedy; - private LocalDateTime lastRequest = LocalDateTime.now(); + private Time lastRequest = Time.now(); @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; - public JAddic7edApi(boolean speedy, Manager manager) { - super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); + public JAddic7edApi(Manager manager, boolean speedy, Credentials credentials=null) throws Addic7edException { +// super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); + this.manager = manager; this.speedy = speedy; + if (credentials != null) { + login(credentials); + } } - public JAddic7edApi(String username, String password, boolean speedy, Manager manager) throws Addic7edException { - super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); - this.speedy = speedy; - login(username, password); - } - - public void login(String username, String password) throws Addic7edException { + public void login(Credentials credentials) throws Addic7edException { try { manager.postBuilder("$DOMAIN/dologin.php") - .addData("username", username) - .addData("password", password) + .addData("username", credentials.username) + .addData("password", credentials.password) .addData("remember", "false") .post(); } catch (ManagerException e) { @@ -87,24 +86,13 @@ public List getProviderId(String serieName) throws Addic7edExce } } - // private List getAllMappings() throws Addic7edException { - // return ALL_MAPPINGS.get(); - // } - // - // private final LazyThrowingSupplier, Addic7edException> ALL_MAPPINGS = - // new LazyThrowingSupplier<>(() -> getContent(DOMAIN) - // .map(doc -> doc.select("#qsShow option").stream() - // .map(e -> new ProviderSerieId(e.text(), e.attr("value"))) - // .toList()) - // .orElseGet(List::of)); - 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(subtitleSource.name(), addic7edSerieMapping.providerId, + + return manager.getCache(CacheType.MEMORY, + "%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), addic7edSerieMapping.providerId, season, episode, language)) - .collectionSupplier(Addic7edSubtitleDescriptor.class, () -> { + .getCollection(() -> { List languageIds = LanguageId.forLanguage(language); String url = "%s/serie/%s/%s/%s/%s".formatted(DOMAIN, URLEncoder.encode(addic7edSerieMapping.providerName.replace(" ", "_"), UTF_8), season, @@ -121,7 +109,6 @@ public List getSubtitles(SerieMapping addic7edSerieM } } - Elements blocks = doc.select(".tabel95[width='100%']"); List lSubtitles = new ArrayList<>(); @@ -181,8 +168,7 @@ public List getSubtitles(SerieMapping addic7edSerieM } } return lSubtitles; - }) - .getCollection(); + }); } public boolean isDuplicate(List lSubtitles, Addic7edSubtitleDescriptor sub) { @@ -192,20 +178,15 @@ public boolean isDuplicate(List lSubtitles, Addic7ed private Document getContent(String url) throws Addic7edException { try { - if (!speedy && !manager.valueBuilder().cacheType(CacheType.MEMORY).key(url).isPresent()) { + if (!speedy && !manager.getCache(CacheType.MEMORY, url).isPresent()) { // if (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATEDURATION) { // LOGGER.info("RateLimit is reached for ADDIC7ed, please wait {} seconds", RATEDURATION); // } - while (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATEDURATION) { - try { - // Pause for 1 seconds - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - // restore interrupted status - Thread.currentThread().interrupt(); - } + Time timeToSleep = RATE_DURATION - Time.now() + lastRequest; + if (timeToSleep.isPositive) { + sleep(timeToSleep); } - lastRequest = LocalDateTime.now(); + lastRequest = Time.now(); } return manager.getAsJsoupDocument(PageContentParams.params(url:url, userAgent:"")); } catch (Exception e) { 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 bbe2c0f1..7d4a6da2 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,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.model; +import java.io.Serializable; + import org.lodder.subtools.sublibrary.Language; public record Addic7edSubtitleDescriptor(String version, Language language, String url, String title, String uploader, - boolean hearingImpaired) { + boolean hearingImpaired) implements Serializable { } 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 22a59724..2266dc90 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 @@ -17,8 +17,8 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; +import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.control.ReleaseParser; -import org.lodder.subtools.sublibrary.data.Html; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleMatchType; @@ -26,16 +26,17 @@ import org.lodder.subtools.sublibrary.settings.model.SerieMapping; // see https://www.gestdown.info/Api -public class JAddic7edProxyGestdownApi extends Html implements SubtitleApi { +public class JAddic7edProxyGestdownApi implements SubtitleApi { private static final String DOMAIN = "https://api.gestdown.info"; + private final Manager manager; private final TvShowsApi tvShowsApi; private final SubtitlesApi subtitlesApi; @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; public JAddic7edProxyGestdownApi(Manager manager) { - super(manager); + this.manager = manager; tvShowsApi = new TvShowsApi(); subtitlesApi = new SubtitlesApi(); } @@ -52,11 +53,9 @@ public List getProviderSerieName(int tvdbId) throws ApiExceptio 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(subtitleSource.name(), providerSerieId.providerId, - season, episode, language)) - .collectionSupplier(Subtitle.class, () -> { + return manager.getCache(CacheType.MEMORY, "%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), + providerSerieId.providerId, season, episode, language)) + .getCollection(() -> { Set results = new HashSet<>(); SubtitleSearchResponse response = subtitlesApi.subtitlesGetShowUniqueIdSeasonEpisodeLanguageGet(language.getName(), @@ -67,7 +66,7 @@ public Set getSubtitles(SerieMapping providerSerieId, int season, int .map(sub -> mapToSubtitle(sub, response.episode, language)) .forEach(results::add); return results; - }).getCollection(); + }); } private Subtitle mapToSubtitle(SubtitleDto sub, EpisodeDto episodedto, Language language) { 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 e5995d9c..0a72e6a5 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 @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles; +import static manifold.science.measures.TimeUnit.*; + import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; @@ -9,6 +11,7 @@ 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.Credentials; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.PageContentParams; @@ -34,20 +37,18 @@ public class OpenSubtitlesApi implements SubtitleApi { API_CLIENT.setApiKey(APIKEY); } - public OpenSubtitlesApi(Manager manager) { + public OpenSubtitlesApi(Manager manager, Credentials credentials=null) throws OpenSubtitlesException { this.manager = manager; + if (credentials != null) { + login(credentials); + } } - public OpenSubtitlesApi(Manager manager, String userName, String password) throws OpenSubtitlesException { - this(manager); - login(userName, password); - } - - public void login(String userName, String password) throws OpenSubtitlesException { + public void login(Credentials credentials) throws OpenSubtitlesException { try { Login200Response loginResponse = new AuthenticationApi(API_CLIENT).login("application/json", USER_AGENT, - new LoginRequest().username(userName).password(password)); + new LoginRequest().username(credentials.username).password(credentials.password)); API_CLIENT.setBearerToken(loginResponse.getToken()); } catch (ApiException e) { throw new OpenSubtitlesException(e); @@ -82,7 +83,7 @@ public List getProviderSerieIds(String serieName) throws Op retry:new Retry( 1, exc -> exc instanceof HttpClientException e && e.responseCode == 429, - 5) + 5 Second) )) .streamJsonObjects() .filter(show -> "tv".equals(show.getString("kind"))) 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 560602ad..5ba76ec1 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,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles; +import static manifold.science.measures.TimeUnit.*; +import static org.lodder.subtools.sublibrary.util.Sleep.*; + import com.pivovarit.function.ThrowingSupplier; import org.opensubtitles.invoker.ApiException; @@ -11,11 +14,7 @@ protected T execute(ThrowingSupplier callable) throws ApiEx } catch (ApiException e) { if (e.getCode() == 429 || e.getMessage().contains("ratelimit")) { // Too Many Requests - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - throw new RuntimeException(e1); - } + sleep(1 Second); // retry return callable.get(); } else { 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 09978325..7816259a 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 @@ -79,14 +79,12 @@ public final class SearchSubtitles extends OpenSubtitlesExecuter { private String userAgent = "SubTools"; // should be set public Subtitles200Response searchSubtitles() throws OpenSubtitlesException { - return manager.valueBuilder() - .cacheType(CacheType.MEMORY) - .key( + return manager.getCache(CacheType.MEMORY, "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(() -> { + .get(() -> { try { return execute( () -> new SubtitlesApi(apiClient).subtitles(id, imdbId, tmdbId, getValue(type), query, @@ -99,8 +97,7 @@ public Subtitles200Response searchSubtitles() throws OpenSubtitlesException { } catch (Exception e) { throw new OpenSubtitlesException(e); } - }) - .get(); + }); } private String getValue(ParamIntf param) { 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 d1e7aea5..08e9e71e 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 @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi; +import static manifold.science.measures.TimeUnit.*; import static org.lodder.subtools.sublibrary.PageContentParams.*; import java.io.Serial; @@ -61,12 +62,10 @@ public List getSerieSubtitles(SerieMapping provider private List getSubtitles(SerieMapping providerSerieId, Integer year, int season, int episode, Language language) throws PodnapisiException { - return manager.valueBuilder() - .memoryCache() - .key( + return manager.getCache(CacheType.MEMORY, "%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), providerSerieId.providerId, season, episode, language)) - .collectionSupplier(PodnapisiSubtitleDescriptor.class, () -> { + .getCollection(() -> { try { StringBuilder url = new StringBuilder("$DOMAIN/sl/ppodnapisi/search?sK=").append( URLEncoder.encode(providerSerieId.providerId.trim().toLowerCase(), StandardCharsets.UTF_8)); @@ -93,8 +92,7 @@ private List getSubtitles(SerieMapping providerSeri } catch (Exception e) { throw new PodnapisiException(e); } - }) - .getCollection(); + }); } @@ -105,7 +103,7 @@ private List getSubtitles(SerieMapping providerSeri 1, ex -> ex instanceof HttpClientException e && e.responseCode >= 500 && e.responseCode < 600, - 5))); + 5 Second))); } catch (Exception e) { throw new PodnapisiException(e); } 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 d0492152..e838ac3a 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,10 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.subscene; +import static manifold.science.measures.TimeUnit.*; +import static org.lodder.subtools.sublibrary.util.Sleep.*; + import java.io.Serial; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.EnumMap; import java.util.List; @@ -18,6 +20,7 @@ import lombok.RequiredArgsConstructor; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import manifold.science.measures.Time; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -30,17 +33,17 @@ import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.ManagerException; import org.lodder.subtools.sublibrary.PageContentParams; -import org.lodder.subtools.sublibrary.data.Html; +import org.lodder.subtools.sublibrary.cache.CacheType; 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.http.HttpClientException; import util.Utils; -public class SubsceneApi extends Html implements SubtitleApi { +public class SubsceneApi implements SubtitleApi { - private static final int RATE_DURATION_SHORT = 1; // seconds - private static final int RATE_DURATION_LONG = 5; // seconds + private static final Time RATE_DURATION_SHORT = 1 Second; + private static final Time RATE_DURATION_LONG = 5 Second; private static final String DOMAIN = "https://subscene.com"; private static final Pattern SERIE_NAME_PATTERN = Pattern.compile(".*? - ([A-Z][a-z]*) Season.*"); @@ -51,6 +54,7 @@ public class SubsceneApi extends Html implements SubtitleApi { default -> false; }; + private final Manager manager; private int selectedLanguage; private boolean selectedIncludeHearingImpaired; @@ -58,7 +62,8 @@ public class SubsceneApi extends Html implements SubtitleApi { @val @override SubtitleSource subtitleSource = SubtitleSource.SUBSCENE; public SubsceneApi(Manager manager) { - super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); +// super(manager, "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); + this.manager = manager; addCookie("ForeignOnly", "False"); } @@ -92,11 +97,9 @@ public Map> getSubSceneSerieNames(String serieName 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, () -> { + return manager.getCache(CacheType.MEMORY, "%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name, + providerSerieId.providerId, season, episode, language)) + .getCollection(() -> { setLanguageWithCookie(language); try { return getJsoupDocument(DOMAIN + providerSerieId.providerId) @@ -138,9 +141,11 @@ private String getDownloadUrl(String seriePageUrl) throws SubsceneException { } private Document getJsoupDocument(String url) throws ManagerException { - while (ChronoUnit.SECONDS.between(lastRequest, LocalDateTime.now()) < RATE_DURATION_SHORT) { - sleepSeconds(1); + Time timeToSleep = RATE_DURATION_SHORT - Time.now() + lastRequest; + if (timeToSleep.isPositive) { + sleep(timeToSleep); } + Document document = manager.getAsJsoupDocument(PageContentParams.params( url:url, 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 3f2cc550..428df26a 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 @@ -21,19 +21,20 @@ import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.PageContentParams; -import org.lodder.subtools.sublibrary.data.Html; +import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; -public class JTVSubtitlesApi extends Html implements SubtitleApi { +public class JTVSubtitlesApi implements SubtitleApi { private static final String DOMAIN = "https://www.tvsubtitles.net"; private static final String SERIE_URL_PREFIX = DOMAIN + "/"; + private final Manager manager; @val @override SubtitleSource subtitleSource = SubtitleSource.TVSUBTITLES; public JTVSubtitlesApi(Manager manager) { - super(manager); + this.manager = manager; } public List getUrisForSerieName(String serieName) throws TvSubtitlesException { @@ -59,10 +60,8 @@ public Set getSubtitles(SerieMapping providerSeri private Set getSubtitles(String episodeUrl, Language language) throws TvSubtitlesException { - return manager.valueBuilder() - .memoryCache() - .key("%s-subtitles-%s-%s".formatted(subtitleSource.name(), episodeUrl, language)) - .collectionSupplier(TVsubtitlesSubtitleDescriptor.class, () -> { + return manager.getCache(CacheType.MEMORY, subtitleSource.name() + "subtitles-$episodeUrl-$language") + .getCollection(() -> { Set lSubtitles = new HashSet<>(); try { Elements searchEpisodes = @@ -123,15 +122,12 @@ private Set getSubtitles(String episodeUrl, Langu } catch (Exception e) { throw new TvSubtitlesException(e); } - }) - .getCollection(); + }); } private Optional getEpisodeUrl(String showUrl, int season, int episode) throws TvSubtitlesException { - return manager.valueBuilder() - .memoryCache() - .key("%s-episodeUrl-%s-%s-%s".formatted(subtitleSource.name(), showUrl, season, episode)) - .optionalSupplier(() -> { + return manager.getCache(CacheType.MEMORY, subtitleSource.name() + "-episodeUrl-$showUrl-$season-$episode") + .getOptional(() -> { try { String formattedSeasonEpisode = season + "x" + (episode < 10 ? "0" + episode : String.valueOf(episode)); @@ -152,7 +148,6 @@ private Optional getEpisodeUrl(String showUrl, int season, int episode) } catch (Exception e) { throw new TvSubtitlesException(e); } - }) - .getOptional(); + }); } } 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 6160551a..a018bbba 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 @@ -27,6 +27,7 @@ import org.lodder.subtools.multisubdownloader.gui.dialog.MappingEpisodeNameDialog.MappingType; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.sublibrary.Manager; +import org.lodder.subtools.sublibrary.Manager.Value; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler.MessageSeverity; @@ -123,11 +124,8 @@ public void exportSettings(Path path, Manager manager) throws IOException { 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() + .flatMap(selectionForKeyPrefix -> manager.getCache(CacheType.DISK, + k -> k.startsWith(selectionForKeyPrefix.keyPrefix())).getEntries(SerieMapping.class) .stream() .map(pair -> new SeriemappingWithKey(pair.getKey(), pair.getValue()))) .toList(); @@ -150,14 +148,11 @@ public void importSettings(Path path, UserInteractionHandler userInteractionHand .map(MappingType::getSelectionForKeyPrefixList) .flatMap(Arrays::stream) .forEach(selectionForKeyPrefix -> - manager.clearExpiredCache(CacheType.DISK, - k -> k.startsWith(selectionForKeyPrefix.keyPrefix))); + manager.getCache(CacheType.DISK, k -> k.startsWith(selectionForKeyPrefix.keyPrefix)) + .clearExpiredCache()); } - serieMappings.forEach(serieMapping -> manager.valueBuilder() - .cacheType(CacheType.DISK) - .key(serieMapping.key) - .value(serieMapping.serieMapping) - .store()); + serieMappings.forEach(serieMapping -> + manager.getCache(CacheType.DISK, serieMapping.key).store(Value.of(serieMapping.serieMapping))); }); } diff --git a/SubLibrary/pom.xml b/SubLibrary/pom.xml index ca3c0ebc..32b81be0 100644 --- a/SubLibrary/pom.xml +++ b/SubLibrary/pom.xml @@ -95,6 +95,10 @@ systems.manifold manifold-params-rt + + systems.manifold + manifold-science + org.projectlombok lombok diff --git a/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java b/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java index 2893a56b..4dc390bc 100644 --- a/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java @@ -9,6 +9,7 @@ import manifold.ext.rt.api.This; import name.falgout.jeffrey.throwing.ThrowingConsumer; import name.falgout.jeffrey.throwing.ThrowingFunction; +import name.falgout.jeffrey.throwing.ThrowingSupplier; import name.falgout.jeffrey.throwing.ThrowingToIntFunction; @UtilityClass @@ -44,4 +45,10 @@ public static OptionalInt mapToInt(@This Optional op } return optional; } + + public static T orElseGetThrowing(@This Optional optional, + ThrowingSupplier supplier) throws X { + return optional.isPresent() ? optional.get() : supplier.get(); + } + } diff --git a/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java b/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java new file mode 100644 index 00000000..84fb2e6b --- /dev/null +++ b/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java @@ -0,0 +1,26 @@ +package extensions.manifold.science.measures.Time; + +import java.math.BigInteger; + +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import manifold.science.measures.Time; +import manifold.science.measures.TimeUnit; +import manifold.science.util.Rational; + +@Extension +public class TimeExt { + + public static boolean isPositive(@This Time time) { + return time.value.numerator.compareTo(BigInteger.ZERO) > 0; + } + + public static boolean isNegative(@This Time time) { + return time.value.numerator.compareTo(BigInteger.ZERO) < 0; + } + + @Extension + public static Time create(Number duration, TimeUnit unit) { + return new Time(Rational.get(duration), unit); + } +} diff --git a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java index 02d2b922..b1f34c23 100644 --- a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java +++ b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java @@ -9,6 +9,7 @@ import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -112,7 +113,7 @@ public static List promptValuesFromList(@This Prompter prompter, Validator inputValidator = new Validator<>(v -> v == null || v.split(",").stream().allMatch(n -> n.parseAsNumber(Integer::parseUnsignedInt).isPresent())); Function toObjectsMapper = - v -> v.split(",").stream().mapToInt(Integer::parseUnsignedInt).toArray(); + v -> Arrays.stream(v.split(",")).mapToInt(Integer::parseUnsignedInt).toArray(); int numberOfElements = elements.size(); List> objectValidators = List.of( new Validator<>(numbers -> numbers.stream().distinct().count() == numbers.stream().count(), diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java new file mode 100644 index 00000000..32e43b58 --- /dev/null +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java @@ -0,0 +1,4 @@ +package org.lodder.subtools.sublibrary; + +public record Credentials(String username, String password) { +} 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 29d0eae4..fffbbb64 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -1,6 +1,7 @@ package org.lodder.subtools.sublibrary; -import static java.util.concurrent.TimeUnit.*; +import static manifold.science.measures.TimeUnit.*; +import static manifold.science.util.UnitConstants.*; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; @@ -17,18 +18,13 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalInt; -import java.util.OptionalLong; -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 manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; -import name.falgout.jeffrey.throwing.Nothing; +import manifold.science.measures.Time; +import name.falgout.jeffrey.throwing.ThrowingSupplier; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.json.JSONArray; @@ -40,6 +36,8 @@ 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.Nothing; +import org.lodder.subtools.sublibrary.util.Sleep; import org.lodder.subtools.sublibrary.util.http.HttpClient; import org.lodder.subtools.sublibrary.util.http.HttpClientException; import org.lodder.subtools.sublibrary.xml.XMLHelper; @@ -113,10 +111,9 @@ public org.jsoup.nodes.Document postAsJsoupDocument() throws ManagerException { // ================ \\ public String get(PageContentParams params) throws ManagerException { - ThrowingSupplier supplier = () -> getContentWithoutCache(params); return switch (params.cacheType) { case NONE -> getContentWithoutCache(params); - case MEMORY -> inMemoryCache.getOrPut(params.url, supplier); + case MEMORY -> inMemoryCache.getOrPut(params.url, () -> getContentWithoutCache(params)); case DISK -> throw new IllegalArgumentException("Unexpected value: " + params.cacheType); }; } @@ -162,22 +159,21 @@ private String getAsJsonString(PageContentParams params) throws ManagerException } private String getContentWithoutCache(PageContentParams params) throws ManagerException { - String url = params.url; - String userAgent = params.userAgent; - Retry retry = params.retry; + return getContentWithoutCache(params.url, params.userAgent, params.retry); + } + + private String getContentWithoutCache(String url, String userAgent, Retry retry) throws ManagerException { try { return httpClient.doGet(new URI(url).toURL(), userAgent); } catch (HttpClientException e) { if (retry.canRetry() && retry.predicate.test(e)) { - retry.decreaseRetries().sleep(); - return getContentWithoutCache(params); + return getContentWithoutCache(url, userAgent, retry.decreaseRetries().sleep()); } throw new ManagerException( "Error occurred with httpclient response: %s %s".formatted(e.responseCode, e.responseMessage), e); } catch (IOException e) { if (retry.canRetry() && retry.predicate.test(e)) { - retry.decreaseRetries(); - return getContentWithoutCache(params); + return getContentWithoutCache(url, userAgent, retry.decreaseRetries()); } throw new ManagerException(e); } catch (URISyntaxException e) { @@ -185,9 +181,9 @@ private String getContentWithoutCache(PageContentParams params) throws ManagerEx } } - public record Retry(int retries, Predicate predicate, int waitSeconds) { + public record Retry(int retries, Predicate predicate, Time waitTime) { - public static final Retry DEFAULT = new Retry(0, null, 0); + public static final Retry NONE = new Retry(0, null, 0 Second); public Retry { if (retries < 0) { @@ -196,7 +192,7 @@ public record Retry(int retries, Predicate predicate, int waitSeconds } public Retry decreaseRetries() { - return new Retry(retries - 1, predicate, waitSeconds); + return new Retry(retries - 1, predicate, waitTime); } public boolean canRetry() { @@ -204,616 +200,271 @@ public boolean canRetry() { } public Retry sleep() { - try { - Thread.sleep(waitSeconds * 1000L); - } catch (InterruptedException e1) { - // continue - } + Sleep.sleep(waitTime); return this; } } - - // =========== \\ - // CLEAR CACHE \\ - // =========== \\ - - public void clearExpiredCache(CacheType cacheType, Predicate keyFilter=null) { - switch (cacheType) { - case MEMORY -> inMemoryCache.cleanup(keyFilter); - case DISK -> diskCache.cleanup(keyFilter); - default -> throw new IllegalArgumentException("Unexpected value: " + cacheType); - } - } - // ============= \\ - // VALUE BUILDER \\ + // CACHE METHODS \\ // ============= \\ - public ValueBuilderCacheTypeIntf valueBuilder() { - return new ValueBuilder<>(inMemoryCache, diskCache); - } - - public interface ValueBuilderCacheTypeIntf { - - ValueBuilderKeyIntf cacheType(CacheType cacheType); - - ValueBuilderKeyIntf memoryCache(); - - ValueBuilderKeyIntf diskCache(); - } - - public interface ValueBuilderKeyIntf { - ValueBuilderIsPresentIntf key(String key); - - ValuesBuilderCacheTypeIntf keyFilter(Predicate keyFilter); - } - - public interface ValueBuilderIsPresentIntf extends ValuesBuilderCacheTypeIntf { - boolean isPresent(); - - boolean isExpiredTemporary(); - - boolean isTemporaryObject(); - - OptionalLong getTemporaryTimeToLive(); - } - - public interface ValuesBuilderCacheTypeIntf extends ValueBuilderRetryIntf { - ValueBuilderGetOptionalIntf returnType(Class returnType); - - , S extends T> ValueBuilderGetCollectionIntf returnType( - Class collectionReturnType, Class returnType); - - void remove(); - } - - public interface ValueBuilderRetryIntf extends ValueBuilderValueSupplierIntf { - ValueBuilderRetryConditionIntf retries(int retries); - - ValueBuilderGetValueStoreTempValueIntf value(S value); - - ValueBuilderGetOptionalStoreTempValueIntf optionalValue(Optional optionalValue); - - ValueBuilderGetOptionalIntStoreTempValueIntf optionalIntValue(OptionalInt optionalIntValue); - - , S extends T> ValueBuilderGetCollectionIntf collectionValue( - C collectionValue); - } - - public interface ValueBuilderRetryConditionIntf { - ValueBuilderRetryWaitIntf retryPredicate(Predicate retryPredicate); - } - - public interface ValueBuilderRetryWaitIntf { - ValueBuilderValueSupplierIntf retryWait(int retryWait); - } - - public interface ValueBuilderValueSupplierIntf { + public record CacheKey(Manager manager, CacheType cacheType, String key) { + public boolean isPresent() { + return manager.getOptionalCache(cacheType).map(cache -> cache.contains(key)).orElse(false); + } - ValueBuilderGetValueStoreTempValueIntf valueSupplier( - ThrowingSupplier valueSupplier); + public boolean isExpiredTemporary() { + return manager.getOptionalCache(cacheType).map(cache -> cache.isTemporaryExpired(key)).orElse(false); + } - , S extends T, X extends Exception> ValueBuilderGetCollectionIntf collectionSupplier( - Class collectionValueType, ThrowingSupplier valueSupplier); + public boolean isTemporaryObject() { + return manager.getOptionalCache(cacheType).map(cache -> cache.isTemporaryObject(key)).orElse(false); + } - ValueBuilderGetOptionalStoreTempValueIntf optionalSupplier( - ThrowingSupplier, X> valueSupplier); + public Optional + + systems.manifold + manifold-science + ${manifold.version} + org.projectlombok lombok From 06593376d5ed9ba2a84f3109c96ab9441e174505 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 26 Apr 2025 16:31:37 +0200 Subject: [PATCH 42/58] fixes: why is this needed? --- .../lodder/subtools/sublibrary/data/imdb/ImdbAdapter.java | 4 +++- .../lodder/subtools/sublibrary/data/omdb/OmdbAdapter.java | 6 ++++-- .../subtools/sublibrary/data/tvdb/TheTvdbAdapter.java | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) 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 56ec109a..d6f311c4 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 @@ -9,6 +9,7 @@ import com.pivovarit.function.ThrowingBiFunction; import org.lodder.subtools.sublibrary.Manager; +import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.data.imdb.exception.ImdbException; @@ -64,13 +65,14 @@ public Optional getMovieDetails(int imdbId) { public OptionalInt getImdbId(String title, Integer year) { try { + // TODO don't include optional parameters return manager.getCache(CacheType.DISK, "%s-id-%s-%s".formatted(PROVIDER_NAME, title, year)) .getOptionalInt( () -> getImdbIdOnImdb(title, year) .orElseMap(() -> getImdbIdOnGoogle(title, year)) .orElseMap(() -> getImdbIdOnYahoo(title, year)) .orElseMap(() -> promptUserToEnterImdbId(title, year)), - storeTempNullValue:true); + Retry.NONE, null, true, false); } catch (Exception e) { LOGGER.error("API %s getImdbId for title [%s] (%s)".formatted(PROVIDER_NAME, title, e.getMessage()), e); return OptionalInt.empty(); 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 1073ee7f..d11cb699 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 @@ -4,6 +4,7 @@ import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Manager; +import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.omdb.model.OmdbDetails; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderInitException; @@ -36,10 +37,11 @@ private OmdbApi getApi() { } public Optional getMovieDetails(int imdbId) { try { + // TODO don't include optional parameters return manager.getCache(CacheType.DISK, "$providerName-movieDetails-$imdbId") .getOptional( - supplier:() -> getApi().getMovieDetails(imdbId), - storeTempNullValue:true); + () -> getApi().getMovieDetails(imdbId), + Retry.NONE, null, true, false); } catch (Exception e) { LOGGER.error("API $providerName getMovieDetails for id [$imdbId] (${e.getMessage()})", e); return Optional.empty(); 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 995416b4..01680c73 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 @@ -109,9 +109,10 @@ public Optional getSerie(String serieName) { } public Optional getEpisode(int tvdbId, int season, int episode) { + // TODO don't include optional parameters return manager.getCache(CacheType.DISK, "%s-episode-%s-%s-%s".formatted(PROVIDER_NAME, tvdbId, season, episode)) .getOptional( - supplier:() -> { + () -> { try { return getApi().getEpisode(tvdbId, season, episode, Language.ENGLISH); } catch (TheTvdbException e) { @@ -121,7 +122,7 @@ public Optional getEpisode(int tvdbId, int season, int episode) return Optional.empty(); } }, - storeTempNullValue:true); + Retry.NONE, null, true, false); } public static synchronized TheTvdbAdapter getInstance(Manager manager, From 3c48b470fba0a9adaf9768ca35d0a020b208af07 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Sat, 26 Apr 2025 19:46:14 +0200 Subject: [PATCH 43/58] changes --- .../subtools/sublibrary/data/tvdb/TheTvdbAdapter.java | 2 +- .../lodder/subtools/sublibrary/model/TvRelease.java | 10 +++++----- .../sublibrary/settings/model/SerieMapping.java | 10 +--------- 3 files changed, 7 insertions(+), 15 deletions(-) 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 01680c73..5586a64d 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 @@ -102,7 +102,7 @@ public Optional getSerie(String serieName) { manager.getCache(CacheType.DISK, "$PROVIDER_NAME-serieId-$encodedSerieName") .store( value:OptionalValue.of( - tvdbSerie.map(tvdbS -> new SerieMapping(serieName, tvdbS.id, tvdbS.serieName))), + tvdbSerie.map(tvdbS -> new SerieMapping(serieName, String.valueOf(tvdbS.id), tvdbS.serieName))), storeTempNullValue:true); } return tvdbSerie; 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 a83d085f..097c23d5 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 @@ -24,15 +24,15 @@ public final class TvRelease extends Release { // custom name which can be used to search subtitle providers @val String customName; - public TvRelease(Path file=null, String releaseGroup=null, String quality=null, String extension=null, String name, - String originalName=null, String customName=null, String title=null, int season, int episode, + public TvRelease(String name, int season, int episode, Path file=null, String releaseGroup=null, String quality= + null, String extension=null, String originalName=null, String customName=null, String title=null, boolean special=false) { - this(file, releaseGroup, quality, extension, name, originalName, customName, title, season, List.of(episode), + this(name, season, List.of(episode), file, releaseGroup, quality, extension, originalName, customName, title, special); } - public TvRelease(Path file=null, String releaseGroup=null, String quality=null, String extension=null, String name, - String originalName=null, String customName=null, String title=null, int season, List episodes, + public TvRelease(String name, int season, List episodes, Path file=null, String releaseGroup=null, + String quality=null, String extension=null, String originalName=null, String customName=null, String title=null, boolean special=false) { super(VideoType.EPISODE, file, releaseGroup, quality, extension); this.name = name; 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 6cd82a52..546de8d5 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 @@ -21,15 +21,7 @@ public class SerieMapping implements Serializable { // implements SerieMappingIn @val int season; @var transient String formattedName; - public SerieMapping(String name, int providerId, String providerName) { - this(name, providerId, providerName, 0); - } - - public SerieMapping(String name, int providerId, String providerName, int season) { - this(name, String.valueOf(providerId), providerName, season); - } - - public SerieMapping(String name, String providerId, String providerName, int season) { + public SerieMapping(String name, String providerId, String providerName, int season=0) { this.name = name; this.providerId = providerId; this.providerName = providerName; From 3d78cf9d4ac7313976f08dfa0ffbac2991237df9 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Mon, 28 Apr 2025 21:38:38 +0200 Subject: [PATCH 44/58] changes fix fix --- .../gui/dialog/MappingEpisodeNameDialog.java | 2 +- .../lib/control/MovieReleaseControl.java | 25 +++++----- .../subtitleproviders/Local.java | 5 +- .../subtitleproviders/adapters/Adapter.java | 47 +++++++++---------- .../adapters/JAddic7edAdapter.java | 7 +-- .../adapters/JAddic7edViaProxyAdapter.java | 14 +++--- .../adapters/JOpenSubAdapter.java | 20 +++++--- .../adapters/JPodnapisiAdapter.java | 8 ++-- .../adapters/JSubsceneAdapter.java | 6 ++- .../adapters/JTVsubtitlesAdapter.java | 7 +-- .../gestdown/JAddic7edProxyGestdownApi.java | 25 +++++----- .../podnapisi/JPodnapisiApi.java | 8 ++-- .../model/PodnapisiSubtitleDescriptor.java | 4 +- .../subscene/SubsceneApi.java | 8 ++-- .../model/SubsceneSubtitleDescriptor.java | 4 +- .../lodder/subtools/sublibrary/Manager.java | 26 +++++----- .../sublibrary/control/ReleaseParser.java | 3 +- .../data/imdb/model/ImdbDetails.java | 14 +++--- .../data/omdb/model/OmdbDetails.java | 13 ++--- .../sublibrary/data/tvdb/TheTvdbAdapter.java | 23 +++++---- .../sublibrary/model/MovieRelease.java | 27 ++--------- .../subtools/sublibrary/model/Release.java | 34 +++++++------- .../subtools/sublibrary/model/Subtitle.java | 47 ++++++++++--------- .../subtools/sublibrary/model/TvRelease.java | 26 +++++----- .../UserInteractionHandler.java | 3 +- .../UserInteractionHandlerCLI.java | 2 +- .../UserInteractionHandlerGUI.java | 3 +- .../sublibrary/util/CopyDirVisitor.java | 11 ++--- .../resourcebundle/Message.properties | 2 + .../resourcebundle/Message_nl.properties | 2 + 30 files changed, 206 insertions(+), 220 deletions(-) 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 4e97fffe..22a26d32 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 @@ -100,7 +100,7 @@ public MappingEpisodeNameDialog(JFrame frame, Manager manager, SubtitleProviderS String message = getText("MappingEpisodeNameDialog.enterNewNameForSerie", currentName); selectedSubtitleProvider.ifPresent(provider -> - userInteractionHandler.enter(message, message).ifPresent(newName -> { + userInteractionHandler.enter(message).ifPresent(newName -> { TvRelease tvRelease = new TvRelease( name:currentName, season:row.serieMapping.season, 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 6929705e..e837c242 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 @@ -1,7 +1,5 @@ package org.lodder.subtools.multisubdownloader.lib.control; -import java.util.Optional; - import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; @@ -35,20 +33,19 @@ public void process() throws ReleaseControlException { if (StringUtils.isBlank(movieRelease.name)) { throw new ReleaseControlException("Unable to extract/find title, check file", movieRelease); } else { - movieRelease.setImdbId(imdbAdapter.getImdbId(movieRelease.name, movieRelease.year) - .orElseThrow( - () -> new ReleaseControlException("Movie not found on IMDB, check file", movieRelease))); + movieRelease.imdbId = 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); + ReleaseDBIntf movieDetails = imdbAdapter.getMovieDetails(movieRelease.imdbId).orElse(null); + if (movieDetails == null) { + movieDetails = omdbAdapter.getMovieDetails(movieRelease.imdbId).orElse(null); + } + if (movieDetails != null) { + movieRelease.year = movieDetails.year; + movieRelease.name = movieDetails.name; + } else { + 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/subtitleproviders/Local.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/Local.java index b9e9812b..9d16c0bf 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 @@ -5,6 +5,7 @@ import java.nio.file.Path; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -73,7 +74,7 @@ public Set searchSubtitles(TvRelease tvRelease, Language language) { TvReleaseControl epCtrl = new TvReleaseControl((TvRelease) release, settings, manager, userInteractionHandler); epCtrl.process(); - if (((TvRelease) release).tvdbIdOptional.equals(tvRelease.tvdbIdOptional)) { + if (Objects.equals(release.tvdbId, tvRelease.tvdbId)) { Language detectedLang = DetectLanguage.execute(fileSub); if (detectedLang == language) { LOGGER.debug("Local Sub found, adding [{}]", fileSub); @@ -116,7 +117,7 @@ public Set searchSubtitles(MovieRelease movieRelease, Language languag MovieReleaseControl movieCtrl = new MovieReleaseControl(release, settings, manager, userInteractionHandler); movieCtrl.process(); - if (release.getImdbId().equals(movieRelease.getImdbId()) + if (Objects.equals(release.imdbId, movieRelease.imdbId) && DetectLanguage.execute(fileSub) == language) { LOGGER.debug("Local Sub found, adding {}", fileSub); listFoundSubtitles.add(new Subtitle( 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 9a9fce75..4605951d 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 @@ -10,12 +10,12 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.OptionalInt; import java.util.Set; import java.util.function.Function; import lombok.experimental.ExtensionMethod; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesHasher; @@ -30,6 +30,7 @@ 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.lazy.LazySupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,14 +65,14 @@ default Set searchSubtitles(MovieRelease movieRelease, Language langua } } } - movieRelease.getImdbId().ifPresent(imdbId -> { + if (movieRelease.imdbId != null) { try { - subtitles.addAll(searchMovieSubtitlesWithId(imdbId, language)); + subtitles.addAll(searchMovieSubtitlesWithId(movieRelease.imdbId, language)); } catch (Exception e) { LOGGER.error("API %s searchSubtitles using imdbid [%s] for movie [%s] (%s)".formatted( - subtitleSource.name, imdbId, movieRelease.name, e.getMessage()), e); + subtitleSource.name, movieRelease.imdbId, movieRelease.name, e.getMessage()), e); } - }); + } if (subtitles.isEmpty()) { try { subtitles.addAll(searchMovieSubtitlesWithName(movieRelease.name, movieRelease.year, language)); @@ -89,7 +90,7 @@ default Set searchSubtitles(MovieRelease movieRelease, Language langua Collection searchMovieSubtitlesWithId(int tvdbId, Language language) throws X; - Collection searchMovieSubtitlesWithName(String name, int year, Language language) throws X; + Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, Language language) throws X; @Override default Set searchSubtitles(TvRelease tvRelease, Language language) { @@ -107,7 +108,7 @@ default Set searchSubtitles(TvRelease tvRelease, Language language) { Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language); - List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) throws X; + List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws X; @Override default Optional getProviderSerieId(TvRelease tvRelease) throws X { @@ -127,21 +128,16 @@ default Optional getProviderSerieId(TvRelease tvRelease, Function< default Optional getProviderSerieId(TvRelease tvRelease, Function nameFunction, Function customNameFunction) throws X { return getProviderSerieId(nameFunction.apply(tvRelease), customNameFunction.apply(tvRelease), - tvRelease.displayName, tvRelease.season, tvRelease.getTvdbIdOptional()); - } - - default Optional getProviderSerieId(String serieName, String displayName, int season, - OptionalInt tvdbIdOptional) throws X { - return getProviderSerieId(serieName, serieName, displayName, season, tvdbIdOptional); + tvRelease.displayName, tvRelease.season, tvRelease.tvdbId); } default Optional getProviderSerieId(String serieName, String serieNameToSearchFor, String displayName, - int season, OptionalInt tvdbIdOptional) throws X { + int season, Integer tvdbId) throws X { - Function tvdbIdCacheFunction = tvdbId -> manager.getCache(CacheType.DISK, - "%s-serieName-tvdbId:%s-%s".formatted(providerName, tvdbId, useSeasonForSerieId() ? season : -1)); - if (tvdbIdOptional.isPresent()) { - CacheKey tvdbIdCache = tvdbIdCacheFunction.apply(tvdbIdOptional.orElseThrow()); + LazySupplier tvdbIdCacheFunction = new LazySupplier<>(() -> manager.getCache(CacheType.DISK, + "%s-serieName-tvdbId:%s-%s".formatted(providerName, tvdbId, useSeasonForSerieId() ? season : -1))); + if (tvdbId != null) { + CacheKey tvdbIdCache = tvdbIdCacheFunction.get(); if (tvdbIdCache.isPresent()) { // if value using the tvdbId is present, return it return tvdbIdCache.getOptional(); @@ -161,13 +157,14 @@ default Optional getProviderSerieId(String serieName, String serie } } else { Optional serieMapping = serieNameCache.getOptional(); - serieMapping.ifPresent(providerSerieName -> tvdbIdOptional.ifPresent( - tvdbId -> tvdbIdCacheFunction.apply(tvdbId).store(Value.of(providerSerieName)))); + if (tvdbId != null) { + serieMapping.map(Value::of).ifPresent(tvdbIdCacheFunction.get()::store); + } return serieMapping; } } - List providerSerieIds = getSortedProviderSerieIds(tvdbIdOptional, serieNameToSearchFor, seasonToUse); + List providerSerieIds = getSortedProviderSerieIds(tvdbId, serieNameToSearchFor, seasonToUse); if (providerSerieIds.isEmpty()) { // 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). @@ -219,9 +216,11 @@ default Optional getProviderSerieId(String serieName, String serie // create a serieMapping for the selected value serieMapping = new SerieMapping(serieName, uriForSerie.get().id, uriForSerie.get().name, seasonToUse); } - tvdbIdOptional.ifPresentOrElse( - tvdbId -> tvdbIdCacheFunction.apply(tvdbId).store(Value.of(serieMapping)), - () -> serieNameCache.store(Value.of(serieMapping))); + if (tvdbId != null) { + tvdbIdCacheFunction.get().store(Value.of(serieMapping)); + } else { + serieNameCache.store(Value.of(serieMapping)); + } return Optional.of(serieMapping); } 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 e4bb7de1..0f5d402c 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 @@ -3,7 +3,6 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.OptionalInt; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -12,6 +11,7 @@ import lombok.Getter; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.JAddic7edApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.exception.Addic7edException; @@ -73,7 +73,8 @@ public List searchMovieSubtitlesWithId(int tvdbId, L } @Override - public List searchMovieSubtitlesWithName(String name, int year, Language language) { + public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, + Language language) { // TODO implement this return List.of(); } @@ -120,7 +121,7 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + public List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws Addic7edException { return getApi().getProviderId(serieName) .stream() 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 912a9619..1a5a964e 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 @@ -4,7 +4,6 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; -import java.util.OptionalInt; import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; @@ -17,6 +16,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.gestdown.invoker.ApiException; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.proxy.gestdown.JAddic7edProxyGestdownApi; import org.lodder.subtools.sublibrary.Language; @@ -60,7 +60,7 @@ public Collection searchMovieSubtitlesWithId(int tvdbId, Language lang } @Override - public Collection searchMovieSubtitlesWithName(String name, int year, Language language) { + public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, Language language) { // TODO implement this return List.of(); } @@ -93,17 +93,17 @@ public Set searchSerieSubtitles(TvRelease tvRelease, Language language } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + public List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws ApiException { - List serieIds = tvdbIdOptional.mapToObj( - tvdbId -> new ExecuteCall<>(() -> getApi().getProviderSerieName(tvdbId)).message( - "getProviderSerieName: [$tvdbId]") + List serieIds = tvdbId == null ? List.of() : + 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); + .execute(); if (serieIds.isEmpty()) { serieIds = new ExecuteCall<>(() -> getApi().getProviderSerieName(serieName)).message( 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 647b5ad1..be1ec248 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 @@ -3,7 +3,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.OptionalInt; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -13,6 +13,7 @@ import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.exception.OpenSubtitlesException; @@ -73,8 +74,9 @@ public List searchMovieSubtitlesWithId(int tvd } @Override - public List searchMovieSubtitlesWithName(String name, int year, Language language) - throws OpenSubtitlesException { + public Collection searchMovieSubtitlesWithName(String name, + @Nullable Integer year, + Language language) throws OpenSubtitlesException { return getApi().searchSubtitles().query(name).language(language).searchSubtitles().getData(); } @@ -83,7 +85,10 @@ public Set convertToSubtitles(MovieRelease movieRelease, Set movieRelease.year == attributes.getFeatureDetails().getYear().intValue()) + .filter(attributes -> + attributes.getFeatureDetails().getYear() != null + ? Objects.equals(attributes.getFeatureDetails().getYear().intValue(), movieRelease.year) + : movieRelease.year == null) .flatMap(attributes -> attributes.getFiles().stream().map(file -> createSubtitle(file, attributes))) .collect(Collectors.toSet()); } @@ -141,13 +146,14 @@ private Subtitle createSubtitle(SubtitleAttributesFilesInner file, SubtitleAttri } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + public List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws OpenSubtitlesException { return getApi().getProviderSerieIds(serieName) .stream() - .sorted(Comparator.comparing((OpensubtitleSerieId n) -> !serieName.replaceAll("[^A-Za-z]", "") + .sorted( + Comparator.comparing((OpensubtitleSerieId n) -> !serieName.replaceAll("[^A-Za-z]", "") .equalsIgnoreCase(n.name.replaceAll("[^A-Za-z]", ""))) - .thenComparing(OpensubtitleSerieId::getYear, Comparator.reverseOrder())) + .thenComparing(OpensubtitleSerieId::getYear, Comparator.nullsLast(Comparator.reverseOrder()))) .toList(); } 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 420d2d50..9536a65d 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 @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.List; -import java.util.OptionalInt; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -10,6 +9,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.JPodnapisiApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.exception.PodnapisiException; @@ -65,8 +65,8 @@ public List searchMovieSubtitlesWithId(int tvdbId, } @Override - public List searchMovieSubtitlesWithName(String name, int year, Language language) - throws PodnapisiException { + public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, + Language language) throws PodnapisiException { return getApi().getMovieSubtitles(name, year, 0, 0, language); } @@ -116,7 +116,7 @@ private Set buildListSubtitles(Language language, Collection getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + public List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws PodnapisiException { return getApi().getPodnapisiShowName(serieName).stream().toList(); } 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 eee50bc8..d5add85e 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 @@ -17,6 +17,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.SubsceneApi; @@ -75,7 +76,8 @@ public List searchMovieSubtitlesWithId(int tvdbId, L } @Override - public List searchMovieSubtitlesWithName(String name, int year, Language language) { + public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, + Language language) { // TODO implement this return List.of(); } @@ -104,7 +106,7 @@ public Set searchSerieSubtitles(TvRelease tvRelease, } @Override - public List getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + public List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws SubsceneException { ToIntFunction providerTypeFunction = value -> switch (value) { case "TV-Serie" -> 1; 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 a1576634..b9dc530a 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 @@ -3,7 +3,6 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.OptionalInt; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -13,6 +12,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.JTVSubtitlesApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.exception.TvSubtitlesException; @@ -70,7 +70,8 @@ public List searchMovieSubtitlesWithId(int tvdbId } @Override - public List searchMovieSubtitlesWithName(String name, int year, Language language) { + public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, + Language language) { // TODO implement this return List.of(); } @@ -117,7 +118,7 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection getSortedProviderSerieIds(OptionalInt tvdbIdOptional, String serieName, int season) + public List getSortedProviderSerieIds(@Nullable Integer tvdbId, String serieName, int season) throws TvSubtitlesException { Pattern yearPatter = Pattern.compile("\\((\\d\\d\\d\\d)-(\\d\\d\\d\\d)\\)"); return getApi().getUrisForSerieName(serieName) 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 2266dc90..7ce420ef 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 @@ -1,9 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.proxy.gestdown; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import extensions.java.lang.String.StringExt; import manifold.ext.props.rt.api.override; @@ -43,29 +43,26 @@ public JAddic7edProxyGestdownApi(Manager manager) { public List getProviderSerieName(String serieName) throws ApiException { return tvShowsApi.showsSearchSearchGet(serieName).getShows().stream() - .map(showDto -> new ProviderSerieId(showDto.getName(), showDto.getId().toString())).toList(); + .map(showDto -> new ProviderSerieId(showDto.getName(), showDto.getId().toString())).toList(); } public List getProviderSerieName(int tvdbId) throws ApiException { return tvShowsApi.showsExternalTvdbTvdbIdGet(tvdbId).getShows().stream() - .map(showDto -> new ProviderSerieId(showDto.getName(), showDto.getId().toString())).toList(); + .map(showDto -> new ProviderSerieId(showDto.getName(), showDto.getId().toString())).toList(); } public Set getSubtitles(SerieMapping providerSerieId, int season, int episode, Language language) - throws ApiException { + throws ApiException { return manager.getCache(CacheType.MEMORY, "%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), providerSerieId.providerId, season, episode, language)) .getCollection(() -> { - Set results = new HashSet<>(); - SubtitleSearchResponse response = - subtitlesApi.subtitlesGetShowUniqueIdSeasonEpisodeLanguageGet(language.getName(), - UUID.fromString(providerSerieId.providerId), season, episode); - response.getMatchingSubtitles() - .stream() - .filter(SubtitleDto::isCompleted) - .map(sub -> mapToSubtitle(sub, response.episode, language)) - .forEach(results::add); - return results; + SubtitleSearchResponse response = subtitlesApi.subtitlesGetShowUniqueIdSeasonEpisodeLanguageGet( + language.getName(), UUID.fromString(providerSerieId.providerId), season, episode); + return response.getMatchingSubtitles() + .stream() + .filter(SubtitleDto::isCompleted) + .map(sub -> mapToSubtitle(sub, response.episode, language)) + .collect(Collectors.toSet()); }); } 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 08e9e71e..664a808a 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 @@ -48,8 +48,8 @@ public Optional getPodnapisiShowName(String showName) throws Po Optional.of(new ProviderSerieId(showName, showName)) : Optional.empty(); } - public List getMovieSubtitles(String movieName, int year, int season, int episode, - Language language) throws PodnapisiException { + public List getMovieSubtitles(String movieName, @Nullable Integer year, int season, + int episode, Language language) throws PodnapisiException { return getSubtitles(new SerieMapping(movieName, movieName, movieName, season), year, season, episode, language); } @@ -60,8 +60,8 @@ public List getSerieSubtitles(SerieMapping provider } - private List getSubtitles(SerieMapping providerSerieId, Integer year, int season, - int episode, Language language) throws PodnapisiException { + private List getSubtitles(SerieMapping providerSerieId, @Nullable Integer year, + int season, int episode, Language language) throws PodnapisiException { return manager.getCache(CacheType.MEMORY, "%s-subtitles-%s-%s-%s-%s".formatted(subtitleSource.name(), providerSerieId.providerId, season, episode, language)) 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 4431b5a2..80f3519f 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,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.model; +import java.io.Serializable; + import lombok.Builder; import manifold.ext.props.rt.api.var; import org.lodder.subtools.sublibrary.Language; @@ -9,7 +11,7 @@ * Templates. */ @Builder -public class PodnapisiSubtitleDescriptor { +public class PodnapisiSubtitleDescriptor implements Serializable { @var String subtitleId; @var Language language; 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 e838ac3a..4a53cd49 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 @@ -4,7 +4,6 @@ import static org.lodder.subtools.sublibrary.util.Sleep.*; import java.io.Serial; -import java.time.LocalDateTime; import java.util.Collections; import java.util.EnumMap; import java.util.List; @@ -58,7 +57,7 @@ public class SubsceneApi implements SubtitleApi { private int selectedLanguage; private boolean selectedIncludeHearingImpaired; - private LocalDateTime lastRequest = LocalDateTime.now(); + private Time lastRequest = Time.now(); @val @override SubtitleSource subtitleSource = SubtitleSource.SUBSCENE; public SubsceneApi(Manager manager) { @@ -124,8 +123,7 @@ public List getSubtitles(SerieMapping providerSerieI } catch (Exception e) { throw new SubsceneException(e); } - }) - .getCollection(); + }); } private String getDownloadUrl(String seriePageUrl) throws SubsceneException { @@ -151,7 +149,7 @@ private Document getJsoupDocument(String url) throws ManagerException { url:url, userAgent:"", retry:new Retry(1, RETRY_PREDICATE, RATE_DURATION_LONG))); - lastRequest = LocalDateTime.now(); + lastRequest = Time.now(); return document; } 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 3e01dd14..14073c58 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,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.model; +import java.io.Serializable; + import com.pivovarit.function.ThrowingSupplier; import lombok.EqualsAndHashCode; import manifold.ext.props.rt.api.val; @@ -8,7 +10,7 @@ import org.lodder.subtools.sublibrary.model.SeasonEpisode; @EqualsAndHashCode -public class SubsceneSubtitleDescriptor { +public class SubsceneSubtitleDescriptor implements Serializable { @val Language language; @val String name; 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 fffbbb64..a146ae6e 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -66,33 +66,30 @@ public void storeCookies(String domain, Map cookieMap) { // POST \\ // ==== \\ - public PostBuilder postBuilder(String url, String userAgent=null) { + public PostBuilder postBuilder(String url, @Nullable String userAgent=null) { return new PostBuilder(httpClient, url, userAgent); } public static class PostBuilder { @val HttpClient httpClient; @val String url; - @val String userAgent; - private Map data; + @val @Nullable String userAgent; + private Map data = new HashMap<>(); - public PostBuilder(HttpClient httpClient, String url, String userAgent=null) { + public PostBuilder(HttpClient httpClient, String url, @Nullable String userAgent=null) { this.httpClient = httpClient; this.url = url; this.userAgent = userAgent; } public PostBuilder addData(String key, String value) { - if (data == null) { - data = new HashMap<>(); - } data.put(key, value); return this; } public String post() throws ManagerException { try { - return httpClient.doPost(new URI(url).toURL(), userAgent, data == null ? new HashMap<>() : data); + return httpClient.doPost(new URI(url).toURL(), userAgent, data); } catch (MalformedURLException | URISyntaxException e) { throw new ManagerException("incorrect url", e); } catch (HttpClientException e) { @@ -122,19 +119,20 @@ public InputStream getAsInputStream(PageContentParams params) throws ManagerExce return get(params).toInputStream(StandardCharsets.UTF_8); } - public @Nullable Document getAsDocument(PageContentParams params, Predicate emptyResultPredicate=null) + public @Nullable Document getAsDocument(PageContentParams params, + @Nullable Predicate emptyResultPredicate=null) throws ParserConfigurationException, ManagerException, IOException { Optional asStringDocument = getAsStringDocument(params, emptyResultPredicate); return asStringDocument.isPresent() ? XMLHelper.getDocument(asStringDocument.get()) : null; } public org.jsoup.nodes.Document getAsJsoupDocument(PageContentParams params, - Predicate emptyResultPredicate=null) throws ManagerException { + @Nullable Predicate emptyResultPredicate=null) throws ManagerException { return getAsStringDocument(params, emptyResultPredicate).map(Jsoup::parse).orElse(null); } private Optional getAsStringDocument(PageContentParams params, - Predicate emptyResultPredicate) throws ManagerException { + @Nullable Predicate emptyResultPredicate) throws ManagerException { if (emptyResultPredicate == null) { return Optional.of(get(params)); } @@ -268,7 +266,7 @@ public Optional getOptional() { } public Optional getOptional( - ThrowingSupplier, X> supplier, Retry retry=Retry.NONE, Time timeToLive=null, + ThrowingSupplier, X> supplier, Retry retry=Retry.NONE, @Nullable Time timeToLive=null, boolean storeTempNullValue=false, boolean storeAsTempValue=false) throws X { Optional> optionalCache = manager.getOptionalCache(cacheType); @@ -296,7 +294,7 @@ public Optional getOptional( } public OptionalInt getOptionalInt( - ThrowingSupplier supplier, Retry retry=Retry.NONE, Time timeToLive=null, + ThrowingSupplier supplier, Retry retry=Retry.NONE, @Nullable Time timeToLive=null, boolean storeTempNullValue=false, boolean storeAsTempValue=false) throws X { return manager.getOptionalCache(cacheType).mapThrowing(cache -> { @@ -321,7 +319,7 @@ public OptionalInt getOptionalInt( public void store(ValueIntf value, Retry retry=Retry.NONE, boolean storeAsTempValue=false, boolean storeTempNullValue=false, - Time timeToLive=null) throws X { + @Nullable Time timeToLive=null) throws X { V object = value.supplier != null ? executeSupplier(value.supplier, retry) : value.value; Time ttl = null; 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 44ca6d56..38a4c5a5 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 @@ -21,6 +21,7 @@ import lombok.AllArgsConstructor; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.control.Roman.RomanNumeral; import org.lodder.subtools.sublibrary.control.VideoPatterns.AudioEncoding; import org.lodder.subtools.sublibrary.control.VideoPatterns.Quality; @@ -368,7 +369,7 @@ public boolean containsNone(Tag... tags) { } @SafeVarargs - public final T getNamedMatchValue(Tag... tags) { + public final @Nullable T getNamedMatchValue(Tag... tags) { for (Tag tag : tags) { List namedMatch = getNamedMatch(tag, tag.mapper); if (!namedMatch.isEmpty()) { 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 57bdba5d..6da9e634 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 @@ -1,19 +1,17 @@ package org.lodder.subtools.sublibrary.data.imdb.model; -import lombok.AllArgsConstructor; -import manifold.ext.props.rt.api.override; -import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.ReleaseDBIntf; -@AllArgsConstructor -public class ImdbDetails implements ReleaseDBIntf { - - @val String title; - @val @override int year; +public record ImdbDetails(String title, int year) implements ReleaseDBIntf { @Override public String getName() { return title; } + + @Override + public int getYear() { + return year; + } } 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 1d882815..12807ff5 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 @@ -2,21 +2,18 @@ import java.io.Serial; -import lombok.AllArgsConstructor; -import manifold.ext.props.rt.api.override; -import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.data.ReleaseDBIntf; -@AllArgsConstructor -public class OmdbDetails implements ReleaseDBIntf { +public record OmdbDetails(String title, int year) implements ReleaseDBIntf { @Serial private static final long serialVersionUID = 7701770682134890544L; - @val String title; - @val @override int year; - @Override public String getName() { return title; } + + @Override public int getYear() { + 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 5586a64d..4a43833a 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 @@ -4,7 +4,6 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import static org.lodder.subtools.sublibrary.Manager.*; -import javax.swing.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Comparator; @@ -12,6 +11,7 @@ import java.util.Optional; import java.util.OptionalInt; +import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; @@ -84,6 +84,7 @@ public Optional getSerie(String serieName) { PROVIDER_NAME, s -> "${s.serieName} (${s.firstAired})"); if (tvdbSerie.isEmpty()) { + LOGGER.error("Unknown serie name in tvdb: $serieName"); tvdbSerie = askUserToEnterTvdbId(serieName) .mapToObj(tvdbId -> api.getSerie(tvdbId, null).orElse(null)); } @@ -134,16 +135,14 @@ public static synchronized TheTvdbAdapter getInstance(Manager manager, } private OptionalInt askUserToEnterTvdbId(String 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"); - return askUserToEnterTvdbId(showName); - } + return userInteractionHandler.enter(Messages.getText("InputPanel.EnterTvdbId", showName)) + .map(tvdbidString -> { + try { + return OptionalInt.of(Integer.parseInt(tvdbidString)); + } catch (NumberFormatException e) { + LOGGER.error(Messages.getText("InputPanel.invalid.tvdbid", tvdbidString)); + return askUserToEnterTvdbId(showName); + } + }).orElseGet(OptionalInt::empty); } } 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 e9b47267..c92e51f7 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 @@ -1,19 +1,18 @@ package org.lodder.subtools.sublibrary.model; import java.nio.file.Path; -import java.util.OptionalInt; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.Nullable; public final class MovieRelease extends Release { @var String name; - @var Integer year; - private int imdbId; - private int tvdbId; + @var @Nullable Integer year; + @var @Nullable Integer imdbId; - public MovieRelease(String name, Path file=null, String releaseGroup=null, String quality=null, - String extension=null, Integer year=null) { + public MovieRelease(String name, @Nullable Path file=null, @Nullable String releaseGroup=null, + @Nullable String quality=null, @Nullable String extension=null, @Nullable Integer year=null) { super(VideoType.MOVIE, file, releaseGroup, quality, extension); this.name = name; this.year = year; @@ -23,22 +22,6 @@ public String getImdbIdAsString() { 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 "${getClass().getSimpleName()}: $name ${quality} ${releaseGroup}"; 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 400b7d33..61f60fc0 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,16 +6,28 @@ import java.util.Set; import manifold.ext.props.rt.api.val; +import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; public abstract sealed class Release permits MovieRelease, TvRelease { private final Set matchingSubsSet = new HashSet<>(); @val VideoType videoType; - @val Path filePath; - @val String quality; - @val String releaseGroup; - @val String extension; + @val @Nullable Path filePath; + @val @Nullable String quality; + @val @Nullable String releaseGroup; + @val @Nullable String extension; + @var @Nullable Integer tvdbId; + + protected Release(VideoType videoType, @Nullable Path filePath, @Nullable String releaseGroup, + @Nullable String quality, @Nullable String extension) { + this.videoType = videoType; + this.filePath = filePath; + this.releaseGroup = releaseGroup; + this.quality = quality; + this.extension = extension; + } public void addMatchingSub(Subtitle sub) { matchingSubsSet.add(sub); @@ -29,14 +41,6 @@ public int getMatchingSubCount() { return matchingSubsSet.size(); } - protected Release(VideoType videoType, Path filePath, String releaseGroup, String quality, String extension) { - this.videoType = videoType; - this.filePath = filePath; - this.releaseGroup = releaseGroup; - this.quality = quality; - this.extension = extension; - } - public String getFileName() { return filePath != null ? filePath.getFileName().toString() : null; } @@ -45,12 +49,8 @@ public Path getPath() { return filePath != null ? filePath.getParent() : null; } - public String getExtension() { - return extension; - } - public boolean hasExtension(String extension) { - return StringUtils.isNotBlank(extension); + return StringUtils.isNotBlank(extension) && extension.equals(this.extension); } @Override 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 1d5d2486..1f41188d 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 @@ -1,5 +1,6 @@ package org.lodder.subtools.sublibrary.model; +import java.io.Serializable; import java.nio.file.Path; import com.pivovarit.function.ThrowingSupplier; @@ -7,32 +8,33 @@ import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.builder.EqualsExclude; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; @EqualsAndHashCode -public class Subtitle { +public class Subtitle implements Serializable { @val DownloadSource downloadSource; - @var String fileName; - @var Language language; - @var String releaseGroup; - @var String uploader; - @var SubtitleMatchType subtitleMatchType; - @var SubtitleSource subtitleSource; + @var @Nullable String fileName; + @var @Nullable Language language; + @var @Nullable String releaseGroup; + @var @Nullable String uploader; + @var @Nullable SubtitleMatchType subtitleMatchType; + @var @Nullable SubtitleSource subtitleSource; @var boolean hearingImpaired; - @var String quality; + @var @Nullable String quality; @var int score; public Subtitle(DownloadSource downloadSource, - String fileName=null, - Language language=null, - String releaseGroup=null, - String uploader=null, - SubtitleMatchType subtitleMatchType=null, - SubtitleSource subtitleSource=null, + @Nullable String fileName=null, + @Nullable Language language=null, + @Nullable String releaseGroup=null, + @Nullable String uploader=null, + @Nullable SubtitleMatchType subtitleMatchType=null, + @Nullable SubtitleSource subtitleSource=null, boolean hearingImpaired=false, - String quality=null, + @Nullable String quality=null, int score=0) { this.downloadSource = downloadSource; this.fileName = fileName; @@ -49,15 +51,15 @@ public Subtitle(DownloadSource downloadSource, @EqualsAndHashCode public static class DownloadSource { @val SourceLocation sourceLocation; - @EqualsExclude @val ThrowingSupplier urlSupplier; - @val String url; - @val Path file; + @EqualsExclude @val @Nullable ThrowingSupplier urlSupplier; + @val @Nullable String url; + @val @Nullable Path file; private DownloadSource( SourceLocation sourceLocation, - ThrowingSupplier urlSupplier=null, - String url=null, - Path file=null) { + @Nullable ThrowingSupplier urlSupplier=null, + @Nullable String url=null, + @Nullable Path file=null) { this.urlSupplier = urlSupplier; this.url = url; @@ -77,7 +79,8 @@ public static DownloadSource of(String url) { public static DownloadSource of(Path file) { return new DownloadSource(SourceLocation.FILE, file:file); } - + + @SuppressWarnings("ConstantConditions") public String getValue() throws SubtitlesProviderException { return switch (sourceLocation) { case FILE -> file.toString(); 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 097c23d5..dfa96421 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 @@ -3,36 +3,36 @@ import java.nio.file.Path; import java.util.Collections; import java.util.List; -import java.util.OptionalInt; import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.tvdb.model.TheTvdbEpisode; public final class TvRelease extends Release { // parsed from the filename @val String name; - @val List episodes; @val int season; - @var String title; - @var int tvdbId; + @val List episodes; + @var @Nullable String title; // tvdb name - @var String originalName; + @var @Nullable String originalName; @val boolean special; // custom name which can be used to search subtitle providers - @val String customName; + @val @Nullable String customName; - public TvRelease(String name, int season, int episode, Path file=null, String releaseGroup=null, String quality= - null, String extension=null, String originalName=null, String customName=null, String title=null, - boolean special=false) { + public TvRelease(String name, int season, int episode, @Nullable Path file=null, @Nullable String releaseGroup=null, + @Nullable String quality=null, @Nullable String extension=null, @Nullable String originalName=null, + @Nullable String customName=null, @Nullable String title=null, boolean special=false) { this(name, season, List.of(episode), file, releaseGroup, quality, extension, originalName, customName, title, special); } - public TvRelease(String name, int season, List episodes, Path file=null, String releaseGroup=null, - String quality=null, String extension=null, String originalName=null, String customName=null, String title=null, + public TvRelease(String name, int season, List episodes, @Nullable Path file=null, + @Nullable String releaseGroup=null, @Nullable String quality=null, @Nullable String extension=null, + @Nullable String originalName=null, @Nullable String customName=null, @Nullable String title=null, boolean special=false) { super(VideoType.EPISODE, file, releaseGroup, quality, extension); this.name = name; @@ -60,10 +60,6 @@ public void updateTvdbEpisodeInfo(TheTvdbEpisode tvdbEpisode) { this.title = tvdbEpisode.episodeName; // update to reflect correct episode title } - public OptionalInt getTvdbIdOptional() { - return tvdbId == 0 ? OptionalInt.empty() : OptionalInt.of(tvdbId); - } - public int getFirstEpisode() { return episodes.first; } 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 bb91b7ee..e63ea8b7 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 @@ -19,7 +19,8 @@ public interface UserInteractionHandler { Optional selectFromList(Iterable options, String message, @Nullable String title=null, @Nullable Function toStringMapper=null); - Optional enter(String title, String message, @Nullable List> inputValidators=null); + Optional enter(String message, @Nullable String title=null, + @Nullable List> inputValidators=null); OptionalInt enterNumber(String title, String message, @Nullable List> objectValidators=null); 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 4465ed88..7a508f37 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 @@ -50,7 +50,7 @@ public boolean confirm(String message, String title) { } @Override - public Optional enter(String title, String message, + public Optional enter(String message, @Nullable String title, @Nullable List> inputValidators) { // TODO use extension method return PrompterExt.promptString(prompter, message, inputValidators:inputValidators); 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 38f95884..501ec0d9 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 @@ -46,7 +46,8 @@ public boolean confirm(String message, String title) { } @Override - public Optional enter(String title, String message, @Nullable List> inputValidators) { + public Optional enter(String message, @Nullable String title, + @Nullable List> inputValidators) { synchronized (LOCK) { return new InputPane<>( title:title, 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 69d80a92..ee7b3d82 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 @@ -8,20 +8,19 @@ import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; -import lombok.RequiredArgsConstructor; import org.jspecify.annotations.Nullable; -@RequiredArgsConstructor public class CopyDirVisitor extends SimpleFileVisitor { private final Path fromPath; - private final Path toPath; - private final StandardCopyOption[] copyOptions; - public CopyDirVisitor(Path fromPath, Path toPath) { - this(fromPath, toPath, new StandardCopyOption[]{ StandardCopyOption.REPLACE_EXISTING }); + public CopyDirVisitor(Path fromPath, Path toPath, + StandardCopyOption[] copyOptions=new StandardCopyOption[]{StandardCopyOption.REPLACE_EXISTING}) { + this.fromPath = fromPath; + this.toPath = toPath; + this.copyOptions = copyOptions; } @Override diff --git a/SubLibrary/src/main/resources/resourcebundle/Message.properties b/SubLibrary/src/main/resources/resourcebundle/Message.properties index 2d870b75..43cf25e6 100644 --- a/SubLibrary/src/main/resources/resourcebundle/Message.properties +++ b/SubLibrary/src/main/resources/resourcebundle/Message.properties @@ -117,6 +117,8 @@ InputPanel.UpdateInterval.Monthly=Monthly InputPanel.UpdateInterval.Weekly=Weekly InputPanel.UpdateType.Stable=Stable InputPanel.UpdateType.Nightly=Nightly +InputPanel.EnterTvdbId="Enter tvdb id for serie %s +InputPanel.invalid.tvdbid=Invalid tvdb id: %s LoggingPanel.RateLimitReached=Rate limit reached for %s, please wait %s seconds LoggingPanel.UpdateCheckFailed=Update check failed MainWindow.CurrentVersion=current version diff --git a/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties b/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties index f124d4b4..c66c66f0 100644 --- a/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties +++ b/SubLibrary/src/main/resources/resourcebundle/Message_nl.properties @@ -117,6 +117,8 @@ InputPanel.UpdateInterval.Monthly=Maandelijks InputPanel.UpdateInterval.Weekly=Wekelijks InputPanel.UpdateType.Stable=Stabiel InputPanel.UpdateType.Nightly=Nightly +InputPanel.EnterTvdbId=Voor tvdb id in voor serie %s +InputPanel.invalid.tvdbid=Ongeldig tvdb id: %s LoggingPanel.RateLimitReached=Rate limiet is bereikt voor %s, gelieve %s sec te wachten LoggingPanel.UpdateCheckFailed=Updatecontrole mislukt MainWindow.CurrentVersion=huidige versie From 5004db1af5ac0e92fdf7af8e827cd9f820083af1 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Mon, 28 Apr 2025 21:38:46 +0200 Subject: [PATCH 45/58] update manifold --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a297b553..a2eeb11a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ UTF-8 - 2025.1.11 + 2025.1.12 1.18.38 24 24 From 87f90f0e816c722eb247bb1d928c4912822bb645 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Mon, 28 Apr 2025 21:59:36 +0200 Subject: [PATCH 46/58] fix --- .../lodder/subtools/sublibrary/data/imdb/ImdbAdapter.java | 4 +--- .../lodder/subtools/sublibrary/data/omdb/OmdbAdapter.java | 6 ++---- .../subtools/sublibrary/data/tvdb/TheTvdbAdapter.java | 5 ++--- 3 files changed, 5 insertions(+), 10 deletions(-) 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 d6f311c4..56ec109a 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 @@ -9,7 +9,6 @@ import com.pivovarit.function.ThrowingBiFunction; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; import org.lodder.subtools.sublibrary.data.imdb.exception.ImdbException; @@ -65,14 +64,13 @@ public Optional getMovieDetails(int imdbId) { public OptionalInt getImdbId(String title, Integer year) { try { - // TODO don't include optional parameters return manager.getCache(CacheType.DISK, "%s-id-%s-%s".formatted(PROVIDER_NAME, title, year)) .getOptionalInt( () -> getImdbIdOnImdb(title, year) .orElseMap(() -> getImdbIdOnGoogle(title, year)) .orElseMap(() -> getImdbIdOnYahoo(title, year)) .orElseMap(() -> promptUserToEnterImdbId(title, year)), - Retry.NONE, null, true, false); + storeTempNullValue:true); } catch (Exception e) { LOGGER.error("API %s getImdbId for title [%s] (%s)".formatted(PROVIDER_NAME, title, e.getMessage()), e); return OptionalInt.empty(); 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 d11cb699..1073ee7f 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 @@ -4,7 +4,6 @@ import manifold.ext.props.rt.api.val; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.Manager.Retry; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.omdb.model.OmdbDetails; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderInitException; @@ -37,11 +36,10 @@ private OmdbApi getApi() { } public Optional getMovieDetails(int imdbId) { try { - // TODO don't include optional parameters return manager.getCache(CacheType.DISK, "$providerName-movieDetails-$imdbId") .getOptional( - () -> getApi().getMovieDetails(imdbId), - Retry.NONE, null, true, false); + supplier:() -> getApi().getMovieDetails(imdbId), + storeTempNullValue:true); } catch (Exception e) { LOGGER.error("API $providerName getMovieDetails for id [$imdbId] (${e.getMessage()})", e); return Optional.empty(); 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 4a43833a..6224dcad 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 @@ -110,10 +110,9 @@ public Optional getSerie(String serieName) { } public Optional getEpisode(int tvdbId, int season, int episode) { - // TODO don't include optional parameters return manager.getCache(CacheType.DISK, "%s-episode-%s-%s-%s".formatted(PROVIDER_NAME, tvdbId, season, episode)) .getOptional( - () -> { + supplier:() -> { try { return getApi().getEpisode(tvdbId, season, episode, Language.ENGLISH); } catch (TheTvdbException e) { @@ -123,7 +122,7 @@ public Optional getEpisode(int tvdbId, int season, int episode) return Optional.empty(); } }, - Retry.NONE, null, true, false); + storeTempNullValue:true); } public static synchronized TheTvdbAdapter getInstance(Manager manager, From 5b9c46b19520dd09a8221a2c4273f5f99a2deacc Mon Sep 17 00:00:00 2001 From: EotT123 Date: Mon, 28 Apr 2025 23:04:30 +0200 Subject: [PATCH 47/58] changes changes --- .../science/measures/Time/TimeExt.java | 8 ++++ .../subtools/sublibrary/cache/Cache.java | 5 ++- .../subtools/sublibrary/cache/DiskCache.java | 9 +++-- .../sublibrary/cache/ExpiringCacheObject.java | 9 +++-- .../cache/TemporaryCacheObject.java | 5 +-- .../sublibrary/data/imdb/ImdbAdapter.java | 17 ++++---- .../sublibrary/data/imdb/ImdbSearchIdApi.java | 7 ++-- .../data/tvdb/model/TheTvdbEpisode.java | 39 ++++++++++--------- .../data/tvdb/model/TheTvdbSerie.java | 33 ++++++++-------- .../subtools/sublibrary/gui/InputPane.java | 9 ++--- .../settings/model/SerieMapping.java | 2 +- .../sublibrary/util/http/HttpClient.java | 6 +-- 12 files changed, 80 insertions(+), 69 deletions(-) diff --git a/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java b/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java index 84fb2e6b..c65db7ed 100644 --- a/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java +++ b/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java @@ -19,6 +19,14 @@ public static boolean isNegative(@This Time time) { return time.value.numerator.compareTo(BigInteger.ZERO) < 0; } + public static boolean isBefore(@This Time time, Time other) { + return time.compareTo(other) < 0; + } + + public static boolean isAfter(@This Time time, Time other) { + return time.compareTo(other) > 0; + } + @Extension public static Time create(Number duration, TimeUnit unit) { return new Time(Rational.get(duration), unit); 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 de9a3a5b..fc55a107 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 @@ -14,16 +14,17 @@ import manifold.science.measures.Time; import name.falgout.jeffrey.throwing.ThrowingSupplier; import org.apache.commons.lang3.tuple.Pair; +import org.jspecify.annotations.Nullable; public abstract sealed class Cache permits DiskCache, InMemoryCache { @val(Protected) final Map> cacheMap; - protected Cache(Integer maxItems=null) { + protected Cache(@Nullable Integer maxItems=null) { this.cacheMap = maxItems != null ? new LRUMap<>(maxItems) : new HashMap<>(); } - public void put(K key, V value, Time timeToLive=null) { + public void put(K key, V value, @Nullable Time timeToLive=null) { if (timeToLive == null) { put(key, new ExpiringCacheObject<>(value)); } else { 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 577da3c9..5b9accac 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 @@ -24,6 +24,7 @@ import com.google.common.collect.MultimapBuilder; import manifold.science.measures.Time; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.util.lazy.LazyQuadFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -118,9 +119,9 @@ public final class DiskCache ext public DiskCache( Class dbKeyType, Class dbValueType, - Time timeToLive=null, - Integer maxItems=null, - String tableName=null) { + @Nullable Time timeToLive=null, + @Nullable Integer maxItems=null, + @Nullable String tableName=null) { super(maxItems); if (timeToLive != null && timeToLive.isNegative()) { @@ -179,7 +180,7 @@ private void removeFromDisk(K key) { } @Override - public void put(K key, V value, Time timeToLive) { + public void put(K key, V value, @Nullable Time timeToLive) { synchronized (LOCK) { super.put(key, value, timeToLive); putFromMemoryCache(key); 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 613df6a3..d86ded1a 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 @@ -1,5 +1,6 @@ package org.lodder.subtools.sublibrary.cache; +import static manifold.ext.props.rt.api.PropOption.*; import static manifold.science.util.UnitConstants.*; import java.io.Serial; @@ -12,6 +13,7 @@ import lombok.AllArgsConstructor; import lombok.ToString; import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.set; import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; import manifold.science.measures.Time; @@ -24,12 +26,11 @@ sealed class ExpiringCacheObject implements CacheObject permits ExpiringSe private static final long serialVersionUID = 3852086993086134232L; private static final Pattern PATTERN = Pattern.compile("created:(.*?)|lastAccessed:(.*?)|value:(.*)"); - @override @val Time created; - @var Time lastAccessed = Time.now(); + @override @val Time created = Time.now(); + @var @set(Private) Time lastAccessed = Time.now(); @override @var T value; protected ExpiringCacheObject(T value) { - this.created = Time.now(); this.value = value; } @@ -40,7 +41,7 @@ public void updateLastAccessed() { @Override public boolean isExpired(Time ttl) { - return Time.now().compareTo(lastAccessed + ttl) > 0; + return Time.now().isAfter(lastAccessed + ttl); } @Override 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 0809cb30..c4b6589d 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 @@ -22,12 +22,11 @@ sealed class TemporaryCacheObject implements CacheObject permits Temporary @Serial private static final long serialVersionUID = -152474119228350222L; private static final Pattern PATTERN = Pattern.compile("created:(.*?)|expire:(.*?)|value:(.*)"); - @override @val Time created; + @override @val Time created = Time.now(); @val Time timeToLive; @override @val T value; protected TemporaryCacheObject(Time timeToLive, T value) { - this.created = Time.now(); this.timeToLive = timeToLive; this.value = value; } @@ -38,7 +37,7 @@ public boolean isExpired(Time ttl) { } public boolean isExpired() { - return Time.now().compareTo(created + timeToLive) > 0; + return Time.now().isAfter(created + timeToLive); } 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 56ec109a..ac5a7a81 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 @@ -8,6 +8,7 @@ import java.util.OptionalInt; import com.pivovarit.function.ThrowingBiFunction; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; @@ -62,14 +63,14 @@ public Optional getMovieDetails(int imdbId) { }); } - public OptionalInt getImdbId(String title, Integer year) { + public OptionalInt getImdbId(String title, @Nullable Integer year) { try { return manager.getCache(CacheType.DISK, "%s-id-%s-%s".formatted(PROVIDER_NAME, title, year)) .getOptionalInt( () -> getImdbIdOnImdb(title, year) .orElseMap(() -> getImdbIdOnGoogle(title, year)) .orElseMap(() -> getImdbIdOnYahoo(title, year)) - .orElseMap(() -> promptUserToEnterImdbId(title, year)), + .orElseMap(() -> promptUserToEnterImdbId(title)), storeTempNullValue:true); } catch (Exception e) { LOGGER.error("API %s getImdbId for title [%s] (%s)".formatted(PROVIDER_NAME, title, e.getMessage()), e); @@ -77,19 +78,19 @@ public OptionalInt getImdbId(String title, Integer year) { } } - private OptionalInt getImdbIdOnImdb(String title, Integer year) { + private OptionalInt getImdbIdOnImdb(String title, @Nullable Integer year) { return getImdbIdCommon(title, year, imdbSearchIdApi.get()::getImdbIdOnImdb); } - private OptionalInt getImdbIdOnGoogle(String title, Integer year) { + private OptionalInt getImdbIdOnGoogle(String title, @Nullable Integer year) { return getImdbIdCommon(title, year, imdbSearchIdApi.get()::getImdbIdOnGoogle); } - private OptionalInt getImdbIdOnYahoo(String title, Integer year) { + private OptionalInt getImdbIdOnYahoo(String title, @Nullable Integer year) { return getImdbIdCommon(title, year, imdbSearchIdApi.get()::getImdbIdOnYahoo); } - private OptionalInt getImdbIdCommon(String title, Integer year, + private OptionalInt getImdbIdCommon(String title, @Nullable Integer year, ThrowingBiFunction, ImdbSearchIdException> providerSerieIdSupplier) { Collection providerSerieIds; try { @@ -118,8 +119,8 @@ private OptionalInt getImdbIdCommon(String title, Integer year, .mapToInt(providerSerieId -> Integer.parseInt(providerSerieId.id)); } - private OptionalInt promptUserToEnterImdbId(String title, int year) { - return userInteractionHandler.enterNumber(PROVIDER_NAME, getText("Prompter.EnterImdbMatchForSerie", title)); + private OptionalInt promptUserToEnterImdbId(String title) { + return userInteractionHandler.enterNumber(PROVIDER_NAME, getText("Prompter.EnterImdbIdForSerie", title)); } public static synchronized ImdbAdapter getInstance(Manager manager, UserInteractionHandler userInteractionHandler) { 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 9973c5a4..57ddadb4 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 @@ -14,6 +14,7 @@ import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; @@ -24,7 +25,7 @@ record ImdbSearchIdApi(Manager manager) { private static final Pattern IMDB_URL_ID_PATTERN = Pattern.compile("/title/tt(\\d*)"); - public Set getImdbIdOnImdb(String title, Integer year) throws ImdbSearchIdException { + public Set getImdbIdOnImdb(String title, @Nullable Integer year) throws ImdbSearchIdException { return manager.getCache(CacheType.MEMORY, "IMDB-imdbid-imdb-$title-$year") .getCollection(() -> { StringBuilder sb = new StringBuilder("https://www.imdb.com/find?q="); @@ -45,7 +46,7 @@ public Set getImdbIdOnImdb(String title, Integer year) throws I }); } - public Set getImdbIdOnYahoo(String title, Integer year) throws ImdbSearchIdException { + public Set getImdbIdOnYahoo(String title, @Nullable Integer year) throws ImdbSearchIdException { return manager.getCache(CacheType.MEMORY, "IMDB-imdbid-yahoo-$title-$year") .getCollection(() -> { StringBuilder sb = @@ -73,7 +74,7 @@ public Set getImdbIdOnYahoo(String title, Integer year) throws }); } - public Set getImdbIdOnGoogle(String title, Integer year) throws ImdbSearchIdException { + public Set getImdbIdOnGoogle(String title, @Nullable Integer year) throws ImdbSearchIdException { return manager.getCache(CacheType.MEMORY, "IMDB-imdbid-google-$title-$year") .getCollection(() -> { StringBuilder sb = new StringBuilder("http://www.google.com/search?q="); 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 bf9a6cd9..00a25ee3 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 @@ -7,38 +7,39 @@ import lombok.ToString; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; @ToString public class TheTvdbEpisode implements Serializable { @Serial private static final long serialVersionUID = 913790243120597542L; - @var String id; - @var String combinedEpisodeNumber; - @var String combinedSeason; - @var String dvdChapter; - @var String dvdDiscId; - @var String dvdEpisodeNumber; - @var String dvdSeason; + @var @Nullable String id; + @var @Nullable String combinedEpisodeNumber; + @var @Nullable String combinedSeason; + @var @Nullable String dvdChapter; + @var @Nullable String dvdDiscId; + @var @Nullable String dvdEpisodeNumber; + @var @Nullable String dvdSeason; @var List directors = new ArrayList<>(); - @var String epImgFlag; - @var String episodeName; + @var @Nullable String epImgFlag; + @var @Nullable String episodeName; @var int episodeNumber; - @var String firstAired; + @var @Nullable String firstAired; @var List guestStars = new ArrayList<>(); - @var String imdbId; - @var Language language; + @var @Nullable String imdbId; + @var @Nullable Language language; // @var String overview; - @var String productionCode; - @var String rating; + @var @Nullable String productionCode; + @var @Nullable String rating; @var int seasonNumber; @var List writers = new ArrayList<>(); - @var String absoluteNumber; + @var @Nullable String absoluteNumber; @var int airsAfterSeason; @var int airsBeforeSeason; @var int airsBeforeEpisode; - @var String filename; - @var String lastUpdated; - @var String seriesId; - @var String seasonId; + @var @Nullable String filename; + @var @Nullable String lastUpdated; + @var @Nullable String seriesId; + @var @Nullable 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 cc7dc468..32bf30fb 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 @@ -7,6 +7,7 @@ import lombok.ToString; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; @ToString @@ -15,23 +16,23 @@ public class TheTvdbSerie implements Serializable { private static final long serialVersionUID = -4036836377513152443L; @var int id; //@var String serieId; - @var Language language; - @var String serieName; - @var String banner; + @var @Nullable Language language; + @var @Nullable String serieName; + @var @Nullable String banner; //@var String overview; - @var String firstAired; - @var String imdbId; - @var String zap2ItId; + @var @Nullable String firstAired; + @var @Nullable String imdbId; + @var @Nullable String zap2ItId; @var List actors = new ArrayList<>(); - @var String airsDayOfWeek; - @var String airsTime; - @var String contentRating; + @var @Nullable String airsDayOfWeek; + @var @Nullable String airsTime; + @var @Nullable 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; + @var @Nullable String network; + @var @Nullable String rating; + @var @Nullable String runtime; + @var @Nullable String status; + @var @Nullable String fanArt; + @var @Nullable String lastUpdated; + @var @Nullable String poster; } 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 189ec583..aa285ec5 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 @@ -18,13 +18,12 @@ import java.util.function.Function; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.util.Validator; public class InputPane extends JDialog implements ActionListener, PropertyChangeListener { private static final long serialVersionUID = 1L; - private static final String OK = getText("App.OK"); - private static final String CANCEL = getText("App.Cancel"); private final String message; private final List> inputValidators; private final Function toObjectMapper; @@ -37,14 +36,14 @@ public class InputPane extends JDialog implements ActionListener, PropertyCha private JOptionPane optionPane; private T input; - public InputPane(Frame owner=null, + public InputPane(@Nullable Frame owner=null, String title, String message, List> inputValidators=new ArrayList>(), Function toObjectMapper, List> objectValidators=new ArrayList>(), - String okText=OK, - String cancelText=CANCEL) { + String okText=getText("App.OK"), + String cancelText=getText("App.Cancel")) { super(owner, true); setTitle(title); 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 546de8d5..925e1fa7 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 @@ -10,7 +10,7 @@ import manifold.ext.props.rt.api.var; @ToString -public class SerieMapping implements Serializable { // implements SerieMappingIntf { +public class SerieMapping implements Serializable { @Serial private static final long serialVersionUID = 6551798252915028805L; 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 7286cd88..a1656943 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 @@ -23,21 +23,19 @@ import extensions.java.io.InputStream.InputStreamExt; import extensions.java.nio.file.Path.PathExt; import jakarta.ws.rs.core.HttpHeaders; -import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.jsoup.helper.HttpConnection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@RequiredArgsConstructor public class HttpClient { private final CookieManager cookieManager; private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class); - public HttpClient() { - this(new CookieManager()); + public HttpClient(CookieManager cookieManager=new CookieManager()) { + this.cookieManager = cookieManager; } public String doGet(URL url, String userAgent) throws IOException, HttpClientException { From 11e70e200d6f5c6ced7f6d3fed63f2190660b183 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Mon, 28 Apr 2025 23:59:00 +0200 Subject: [PATCH 48/58] changes --- .../cli/progress/CLISearchProgress.java | 2 +- .../progress/search/SearchProgressDialog.java | 4 +- .../lib/ReleaseFactory.java | 3 +- .../lib/control/ReleaseControl.java | 10 ++-- .../subtitles/filters/KeywordFilter.java | 3 +- .../subtitles/filters/ReleaseGroupFilter.java | 4 +- .../lib/library/PathLibraryBuilder.java | 3 +- .../subtitleproviders/SubtitleProvider.java | 10 +--- .../subtitleproviders/adapters/Adapter.java | 9 ++-- .../adapters/JAddic7edAdapter.java | 6 +-- .../adapters/JAddic7edViaProxyAdapter.java | 6 +-- .../adapters/JOpenSubAdapter.java | 6 +-- .../adapters/JPodnapisiAdapter.java | 6 +-- .../adapters/JSubsceneAdapter.java | 6 +-- .../adapters/JTVsubtitlesAdapter.java | 22 ++++----- .../model/Addic7edSubtitleDescriptor.java | 5 +- .../gestdown/JAddic7edProxyGestdownApi.java | 6 +-- .../opensubtitles/DownloadSubtitle.java | 4 +- .../opensubtitles/SearchSubtitles.java | 47 ++++++++++--------- .../podnapisi/JPodnapisiApi.java | 2 - .../model/SubsceneSubtitleDescriptor.java | 15 +++--- .../tvsubtitles/JTVSubtitlesApi.java | 10 ++-- ...ava => TVSubtitlesSubtitleDescriptor.java} | 5 +- .../multisubdownloader/util/ExportImport.java | 3 +- .../workers/SearchManager.java | 6 +-- .../workers/SearchWorker.java | 33 +++++-------- .../sublibrary/cache/ExpiringCacheObject.java | 3 +- .../cache/TemporaryCacheObject.java | 10 ++-- 28 files changed, 106 insertions(+), 143 deletions(-) rename MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/{TVsubtitlesSubtitleDescriptor.java => TVSubtitlesSubtitleDescriptor.java} (69%) 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 5e0b70de..a95695d7 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 @@ -19,7 +19,7 @@ public CLISearchProgress() { @Override public void progress(SubtitleProvider provider, int jobsLeft, Release release) { - this.tableModel.update(provider.getName(), jobsLeft, release == null ? "Done" : release.fileName); + this.tableModel.update(provider.name, jobsLeft, release == null ? "Done" : release.fileName); this.printProgress(); } 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 6aec818a..e67720f4 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 @@ -3,7 +3,7 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.table.*; +import javax.swing.table.TableColumn; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.Serial; @@ -61,7 +61,7 @@ public void windowClosing(WindowEvent e) { @Override public void progress(SubtitleProvider provider, int jobsLeft, Release release) { this.setVisible(); - this.tableModel.update(provider.getName(), jobsLeft, release == null ? "Done" : release.fileName); + this.tableModel.update(provider.name, jobsLeft, release == null ? "Done" : release.fileName); } @Override 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 f3ddb1e2..6c729d5f 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 @@ -21,12 +21,11 @@ public class ReleaseFactory { private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseFactory.class); - private final ReleaseParser releaseParser; + private final ReleaseParser releaseParser = new ReleaseParser(); private final Settings settings; private final Manager manager; public ReleaseFactory(Settings settings, Manager manager) { - this.releaseParser = new ReleaseParser(); this.settings = settings; this.manager = manager; } 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 394df25e..7cabaf5c 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 @@ -2,19 +2,17 @@ import static manifold.ext.props.rt.api.PropOption.*; -import lombok.AllArgsConstructor; -import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.val; 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; -@AllArgsConstructor public abstract sealed class ReleaseControl permits MovieReleaseControl, TvReleaseControl { - @get(Protected) Settings settings; - @get(Protected) Manager manager; - @get abstract Release videoFile; + @val(Protected) Settings settings; + @val(Protected) Manager manager; + @val(Abstract) Release videoFile; ReleaseControl(Settings settings, Manager manager) { this.settings = settings; 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 c7469d94..2e47d5df 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 @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters; +import org.apache.commons.lang3.StringUtils; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; @@ -15,7 +16,7 @@ public final class KeywordFilter extends SubtitleFilter { public boolean useSubtitle(Release release, Subtitle subtitle) { String keywordsFile = ReleaseParser.getQualityKeyword(getReleaseName(release)); - if (subtitle.quality.isEmpty()) { + if (StringUtils.isEmpty(subtitle.quality)) { subtitle.quality = ReleaseParser.getQualityKeyword(subtitle.fileName); } if (!checkKeywordSubtitleMatch(subtitle, keywordsFile)) { 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 index ea4cc496..7add5191 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 @@ -14,9 +14,9 @@ public final class ReleaseGroupFilter extends SubtitleFilter { @Override public boolean useSubtitle(Release release, Subtitle subtitle) { - if (subtitle.releaseGroup.isEmpty()) { + if (StringUtils.isEmpty(subtitle.releaseGroup)) { subtitle.releaseGroup = - ReleaseParser.extractReleaseGroup(subtitle.fileName, subtitle.fileName.endsWith(".srt")); + ReleaseParser.extractReleaseGroup(subtitle.fileName, StringUtils.endsWith(subtitle.fileName, ".srt")); } if (!StringUtils.containsAnyIgnoreCase(subtitle.releaseGroup, release.releaseGroup, subtitle.releaseGroup)) { return false; 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 7c1ca043..c16f8952 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,7 @@ import java.nio.file.Path; import java.nio.file.Paths; +import org.jspecify.annotations.Nullable; 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; @@ -23,7 +24,7 @@ public final class PathLibraryBuilder extends LibraryBuilder { private final boolean move; public PathLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, - TheTvdbAdapter tvdbAdapter=null, Path libraryFolder, boolean move) { + @Nullable TheTvdbAdapter tvdbAdapter=null, Path libraryFolder, boolean move) { super(tvdbAdapter); this.structure = structure; this.replaceSpace = replaceSpace; 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 c04d3e1e..7aacd1f5 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 @@ -20,18 +20,12 @@ public interface SubtitleProvider { @val Manager manager; @val SubtitleSource subtitleSource; @val String providerName; + @val String name = subtitleSource.name; Set searchSubtitles(TvRelease tvRelease, Language language); Set searchSubtitles(MovieRelease movieRelease, Language language); - /** - * @return The name of the SubtitleProvider - */ - default String getName() { - return subtitleSource.name; - } - /** * Starts a search for subtitles * @@ -47,7 +41,7 @@ default Set search(Release release, Language language) { }; } catch (Exception e) { LoggerFactory.getLogger(SubtitleProvider.class) - .error("Error in %s API: %s".formatted(getName(), e.getMessage()), e); + .error("Error in %s API: %s".formatted(name, e.getMessage()), e); } return Set.of(); } 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 4605951d..5937ebdc 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 @@ -14,6 +14,7 @@ import java.util.function.Function; import lombok.experimental.ExtensionMethod; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; @@ -135,7 +136,7 @@ default Optional getProviderSerieId(String serieName, String serie int season, Integer tvdbId) throws X { LazySupplier tvdbIdCacheFunction = new LazySupplier<>(() -> manager.getCache(CacheType.DISK, - "%s-serieName-tvdbId:%s-%s".formatted(providerName, tvdbId, useSeasonForSerieId() ? season : -1))); + "%s-serieName-tvdbId:%s-%s".formatted(providerName, tvdbId, useSeasonForSerieId ? season : -1))); if (tvdbId != null) { CacheKey tvdbIdCache = tvdbIdCacheFunction.get(); if (tvdbIdCache.isPresent()) { @@ -147,7 +148,7 @@ default Optional getProviderSerieId(String serieName, String serie return Optional.empty(); } - int seasonToUse = useSeasonForSerieId() ? season : 0; + int seasonToUse = useSeasonForSerieId ? season : 0; CacheKey serieNameCache = manager.getCache(CacheType.DISK, "%s-serieName-name:%s-%s".formatted(providerName, serieName.toLowerCase(), seasonToUse)); if (StringUtils.equals(serieNameToSearchFor, serieName) && serieNameCache.isPresent()) { @@ -194,7 +195,7 @@ default Optional getProviderSerieId(String serieName, String serie // let the user select the correct provider serie id uriForSerie = getUserInteractionHandler().selectFromList( providerSerieIds, - useSeasonForSerieId() ? + useSeasonForSerieId ? getText("SelectDialog.SelectSerieNameForNameWithSeason", displayName, seasonToUse) : getText("SelectDialog.SelectSerieNameForName", displayName), providerName, @@ -224,7 +225,7 @@ default Optional getProviderSerieId(String serieName, String serie return Optional.of(serieMapping); } - boolean useSeasonForSerieId(); + @val boolean useSeasonForSerieId; String providerSerieIdToDisplayString(S providerSerieId); } 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 0f5d402c..fa0b96cd 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 @@ -40,6 +40,7 @@ public final class JAddic7edAdapter extends AbstractAdapter jaapi; @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; @val @override String providerName = subtitleSource.name(); + @val @override boolean useSeasonForSerieId = true; public JAddic7edAdapter(Manager manager, boolean speedy, Credentials credentials=null, UserInteractionHandler userInteractionHandler) { @@ -130,11 +131,6 @@ public List getSortedProviderSerieIds(@Nullable Integer tvdbId, .toList(); } - @Override - public boolean useSeasonForSerieId() { - return true; - } - @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { 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 1a5a964e..b017a44a 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 @@ -37,6 +37,7 @@ public final class JAddic7edViaProxyAdapter extends AbstractAdapter convertToSubtitles(TvRelease tvRelease, Collection(subtitles); } - @Override - public boolean useSeasonForSerieId() { - return false; - } - @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { return providerSerieId.name; 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 be1ec248..c493a951 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 @@ -43,6 +43,7 @@ public final class JOpenSubAdapter private static LazySupplier osApi; @val @override SubtitleSource subtitleSource = SubtitleSource.OPENSUBTITLES; @val @override String providerName = subtitleSource.name(); + @val @override boolean useSeasonForSerieId = false; public JOpenSubAdapter(Manager manager, Credentials credentials, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -157,11 +158,6 @@ public List getSortedProviderSerieIds(@Nullable Integer tvd .toList(); } - @Override - public boolean useSeasonForSerieId() { - return false; - } - @Override public String providerSerieIdToDisplayString(OpensubtitleSerieId providerSerieId) { 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 9536a65d..32e74746 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 @@ -36,6 +36,7 @@ public final class JPodnapisiAdapter private static LazySupplier jpapi; @val @override SubtitleSource subtitleSource = SubtitleSource.PODNAPISI; @val @override String providerName = subtitleSource.name(); + @val @override boolean useSeasonForSerieId = false; public JPodnapisiAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -121,11 +122,6 @@ public List getSortedProviderSerieIds(@Nullable Integer tvdbId, return getApi().getPodnapisiShowName(serieName).stream().toList(); } - @Override - public boolean useSeasonForSerieId() { - return false; - } - @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { 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 d5add85e..0e896877 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 @@ -45,6 +45,7 @@ public final class JSubsceneAdapter private static LazySupplier api; @val @override SubtitleSource subtitleSource = SubtitleSource.SUBSCENE; @val @override String providerName = subtitleSource.name(); + @val @override boolean useSeasonForSerieId = true; public JSubsceneAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -155,11 +156,6 @@ private String getSeasonEpisodeString(int season, int episode) { return "S%02dE%02d".formatted(season, episode); } - @Override - public boolean useSeasonForSerieId() { - return true; - } - @Override public String providerSerieIdToDisplayString(SubSceneSerieId providerSerieId) { if (providerSerieId.id.endsWith("-season")) { 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 b9dc530a..6b68c375 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 @@ -16,7 +16,7 @@ import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.JTVSubtitlesApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.exception.TvSubtitlesException; -import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.model.TVsubtitlesSubtitleDescriptor; +import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.model.TVSubtitlesSubtitleDescriptor; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.control.ReleaseParser; @@ -32,13 +32,14 @@ import org.slf4j.LoggerFactory; public final class JTVsubtitlesAdapter - extends AbstractAdapter { + 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(); + @val @override boolean useSeasonForSerieId = false; public JTVsubtitlesAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { super(manager, userInteractionHandler); @@ -58,33 +59,33 @@ private JTVSubtitlesApi getApi() { } @Override - public List searchMovieSubtitlesWithHash(String hash, Language language) { + public List searchMovieSubtitlesWithHash(String hash, Language language) { // TODO implement this return List.of(); } @Override - public List searchMovieSubtitlesWithId(int tvdbId, Language language) { + public List searchMovieSubtitlesWithId(int tvdbId, Language language) { // TODO implement this return List.of(); } @Override - public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, + public Collection searchMovieSubtitlesWithName(String name, @Nullable Integer year, Language language) { // TODO implement this return List.of(); } @Override - public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, + public Set convertToSubtitles(MovieRelease movieRelease, Set subtitles, Language language) { // TODO implement this return Set.of(); } @Override - public Set searchSerieSubtitles(TvRelease tvRelease, Language language) + public Set searchSerieSubtitles(TvRelease tvRelease, Language language) throws TvSubtitlesException { return getProviderSerieId(tvRelease).map( providerSerieId -> tvRelease.episodes.stream().flatMap(episode -> { @@ -100,7 +101,7 @@ public Set searchSerieSubtitles(TvRelease tvRelea } @Override - public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, + public Set convertToSubtitles(TvRelease tvRelease, Collection subtitles, Language language) { return subtitles.stream() .map(sub -> new Subtitle( @@ -135,11 +136,6 @@ public List getSortedProviderSerieIds(@Nullable Integer tvdbId, .toList(); } - @Override - public boolean useSeasonForSerieId() { - return false; - } - @Override public String providerSerieIdToDisplayString(ProviderSerieId providerSerieId) { return providerSerieId.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 7d4a6da2..f1e5f9f5 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 @@ -2,8 +2,9 @@ import java.io.Serializable; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; -public record Addic7edSubtitleDescriptor(String version, Language language, String url, String title, String uploader, - boolean hearingImpaired) implements Serializable { +public record Addic7edSubtitleDescriptor(String version, @Nullable Language language, String url, String title, + String uploader, boolean hearingImpaired) implements Serializable { } 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 7ce420ef..f9648fee 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 @@ -31,14 +31,12 @@ public class JAddic7edProxyGestdownApi implements SubtitleApi { private static final String DOMAIN = "https://api.gestdown.info"; private final Manager manager; - private final TvShowsApi tvShowsApi; - private final SubtitlesApi subtitlesApi; + private final TvShowsApi tvShowsApi = new TvShowsApi(); + private final SubtitlesApi subtitlesApi = new SubtitlesApi(); @val @override SubtitleSource subtitleSource = SubtitleSource.ADDIC7ED; public JAddic7edProxyGestdownApi(Manager manager) { this.manager = manager; - tvShowsApi = new TvShowsApi(); - subtitlesApi = new SubtitlesApi(); } public List getProviderSerieName(String serieName) throws ApiException { 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 0a84aa18..f6bef9f4 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 @@ -15,8 +15,8 @@ public final class DownloadSubtitle extends OpenSubtitlesExecuter { public Download200Response download() throws OpenSubtitlesException { try { - return execute(() -> new DownloadApi(apiClient).download("SubTools", - 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/SearchSubtitles.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/SearchSubtitles.java index 7816259a..c0cea172 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 @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import org.jspecify.annotations.Nullable; 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; @@ -30,51 +31,51 @@ public final class SearchSubtitles extends OpenSubtitlesExecuter { private final Manager manager; private final ApiClient apiClient; - private AiTranslatedEnum aiTranslated; + private @Nullable AiTranslatedEnum aiTranslated; - private Integer episode; + private @Nullable Integer episode; - private ForeignPartsOnlyEnum foreignPartsOnly; + private @Nullable ForeignPartsOnlyEnum foreignPartsOnly; - private HearingImpairedEnum hearingImpaired; + private @Nullable HearingImpairedEnum hearingImpaired; - private Integer id; + private @Nullable Integer id; - private Integer imdbId; + private @Nullable Integer imdbId; - private Language language; + private @Nullable Language language; - private MachineTranslatedEnum machineTranslated; + private @Nullable MachineTranslatedEnum machineTranslated; - private String movieHash; + private @Nullable String movieHash; - private MoviehashMatchEnum movieHashMatch; + private @Nullable MoviehashMatchEnum movieHashMatch; - private SearchSubtitlesEnum orderBy; + private @Nullable SearchSubtitlesEnum orderBy; - private OrderDirectionEnum orderDirection; + private @Nullable OrderDirectionEnum orderDirection; - private Integer page; + private @Nullable Integer page; - private Integer parentFeatureId; + private @Nullable Integer parentFeatureId; - private Integer parentImdbId; + private @Nullable Integer parentImdbId; - private Integer parentTmdbId; + private @Nullable Integer parentTmdbId; - private String query; + private @Nullable String query; - private Integer season; + private @Nullable Integer season; - private Integer tmdbId; + private @Nullable Integer tmdbId; - private TrustedSourcesEnum trustedSources; + private @Nullable TrustedSourcesEnum trustedSources; - private TypeEnum type; + private @Nullable TypeEnum type; - private Integer userId; + private @Nullable Integer userId; - private Integer year; + private @Nullable Integer year; private String userAgent = "SubTools"; // should be set 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 664a808a..52c6dc97 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 @@ -6,7 +6,6 @@ import java.io.Serial; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; import java.util.Collections; import java.util.EnumMap; import java.util.List; @@ -39,7 +38,6 @@ public class JPodnapisiApi implements SubtitleApi { 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 { 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 14073c58..b8eba1ea 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 @@ -5,6 +5,7 @@ import com.pivovarit.function.ThrowingSupplier; import lombok.EqualsAndHashCode; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.exception.SubsceneException; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.model.SeasonEpisode; @@ -12,16 +13,16 @@ @EqualsAndHashCode public class SubsceneSubtitleDescriptor implements Serializable { - @val Language language; - @val String name; + @val @Nullable Language language; + @val @Nullable String name; @val boolean hearingImpaired; - @val String uploader; - @val String comment; - @val SeasonEpisode seasonEpisode; + @val @Nullable String uploader; + @val @Nullable String comment; + @val @Nullable SeasonEpisode seasonEpisode; @EqualsAndHashCode.Exclude @val ThrowingSupplier urlSupplier; - public SubsceneSubtitleDescriptor(Language language, - String name, boolean hearingImpaired, String uploader, String comment, + public SubsceneSubtitleDescriptor(@Nullable Language language, + @Nullable String name, boolean hearingImpaired,@Nullable String uploader, @Nullable String comment, ThrowingSupplier urlSupplier) { this.language = language; this.name = name; 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 428df26a..437b2eac 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 @@ -17,7 +17,7 @@ import org.jsoup.select.Elements; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.exception.TvSubtitlesException; -import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.model.TVsubtitlesSubtitleDescriptor; +import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.model.TVSubtitlesSubtitleDescriptor; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.PageContentParams; @@ -52,17 +52,17 @@ public List getUrisForSerieName(String serieName) throws TvSubt } } - public Set getSubtitles(SerieMapping providerSerieId, int season, int episode, + 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) + private Set getSubtitles(String episodeUrl, Language language) throws TvSubtitlesException { return manager.getCache(CacheType.MEMORY, subtitleSource.name() + "subtitles-$episodeUrl-$language") .getCollection(() -> { - Set lSubtitles = new HashSet<>(); + Set lSubtitles = new HashSet<>(); try { Elements searchEpisodes = manager.getAsJsoupDocument(PageContentParams.params( @@ -101,7 +101,7 @@ private Set getSubtitles(String episodeUrl, Langu author = getRowValue.apply(row); } if (filename != null && rip != null) { - TVsubtitlesSubtitleDescriptor sub = TVsubtitlesSubtitleDescriptor.builder() + TVSubtitlesSubtitleDescriptor sub = TVSubtitlesSubtitleDescriptor.builder() .filename(filename) .url("$DOMAIN/files/" + URLEncoder.encode( filename.replace(title + ".", "") 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 similarity index 69% rename from MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVsubtitlesSubtitleDescriptor.java rename to MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVSubtitlesSubtitleDescriptor.java index 2d65efff..b269c756 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 @@ -5,15 +5,16 @@ import lombok.Builder; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.Nullable; @Builder -public class TVsubtitlesSubtitleDescriptor implements Serializable { +public class TVSubtitlesSubtitleDescriptor implements Serializable { @Serial private static final long serialVersionUID = 6423513286301479905L; @val String filename; @val String url; @val String rip; - @val String author; + @val @Nullable String author; } 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 a018bbba..0e007890 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 @@ -193,5 +193,6 @@ private enum ImportStyle { } @StandardException - private static class CorruptSettingsFileException extends Exception {} + public static class CorruptSettingsFileException extends Exception { + } } 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 379d2f68..531ca5a0 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 @@ -75,8 +75,8 @@ public void start() { } public void onCompleted(SearchWorker worker) { - Release release = worker.getRelease(); - List subtitles = new ArrayList<>(worker.getSubtitles()); + Release release = worker.release; + List subtitles = new ArrayList<>(worker.subtitles); /* set the score of the found subtitles */ ScoreCalculator calculator = this.scoreCalculators.get(release); @@ -124,7 +124,7 @@ private int jobsLeft() { for (Entry> provider : this.queue.entrySet()) { jobsLeft += provider.getValue().size(); SearchWorker worker = this.workers.get(provider.getKey()); - if (worker.isAlive() && worker.isBusy()) { + if (worker.isAlive() && worker.busy) { jobsLeft++; } } 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 8e333cdd..0809689b 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 @@ -1,7 +1,12 @@ package org.lodder.subtools.multisubdownloader.workers; +import static manifold.ext.props.rt.api.PropOption.*; + import java.util.Set; +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.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderInitException; @@ -14,12 +19,12 @@ public class SearchWorker extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(SearchWorker.class); + @val SubtitleProvider provider; private final SearchManager scheduler; - protected final SubtitleProvider provider; - private boolean busy = false; + @var @set(Private) boolean busy = false; private boolean isInterrupted = false; - private Release release; - private Set subtitles; + @var @set(Private) Release release; + @var @set(Private) Set subtitles; public SearchWorker(SubtitleProvider provider, SearchManager scheduler) { this.provider = provider; @@ -40,7 +45,7 @@ public void run() { break; } this.release = release; - LOGGER.debug("[Search] {} searching {} ", this.provider.getName(), release); + LOGGER.debug("[Search] {} searching {} ", this.provider.name, release); Set subtitles = this.provider.search(release, language); @@ -48,7 +53,7 @@ public void run() { this.subtitles = Set.copyOf(subtitles); this.busy = false; - LOGGER.debug("[Search] {} found {} subtitles for {} ", this.provider.getName(), subtitles.size(), + LOGGER.debug("[Search] {} found {} subtitles for {} ", this.provider.name, subtitles.size(), release); if (!this.isInterrupted()) { @@ -71,20 +76,4 @@ public void interrupt() { this.isInterrupted = true; super.interrupt(); } - - public boolean isBusy() { - return busy; - } - - public Release getRelease() { - return release; - } - - public Set getSubtitles() { - return subtitles; - } - - public SubtitleProvider getProvider() { - return provider; - } } 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 d86ded1a..9354ee63 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 @@ -26,11 +26,12 @@ sealed class ExpiringCacheObject implements CacheObject permits ExpiringSe private static final long serialVersionUID = 3852086993086134232L; private static final Pattern PATTERN = Pattern.compile("created:(.*?)|lastAccessed:(.*?)|value:(.*)"); - @override @val Time created = Time.now(); + @override @val Time created; @var @set(Private) Time lastAccessed = Time.now(); @override @var T value; protected ExpiringCacheObject(T value) { + this.created = Time.now(); this.value = value; } 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 c4b6589d..cd09b140 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 @@ -8,25 +8,27 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.ToString; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import manifold.science.measures.Time; @ToString -@AllArgsConstructor(access = AccessLevel.PRIVATE) sealed class TemporaryCacheObject implements CacheObject permits TemporarySerializableCacheObject { @Serial private static final long serialVersionUID = -152474119228350222L; private static final Pattern PATTERN = Pattern.compile("created:(.*?)|expire:(.*?)|value:(.*)"); - @override @val Time created = Time.now(); + @override @val Time created; @val Time timeToLive; @override @val T value; protected TemporaryCacheObject(Time timeToLive, T value) { + this(Time.now(), timeToLive, value); + } + + private TemporaryCacheObject(Time created, Time timeToLive, T value) { + this.created = created; this.timeToLive = timeToLive; this.value = value; } From ec3b90aaa7a4f077f3fd66d74560ecc4afee61ce Mon Sep 17 00:00:00 2001 From: EotT123 Date: Tue, 29 Apr 2025 00:23:41 +0200 Subject: [PATCH 49/58] changes --- .../lodder/subtools/multisubdownloader/GUI.java | 2 +- .../actions/DownloadAction.java | 13 ++++++------- .../multisubdownloader/framework/event/Event.java | 7 +------ .../gui/dialog/MappingEpisodeNameDialog.java | 4 +++- .../gui/dialog/MultiSubDialog.java | 10 +++------- .../gui/dialog/ProgressDialog.java | 14 +++++--------- .../gui/dialog/RenameDialog.java | 5 +++-- .../gui/dialog/SelectDialog.java | 7 +++++-- .../gui/dialog/StructureBuilderDialog.java | 8 +++++--- .../gui/extra/MemoryFolderChooser.java | 15 ++++----------- .../gui/extra/PanelCheckBox.java | 3 ++- .../gui/extra/PartialDisableComboBox.java | 3 ++- .../lib/library/FilenameLibraryBuilder.java | 5 +++-- .../multisubdownloader/settings/SettingValue.java | 2 +- 14 files changed, 44 insertions(+), 54 deletions(-) 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 c7bd5418..f36590ba 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -152,7 +152,7 @@ private void checkUpdate(final boolean forceUpdateCheck) { * Initialize the contents of the frame. */ private void initialize() { - MemoryFolderChooser.getInstance().setMemory(settingsControl.settings.lastOutputDir); + MemoryFolderChooser.getInstance().memory = settingsControl.settings.lastOutputDir; addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { 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 a64b3453..f9a6de7f 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 @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import lombok.experimental.ExtensionMethod; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.lib.library.FilenameLibraryBuilder; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; @@ -32,7 +33,9 @@ public class DownloadAction { private final Manager manager; private final UserInteractionHandler userInteractionHandler; - public void download(Release release, Subtitle subtitle, Integer version) throws IOException, ManagerException { + public void download(Release release, Subtitle subtitle, Integer version=0) throws IOException, + ManagerException { + LOGGER.info("Downloading subtitle: [{}] for release: [{}]", subtitle.fileName, release.fileName); switch (release.videoType) { case EPISODE -> download(release, subtitle, settings.episodeLibrarySettings, version); case MOVIE -> download(release, subtitle, settings.movieLibrarySettings, version); @@ -40,12 +43,8 @@ public void download(Release release, Subtitle subtitle, Integer version) throws } } - public void download(Release release, Subtitle subtitle) throws IOException, ManagerException { - LOGGER.info("Downloading subtitle: [{}] for release: [{}]", subtitle.fileName, release.fileName); - download(release, subtitle, 0); - } - - private void download(Release release, Subtitle subtitle, LibrarySettings librarySettings, Integer version) + private void download(Release release, Subtitle subtitle, LibrarySettings librarySettings, + @Nullable Integer version) throws IOException, ManagerException { LOGGER.trace("cleanUpFiles: LibraryAction {}", librarySettings.action); Path path = PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java index 1b9934f9..9d923fc9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java @@ -9,12 +9,7 @@ public class Event { private final String eventName; private final EventBag eventBag; - public Event(String name) { - this.eventName = name; - this.eventBag = new EventBag(); - } - - public Event(String name, EventBag bag) { + public Event(String name, EventBag bag=new EventBag()) { this.eventName = name; this.eventBag = bag; } 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 22a26d32..b8fd6b7a 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 @@ -22,6 +22,7 @@ import manifold.ext.props.rt.api.var; import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.tuple.Pair; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; @@ -42,7 +43,8 @@ public class MappingEpisodeNameDialog extends MultiSubDialog { private Optional selectedSubtitleProvider; private MappingType selectedMappingType; - public MappingEpisodeNameDialog(JFrame frame, Manager manager, SubtitleProviderStore subtitleProviderStore, + public MappingEpisodeNameDialog(@Nullable JFrame frame=null, Manager manager, + SubtitleProviderStore subtitleProviderStore, UserInteractionHandlerGUI userInteractionHandler) { super(frame, getText("MappingEpisodeNameDialog.Title"), true); this.subtitleProviderStore = subtitleProviderStore; 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 802845cd..b23b9895 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 @@ -4,23 +4,19 @@ import java.awt.*; import java.io.Serial; +import org.jspecify.annotations.Nullable; + public class MultiSubDialog extends JDialog { @Serial private static final long serialVersionUID = -2357021997104425566L; - public MultiSubDialog(JFrame frame, String title, boolean modal) { + public MultiSubDialog(@Nullable JFrame frame=null, String title, boolean modal) { super(frame); setTitle(title); setModal(modal); } - public MultiSubDialog(String title, boolean modal) { - super(); - setTitle(title); - setModal(modal); - } - protected void setDialogLocation(Frame f) { Rectangle r = f.getBounds(); int x = r.x + (r.width - size.width) / 2; 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 e21105f9..8ddb05e2 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 @@ -6,6 +6,7 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.gui.extra.progress.Messenger; import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; @@ -18,18 +19,13 @@ public class ProgressDialog extends MultiSubDialog implements Messenger { private JProgressBar progressBar; private JLabel label; - public ProgressDialog(JFrame frame, Cancelable sft) { + public ProgressDialog(@Nullable JFrame frame=null, Cancelable sft) { super(frame, Messages.getText("ProgressDialog.Title"), false); StatusMessenger.instance.addListener(this); initializeUi(sft); - setDialogLocation(frame); - repaint(); - } - - public ProgressDialog(Cancelable sft) { - super(Messages.getText("ProgressDialog.Title"), false); - StatusMessenger.instance.addListener(this); - initializeUi(sft); + if (frame != null) { + setDialogLocation(frame); + } repaint(); } 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 e3814107..a3cb6c2a 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 @@ -19,6 +19,7 @@ import lombok.experimental.ExtensionMethod; import manifold.ext.props.rt.api.set; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.actions.RenameAction; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; @@ -47,8 +48,8 @@ public class RenameDialog extends MultiSubDialog implements PropertyChangeListen private ProgressDialog progressDialog; - public RenameDialog(JFrame frame, Settings settings, VideoType videoType, String title, Manager manager, - UserInteractionHandler userInteractionHandler) { + public RenameDialog(@Nullable JFrame frame=null, Settings settings, VideoType videoType, String title, + Manager manager, UserInteractionHandler userInteractionHandler) { super(frame, title, false); setResizable(false); setBounds(100, 100, 650, 680); 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 0bf331ab..c562a64a 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 @@ -3,7 +3,9 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.table.*; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableRowSorter; import java.awt.*; import java.io.Serial; import java.util.Comparator; @@ -11,6 +13,7 @@ import java.util.stream.IntStream; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.Nullable; 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; @@ -29,7 +32,7 @@ public class SelectDialog extends MultiSubDialog { /** * Create the dialog. */ - public SelectDialog(JFrame frame, List subtitles, Release release) { + public SelectDialog(@Nullable JFrame frame = null, List subtitles, Release release) { super(frame, getText("SelectDialog.SelectCorrectSubtitle"), true); this.subtitles = subtitles.stream().distinct().sorted(Comparator.comparing(Subtitle::getScore).reversed()).toList(); 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 b1da2ebf..8c802b84 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 @@ -3,8 +3,9 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.event.*; -import javax.swing.text.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -13,6 +14,7 @@ import java.util.function.Function; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.lib.ReleaseFactory; import org.lodder.subtools.multisubdownloader.lib.library.LibraryBuilder; import org.lodder.subtools.multisubdownloader.settings.model.Settings; @@ -47,7 +49,7 @@ public enum StructureType { FILE, FOLDER } - public StructureBuilderDialog(JFrame frame, String title, boolean modal, VideoType videoType, + public StructureBuilderDialog(@Nullable JFrame frame=null, String title, boolean modal, VideoType videoType, StructureType structureType, Manager manager, UserInteractionHandler userInteractionHandler, Function filenameLibraryBuilder) { super(frame, title, modal); 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 43639260..77fba6e4 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 @@ -7,13 +7,14 @@ import java.util.Objects; import java.util.Optional; +import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; public class MemoryFolderChooser { private static MemoryFolderChooser instance; private final JFileChooser chooser; - private File memory; + @var Path memory; private MemoryFolderChooser() { chooser = new JFileChooser(); @@ -35,14 +36,14 @@ public Optional selectDirectory(Component c, String title, Path path) { public Optional selectDirectory(Component c, String title, File file) { chooser.setDialogTitle(title); if (file == null || !StringUtils.isBlank(file.getAbsolutePath())) { - chooser.setCurrentDirectory(Objects.requireNonNullElseGet(memory, () -> new File("."))); + chooser.setCurrentDirectory(Objects.requireNonNullElseGet(memory.toFile(), () -> new File("."))); } else { chooser.setCurrentDirectory(file); } int result = chooser.showOpenDialog(c); if (result == JFileChooser.APPROVE_OPTION) { - memory = chooser.getSelectedFile(); + memory = chooser.getSelectedFile().toPath(); return Optional.of(chooser.getSelectedFile().toPath()); } return Optional.empty(); @@ -51,12 +52,4 @@ public Optional selectDirectory(Component c, String title, File file) { public Optional selectDirectory(Component c, String title) { return selectDirectory(c, title, memory); } - - public void setMemory(Path memory) { - this.memory = memory.toFile(); - } - - public Path getMemory() { - return memory.toPath(); - } } 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 53739983..2f0d654e 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 @@ -8,6 +8,7 @@ import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.Nullable; public class PanelCheckBox extends JPanel { @Serial @@ -95,7 +96,7 @@ public boolean isSelected() { return checkbox.isSelected(); } - public JPanel addToPanel(Container parent, Object constraints=null) { + public JPanel addToPanel(Container parent, @Nullable Object constraints=null) { parent.addComponent(this, constraints); return this.panel; } 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 0c3d1f9a..e27317f4 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 @@ -1,7 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.extra; import javax.swing.*; -import javax.swing.plaf.basic.*; +import javax.swing.plaf.basic.BasicComboBoxRenderer; import java.awt.*; import java.io.Serial; import java.util.ArrayList; @@ -37,6 +37,7 @@ public Component getListCellRendererComponent(JList list, Object value, int inde }); } + @SafeVarargs public static PartialDisableComboBox of(T... items) { return new PartialDisableComboBox<>(items); } 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 4f76f0d8..70b5e18b 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 @@ -4,6 +4,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.multisubdownloader.settings.model.structure.MovieStructureTag; import org.lodder.subtools.multisubdownloader.settings.model.structure.SerieStructureTag; @@ -96,11 +97,11 @@ public Path build(Release release) { } } - public String buildSubtitle(Release release, Subtitle sub, String filename, Integer version) { + public String buildSubtitle(Release release, Subtitle sub, String filename, @Nullable Integer version) { return buildSubtitle(release, filename, sub.language, version); } - public String buildSubtitle(Release release, String filename, Language language, Integer version) { + public String buildSubtitle(Release release, String filename, Language language, @Nullable Integer version) { final String extension = "." + release.extension; String subFileName = filename; if (version != null) { 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 31bb43d6..5c56675a 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 @@ -41,7 +41,7 @@ public enum SettingValue { .defaultValue(0)), LAST_OUTPUT_DIR(createSettingPath() .rootElementFunction(SettingsControl::getSettings) - .valueGetter(settings -> MemoryFolderChooser.getInstance().getMemory()) + .valueGetter(settings -> MemoryFolderChooser.getInstance().memory) .valueSetter(Settings::setLastOutputDir) .defaultValue(Path.of(""))), From 90771d028f9ac1f453c9876e9f2aa1e717ebbaa4 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Tue, 29 Apr 2025 22:14:00 +0200 Subject: [PATCH 50/58] changes --- MultiSubDownloader/pom.xml | 10 ++--- .../actions/SearchAction.java | 2 +- .../gui/extra/PanelCheckBox.java | 4 +- .../gui/extra/TitlePanel.java | 5 ++- .../gui/panels/InputPanel.java | 38 ++++++------------- .../gui/panels/SearchFileInputPanel.java | 4 +- .../gui/panels/SearchTextInputPanel.java | 4 +- .../settings/model/Settings.java | 2 +- .../tvsubtitles/JTVSubtitlesApi.java | 2 +- .../UserInteractionHandlerGUI.java | 2 +- 10 files changed, 29 insertions(+), 44 deletions(-) diff --git a/MultiSubDownloader/pom.xml b/MultiSubDownloader/pom.xml index 423373a2..11d3f7dc 100644 --- a/MultiSubDownloader/pom.xml +++ b/MultiSubDownloader/pom.xml @@ -166,11 +166,11 @@ manifold-params ${manifold.version} - - - - - + + systems.manifold + manifold-strings + ${manifold.version} + 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 9ebef8f9..fba9c4be 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 @@ -94,7 +94,7 @@ private void search() throws ActionException { /* Tell the manager which providers to use */ this.subtitleProviderStore.getAllProviders().stream() - .filter(subtitleProvider -> settings.isSerieSource(subtitleProvider.subtitleSource)) + .filter(subtitleProvider -> settings.useSerieSource(subtitleProvider.subtitleSource)) .forEach(searchManager::addProvider); /* Tell the manager which releases to search. */ 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 2f0d654e..52ae5c4d 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 @@ -21,7 +21,7 @@ public PanelCheckBox(JCheckBox checkbox, boolean panelOnNewLine, LayoutManager panelLayout=new MigLayout("insets 0, novisualpadding, fillx"), boolean addVerticalSeparator=false, - int leftGap=0) { + int leftGap=20) { super(new MigLayout("insets 0, novisualpadding, fillx")); this.checkbox = checkbox; @@ -98,6 +98,6 @@ public boolean isSelected() { public JPanel addToPanel(Container parent, @Nullable Object constraints=null) { parent.addComponent(this, constraints); - return this.panel; + return panel; } } 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 6aab4734..083413ff 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 @@ -6,6 +6,7 @@ import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.Nullable; public class TitlePanel extends JPanel { @Serial @@ -30,8 +31,8 @@ public TitlePanel(String title, super.add(this.panel = new JPanel(panelLayoutNew), "growx, span"); } - public JPanel addToPanel(Container parent, Object constraints=null) { + public JPanel addToPanel(Container parent, @Nullable Object constraints=null) { parent.addComponent(this, constraints); - return this.panel; + return panel; } } 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 6826108d..6a255bad 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,8 +1,11 @@ package org.lodder.subtools.multisubdownloader.gui.panels; +import static manifold.ext.props.rt.api.PropOption.*; + import javax.swing.*; import java.io.Serial; +import manifold.ext.props.rt.api.val; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.SearchAction; import org.lodder.subtools.sublibrary.Language; @@ -11,46 +14,27 @@ public abstract sealed class InputPanel extends JPanel permits SearchFileInputPa @Serial private static final long serialVersionUID = 7753220002440733463L; - private JButton btnSearch; - private JComboBox cbxLanguage; - - InputPanel() { - createComponents(); - } + @val JButton searchButton = new JButton(Messages.getText("InputPanel.SearchForSubtitles")); + @val(Protected) JComboBox languageCbx = + new JComboBox<>(Language.values()).toMessageStringRenderer(Language::getMsgCode); public Language getSelectedLanguage() { - return cbxLanguage.getSelectedValue(); + return languageCbx.getSelectedValue(); } public void setSelectedLanguage(Language language) { - cbxLanguage.setSelectedItem(language); + languageCbx.setSelectedItem(language); } public void addSearchAction(SearchAction searchAction) { - if (searchAction != null) { - btnSearch.addActionListener(event -> new Thread(searchAction).start()); - } + searchButton.addActionListener(_ -> new Thread(searchAction).start()); } public void enableSearchButton() { - btnSearch.setEnabled(true); + searchButton.setEnabled(true); } public void disableSearchButton() { - this.btnSearch.setEnabled(false); - } - - protected JButton getSearchButton() { - return this.btnSearch; - } - - protected JComboBox getLanguageCbx() { - return this.cbxLanguage; - } - - private void createComponents() { - cbxLanguage = new JComboBox<>(Language.values()).toMessageStringRenderer(Language::getMsgCode); - - btnSearch = new JButton(Messages.getText("InputPanel.SearchForSubtitles")); + this.searchButton.setEnabled(false); } } 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 0bf87091..a524af2e 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 @@ -31,9 +31,9 @@ private void addComponentsToPanel() { 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(searchButton, "cell 0 5 3 1,alignx center"); add(new JLabel(getText("MainWindow.SelectSubtitleLanguage")), "cell 2 2"); - add(getLanguageCbx(), "cell 3 2"); + add(languageCbx, "cell 3 2"); } private void createComponents() { 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 5ae16b31..eea40fb0 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 @@ -39,8 +39,8 @@ private void addComponentsToPanel() { this.add(new JLabel(getText("App.Episode")), "cell 5 1,alignx trailing"); this.add(txtInputEpisode, "cell 6 1,growx"); 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"); + this.add(languageCbx, "cell 4 2 2 1,growx"); + this.add(searchButton, "cell 2 4 2 1"); } private void setupListeners() { 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 c106884b..79af3894 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 @@ -80,7 +80,7 @@ public boolean hasDefaultFolders() { return !defaultIncomingFolders.isEmpty(); } - public boolean isSerieSource(SubtitleSource subtitleSource) { + public boolean useSerieSource(SubtitleSource subtitleSource) { // TODO: dynamically inject SubtitleProvider to settings return switch (subtitleSource) { case ADDIC7ED -> serieSourceAddic7ed; 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 437b2eac..5d842efc 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 @@ -143,7 +143,7 @@ private Optional getEpisodeUrl(String showUrl, int season, int episode) .map(element -> formattedSeasonEpisode.equals(element.text())) .orElse(false)) .map(element -> DOMAIN + "/" + - element.selectNthByTag("td", 2).selectFirstByTag("a").attr("href")) + element.selectNthByTag("td", 1).selectFirstByTag("a").attr("href")) .findAny(); } catch (Exception e) { throw new TvSubtitlesException(e); 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 501ec0d9..95105e76 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 @@ -28,7 +28,7 @@ public Optional selectFromList(Iterable options, String message, synchronized (LOCK) { ElementWrapper[] wrappedOptions = options.stream() .map(option -> new ElementWrapper<>(option, toStringMapper == null ? String::valueOf : toStringMapper)) - .toTypedArray(); + .toArray(ElementWrapper[]::new); return Optional.ofNullable( (ElementWrapper) JOptionPane.showInputDialog(frame, message, title, JOptionPane.DEFAULT_OPTION, null, wrappedOptions, wrappedOptions[0])) From 9709065e63b78e4f4d8abfeb7c983b07e3fc77c1 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Tue, 29 Apr 2025 22:47:22 +0200 Subject: [PATCH 51/58] changes --- .../subtools/multisubdownloader/actions/SearchAction.java | 1 + .../subtitleproviders/podnapisi/JPodnapisiApi.java | 2 +- .../subtools/multisubdownloader/workers/SearchManager.java | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) 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 fba9c4be..405c9697 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 @@ -93,6 +93,7 @@ private void search() throws ActionException { this); /* Tell the manager which providers to use */ + searchManager.reset(); this.subtitleProviderStore.getAllProviders().stream() .filter(subtitleProvider -> settings.useSerieSource(subtitleProvider.subtitleSource)) .forEach(searchManager::addProvider); 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 52c6dc97..26c5ecbd 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 @@ -90,7 +90,7 @@ private List getSubtitles(SerieMapping providerSeri } catch (Exception e) { throw new PodnapisiException(e); } - }); + }, new Retry(1, ex -> ex instanceof HttpClientException e && e.responseCode >= 500, 1 Second)); } 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 531ca5a0..71ee122a 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 @@ -47,6 +47,12 @@ public SearchManager(Settings settings, Language language, SearchProgressListene this.onFound = onFound; } + public void reset() { + queue.clear(); + workers.clear(); + scoreCalculators.clear(); + } + public void addProvider(SubtitleProvider provider) { if (this.workers.containsKey(provider)) { return; From 20305600647369b9f01b1771a5e248104ef11220 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 30 Apr 2025 23:54:20 +0200 Subject: [PATCH 52/58] changes --- .../java/util/regex/Matcher/MatcherExt.java | 20 +++++++++++ .../sublibrary/PageContentParams.java | 10 ++++-- .../sublibrary/control/VideoPatterns.java | 1 + .../sublibrary/util/http/CookieManager.java | 35 ++++++++++--------- .../sublibrary/util/http/HttpClient.java | 34 +++++++++--------- 5 files changed, 64 insertions(+), 36 deletions(-) create mode 100644 SubLibrary/src/main/java/extensions/java/util/regex/Matcher/MatcherExt.java diff --git a/SubLibrary/src/main/java/extensions/java/util/regex/Matcher/MatcherExt.java b/SubLibrary/src/main/java/extensions/java/util/regex/Matcher/MatcherExt.java new file mode 100644 index 00000000..5a0de0fa --- /dev/null +++ b/SubLibrary/src/main/java/extensions/java/util/regex/Matcher/MatcherExt.java @@ -0,0 +1,20 @@ +package extensions.java.util.regex.Matcher; + +import java.util.Optional; +import java.util.regex.Matcher; + +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; + +@Extension +public class MatcherExt { + + public static Optional getMatch(@This Matcher matcher) { + return matcher.getMatch(0); + } + + public static Optional getMatch(@This Matcher matcher, int idx) { + return matcher.find() ? Optional.of(matcher.group(idx)) : Optional.empty(); + } + +} diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java index fa6793ba..7ccb0e60 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/PageContentParams.java @@ -1,17 +1,21 @@ package org.lodder.subtools.sublibrary; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.cache.CacheType; +import org.lodder.subtools.sublibrary.util.http.CookieManager; public record PageContentParams(String url, CacheType cacheType, String userAgent, - Manager.Retry retry) { + Manager.Retry retry, + @Nullable CookieManager cookieManager) { public static PageContentParams params(String url, CacheType cacheType=CacheType.NONE, String userAgent="Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)", - Manager.Retry retry=Manager.Retry.NONE) { - return new PageContentParams(url, cacheType, userAgent, retry); + Manager.Retry retry=Manager.Retry.NONE, + @Nullable CookieManager cookieManager=null) { + return new PageContentParams(url, cacheType, userAgent, retry, cookieManager); } public static PageContentParams url(String url) { 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 0eb61203..aeab5f82 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 @@ -101,6 +101,7 @@ public enum Source implements RegexPattern { WEBRIP("webrip", "webrip", false, false), RERIP("rerip", "rerip", false, false), WEBDL("web-dl", "web[-.]?dl", false, false), + WEB("web", "web", false, false), TS("ts", "ts", false, true), DVD_SCREENER("dvdscreener", "dvdscreener", false, true), R5("r5", "r5", false, true), diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java index b1e22b7c..d6741fc3 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/util/http/CookieManager.java @@ -6,6 +6,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -61,17 +62,7 @@ public void storeCookies(URLConnection conn) { String domain = getDomainFromHost(conn.getURL().getHost()); // this is where we will store cookies for this domain - Map> domainStore; - - // now let's check the store to see if we have an entry for this domain - if (store.containsKey(domain)) { - // we do, so lets retrieve it from the store - domainStore = store.get(domain); - } else { - // we don't, so let's create it and put it in the store - domainStore = new HashMap<>(); - store.put(domain, domainStore); - } + Map> domainStore = store.computeIfAbsent(domain, _ -> new HashMap<>()); // OK, now we are ready to get the cookies out of the URLConnection @@ -102,12 +93,18 @@ public void storeCookies(URLConnection conn) { } } - public void storeCookies(String domain, Map cookieMap) { - if (cookieMap == null || cookieMap.isEmpty()) { - return; - } + public CookieManager storeCookie(String domain, String cookieName, String cookieValue) { Map> domainStore = store.computeIfAbsent(domain, _ -> new HashMap<>()); - cookieMap.forEach((k, v) -> domainStore.put(k, Map.of(k, v))); + domainStore.put(cookieName, Map.of(cookieName, cookieValue)); + return this; + } + + public CookieManager storeCookies(String domain, Map cookieMap) { + if (cookieMap != null && !cookieMap.isEmpty()) { + Map> domainStore = store.computeIfAbsent(domain, _ -> new HashMap<>()); + cookieMap.forEach((k, v) -> domainStore.put(k, Map.of(k, v))); + } + return this; } /** @@ -186,4 +183,10 @@ private boolean comparePaths(String cookiePath, String targetPath) { public String toString() { return store.toString(); } + + public String toString(String domain) { + return store.computeIfAbsent(domain, _ -> new HashMap<>()).entrySet().stream() + .sorted(Comparator.comparing(Entry::getKey)) + .map(e -> e.getKey() + "=" + e.getValue().get(e.getKey()) + "\n").reduce("", (a, b) -> a + b); + } } 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 a1656943..b73c460e 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 @@ -28,21 +28,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class HttpClient { - - private final CookieManager cookieManager; +public record HttpClient(CookieManager cookieManager=new CookieManager()) { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class); - public HttpClient(CookieManager cookieManager=new CookieManager()) { - this.cookieManager = cookieManager; - } - - public String doGet(URL url, String userAgent) throws IOException, HttpClientException { + public String doGet(URL url, String userAgent, CookieManager cookieManager=null) throws IOException, + HttpClientException { HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); - cookieManager.setCookies(conn); + getCookieManager(cookieManager).setCookies(conn); if (StringUtils.isNotBlank(userAgent)) { conn.setRequestProperty(HttpHeaders.USER_AGENT, userAgent); } @@ -57,7 +52,8 @@ public String doGet(URL url, String userAgent) throws IOException, HttpClientExc } } - public String doPost(URL url, String userAgent, Map data) throws HttpClientException { + public String doPost(URL url, String userAgent, Map data, CookieManager cookieManager=null) + throws HttpClientException { HttpURLConnection conn = null; try { @@ -66,7 +62,7 @@ public String doPost(URL url, String userAgent, Map data) throws .collect(Collectors.joining("&")); conn = (HttpURLConnection) url.openConnection(); - cookieManager.setCookies(conn); + getCookieManager(cookieManager).setCookies(conn); conn.setRequestMethod("POST"); if (StringUtils.isNotBlank(userAgent)) { conn.setRequestProperty(HttpHeaders.USER_AGENT, userAgent); @@ -84,10 +80,10 @@ public String doPost(URL url, String userAgent, Map data) throws out.flush(); } - cookieManager.storeCookies(conn); + getCookieManager(cookieManager).storeCookies(conn); if (conn.responseCode == 302 && isUrl(conn.getHeaderField(HttpHeaders.LOCATION))) { - return doGet(new URI(conn.getHeaderField(HttpHeaders.LOCATION)).toURL(), userAgent); + return doGet(new URI(conn.getHeaderField(HttpHeaders.LOCATION)).toURL(), userAgent, cookieManager); } return InputStreamExt.asString(conn.getInputStream(), StandardCharsets.UTF_8); } catch (IOException | URISyntaxException e) { @@ -99,12 +95,12 @@ public String doPost(URL url, String userAgent, Map data) throws } } - public boolean doDownloadFile(URL url, final Path file) { + public boolean doDownloadFile(URL url, final Path file, CookieManager cookieManager=null) { LOGGER.debug("doDownloadFile: URL [{}], file [{}]", url, file); boolean success = true; try (InputStream in = url.getFile().endsWith(".gz") ? - new GZIPInputStream(url.openStream()) : getInputStream(url)) { + new GZIPInputStream(url.openStream()) : getInputStream(url, getCookieManager(cookieManager))) { byte[] data = in.readAllBytes(); if (url.getFile().endsWith(".zip") || PathExt.isZipFile(new ByteArrayInputStream(data))) { @@ -129,7 +125,7 @@ public boolean doDownloadFile(URL url, final Path file) { return success; } - private InputStream getInputStream(URL url) throws Exception { + private InputStream getInputStream(URL url, CookieManager cookieManager=null) throws Exception { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); cookieManager.setCookies(conn); conn.addRequestProperty(HttpHeaders.USER_AGENT, "Mozilla"); @@ -151,7 +147,7 @@ private InputStream getInputStream(URL url) throws Exception { newUrl = new URI("%s://%s/%s".formatted(url.protocol, conn.getURL().host, locationHeader.trim().replace(" ", "%20"))).toURL(); } - return getInputStream(newUrl); + return getInputStream(newUrl, cookieManager); } throw new Exception("error: " + status); } else { @@ -178,4 +174,8 @@ public String downloadText(String url) throws IOException { public void storeCookies(String domain, Map cookieMap) { cookieManager.storeCookies(domain, cookieMap); } + + private CookieManager getCookieManager(CookieManager cookieManager) { + return cookieManager == null ? this.cookieManager : cookieManager; + } } From f43698c34f821803803241355cf5cc07959f828e Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 30 Apr 2025 23:54:41 +0200 Subject: [PATCH 53/58] refactor tvsubtitles --- .../adapters/JTVsubtitlesAdapter.java | 8 +- .../tvsubtitles/JTVSubtitlesApi.java | 181 ++++++++++-------- .../model/TVSubtitlesSubtitleDescriptor.java | 7 +- 3 files changed, 103 insertions(+), 93 deletions(-) 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 6b68c375..bb962157 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 @@ -11,7 +11,6 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; -import org.apache.commons.lang3.StringUtils; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.JTVSubtitlesApi; @@ -109,12 +108,9 @@ public Set convertToSubtitles(TvRelease tvRelease, Collection getUrisForSerieName(String serieName) throws TvSubt 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); + // https://www.tvsubtitles.net/setlang.php?page=/tvshow-3219-2.html&setlang1=es + Set results = new HashSet<>(); + Optional episodeRow = getSeasonSubtitleInfo(providerSerieId.providerId, season, language).filter( + row -> row.isSameEpisode(season, episode)).findAny(); + if (episodeRow.isPresent()) { + for (String url : episodeRow.get().urls) { + results.addAll(getSubtitles(url)); + } + return results; + } + return results; } - private Set getSubtitles(String episodeUrl, Language language) + private List getSeasonSubtitleInfo(String providerSerieId, int season, Language language) throws TvSubtitlesException { - return manager.getCache(CacheType.MEMORY, subtitleSource.name() + "subtitles-$episodeUrl-$language") + return manager.getCache(CacheType.MEMORY, + subtitleSource.name() + "subtitleInfo-$providerSerieId-$season-$language") .getCollection(() -> { - Set lSubtitles = new HashSet<>(); try { - Elements searchEpisodes = - manager.getAsJsoupDocument(PageContentParams.params( - url:episodeUrl.replace(".html", "-" + language.langCode + ".html"), - userAgent:"")) - .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-")) { - continue; - } - Document subtitlePageDoc = manager.getAsJsoupDocument( - PageContentParams.params(url:DOMAIN + url, userAgent:"")); - String filename = null; - String rip = null; - String title = null; - String author = null; - Elements subtitlePageTableDoc = subtitlePageDoc.selectAllByClass("subtitle1"); - if (subtitlePageTableDoc.size() != 1) { - continue; - } - for (Element item : subtitlePageTableDoc.getFirst().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; - } - } + String languageCode = getLanguageCode(language); + CookieManager cookieManager = null; + if (languageCode != null) { + cookieManager = new CookieManager().storeCookie("tvsubtitles.net", "setlang", languageCode); } - return lSubtitles; +// DOMAIN + "/setlang.php?page=/$providerSerieId-$season.html&setlang1=$languageCode", + return manager.getAsJsoupDocument(PageContentParams.params( + DOMAIN + "/" + providerSerieId.replace(".html", "-$season.html"), + cookieManager:cookieManager)) + .select("#table4 table tr[bgcolor]") + .stream() + .filter(episodeRow -> StringUtils.isNotBlank(episodeRow.selectFirstByTag("td").text())) + .map(episodeRow -> { + Elements tds = episodeRow.selectAllByTag("td"); + String[] seasonEpisode = tds.get(0).text().split("x"); + List urls = + tds.get(3).select("a").stream().map(elem -> DOMAIN + "/" + elem.attr("href")).toList(); + return new EpisodeRow(Integer.parseInt(seasonEpisode[0]), + Integer.parseInt(seasonEpisode[1]), urls); + }).filter(episodeRow -> !episodeRow.urls.isEmpty()).toList(); } catch (Exception e) { throw new TvSubtitlesException(e); } }); } - private Optional getEpisodeUrl(String showUrl, int season, int episode) throws TvSubtitlesException { - return manager.getCache(CacheType.MEMORY, subtitleSource.name() + "-episodeUrl-$showUrl-$season-$episode") - .getOptional(() -> { + private List getSubtitles(String episodeUrl) + throws TvSubtitlesException { + return manager.getCache(CacheType.MEMORY, subtitleSource.name() + "subtitles-$episodeUrl") + .getCollection(() -> { try { - String formattedSeasonEpisode = - season + "x" + (episode < 10 ? "0" + episode : String.valueOf(episode)); - - return manager.getAsJsoupDocument(PageContentParams.params( - url:showUrl.replace(".html", "-$season.html"), - userAgent:"")) - .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.selectNthByTag("td", 1).selectFirstByTag("a").attr("href")) - .findAny(); - } catch (Exception e) { + return manager.getAsJsoupDocument(PageContentParams.url(episodeUrl)) + .select(".left_articles > div[class^='subtitle']") + .stream().map(subtitleElement -> { + TVSubtitlesSubtitleDescriptorBuilder subtitleBuilder = + TVSubtitlesSubtitleDescriptor.builder(); + for (Element titleElement : subtitleElement.select(".subtitle_grid > div > img[title]")) { + String value = + ((Element) titleElement.parent()).nextElementSibling().nextElementSibling().text(); + switch (titleElement.attr("title")) { + case "episode title" -> subtitleBuilder.title(value); + case "rip" -> subtitleBuilder.source(Source.fromValue(value)); + case "release" -> subtitleBuilder.releaseGroup(value); + case "filename" -> subtitleBuilder.filename(value); + default -> { + } + } + } + return subtitleBuilder.build(); + }).toList(); + } catch (ManagerException e) { throw new TvSubtitlesException(e); } }); } + + private record EpisodeRow(int season, int episode, List urls) implements Serializable { + public boolean isSameEpisode(int season, int episode) { + return this.season == season && this.episode == episode; + } + } + + private String getLanguageCode(Language language) { + return switch (language) { + case ENGLISH -> "en"; + case SPANISH -> "es"; + case FRENCH -> "fr"; + case GERMAN -> "de"; + case RUSSIAN -> "ru"; + case UKRAINIAN -> "ua"; + case ITALIAN -> "it"; + case GREEK -> "gr"; + case ARABIC -> "ar"; + case HUNGARIAN -> "hu"; + case POLISH -> "pl"; + case TURKISH -> "tr"; + case DUTCH -> "nl"; + case PORTUGUESE -> "pt"; + case SWEDISH -> "sv"; + case DANISH -> "da"; + case FINNISH -> "fi"; + case KOREAN -> "ko"; + case CHINESE_SIMPLIFIED, CHINESE_TRADITIONAL -> "cn"; + case JAPANESE -> "jp"; + case BULGARIAN -> "bg"; + case CZECH -> "cz"; + case ROMANIAN -> "ro"; + default -> null; + }; + } } 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 b269c756..6d94b18d 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 @@ -5,16 +5,17 @@ import lombok.Builder; import manifold.ext.props.rt.api.val; -import org.jspecify.annotations.Nullable; +import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; @Builder public class TVSubtitlesSubtitleDescriptor implements Serializable { @Serial private static final long serialVersionUID = 6423513286301479905L; + @val String title; @val String filename; @val String url; - @val String rip; - @val @Nullable String author; + @val Source source; + @val String releaseGroup; } From 85fdf75638a82c9486e6b11476a579262212d519 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Wed, 30 Apr 2025 23:55:13 +0200 Subject: [PATCH 54/58] use cookiemanager --- .../lodder/subtools/sublibrary/Manager.java | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) 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 a146ae6e..9f336a9b 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -38,6 +38,7 @@ import org.lodder.subtools.sublibrary.cache.InMemoryCache; import org.lodder.subtools.sublibrary.util.Nothing; import org.lodder.subtools.sublibrary.util.Sleep; +import org.lodder.subtools.sublibrary.util.http.CookieManager; import org.lodder.subtools.sublibrary.util.http.HttpClient; import org.lodder.subtools.sublibrary.util.http.HttpClientException; import org.lodder.subtools.sublibrary.xml.XMLHelper; @@ -110,7 +111,8 @@ public org.jsoup.nodes.Document postAsJsoupDocument() throws ManagerException { public String get(PageContentParams params) throws ManagerException { return switch (params.cacheType) { case NONE -> getContentWithoutCache(params); - case MEMORY -> inMemoryCache.getOrPut(params.url, () -> getContentWithoutCache(params)); + case MEMORY -> inMemoryCache.getOrPut(params.url + params.cookieManager(), + () -> getContentWithoutCache(params)); case DISK -> throw new IllegalArgumentException("Unexpected value: " + params.cacheType); }; } @@ -157,21 +159,23 @@ private String getAsJsonString(PageContentParams params) throws ManagerException } private String getContentWithoutCache(PageContentParams params) throws ManagerException { - return getContentWithoutCache(params.url, params.userAgent, params.retry); + return getContentWithoutCache(params.url, params.userAgent, params.retry, params.cookieManager); } - private String getContentWithoutCache(String url, String userAgent, Retry retry) throws ManagerException { + private String getContentWithoutCache(String url, String userAgent, Retry retry, + CookieManager cookieManager) throws ManagerException { try { - return httpClient.doGet(new URI(url).toURL(), userAgent); + return httpClient.doGet(new URI(url).toURL(), userAgent, cookieManager); } catch (HttpClientException e) { if (retry.canRetry() && retry.predicate.test(e)) { - return getContentWithoutCache(url, userAgent, retry.decreaseRetries().sleep()); + return getContentWithoutCache(url, userAgent, retry.decreaseRetries().sleep(), cookieManager); } throw new ManagerException( - "Error occurred with httpclient response: %s %s".formatted(e.responseCode, e.responseMessage), e); + "Error occurred with httpclient response: %s %s while accessing %s".formatted(e.responseCode, + e.responseMessage, url), e); } catch (IOException e) { if (retry.canRetry() && retry.predicate.test(e)) { - return getContentWithoutCache(url, userAgent, retry.decreaseRetries()); + return getContentWithoutCache(url, userAgent, retry.decreaseRetries(), cookieManager); } throw new ManagerException(e); } catch (URISyntaxException e) { @@ -321,7 +325,7 @@ public void store(ValueIntf value, Retry retry=Retry.NONE, boolean storeAsTempValue=false, boolean storeTempNullValue=false, @Nullable Time timeToLive=null) throws X { - V object = value.supplier != null ? executeSupplier(value.supplier, retry) : value.value; + Object object = value.getBaseValue(retry); Time ttl = null; if (storeAsTempValue || (storeTempNullValue && object == null)) { ttl = getTemporaryTimeToLive().map(time -> time * 2) @@ -370,6 +374,8 @@ public CacheKeyFilter getCache(CacheType cacheType, Predicate keyFilter) public interface ValueIntf { @val V value; @val ThrowingSupplier supplier; + + Object getBaseValue(Retry retry) throws X; } public static class Value implements ValueIntf { @@ -388,6 +394,10 @@ private Value(V value, ThrowingSupplier supplier) { this.value = value; this.supplier = supplier; } + + public Object getBaseValue(Retry retry) throws X { + return supplier != null ? executeSupplier(supplier, retry) : value; + } } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @@ -405,11 +415,14 @@ public static OptionalValue return new OptionalValue<>(null, supplier); } - private OptionalValue(Optional value, ThrowingSupplier, X> supplier) { this.value = value; this.supplier = supplier; } + + public Object getBaseValue(Retry retry) throws X { + return supplier != null ? executeSupplier(supplier, retry).orElse(null) : value.orElse(null); + } } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @@ -429,6 +442,11 @@ private OptionalIntValue(OptionalInt value, ThrowingSupplier sup this.value = value; this.supplier = supplier; } + + public Object getBaseValue(Retry retry) throws X { + return supplier != null ? executeSupplier(supplier, retry).mapToObj(i -> i).orElse(null) : + value.mapToObj(i -> i).orElse(null); + } } @@ -449,6 +467,10 @@ private CollectionValue(C value, ThrowingSupplier supplier) { this.value = value; this.supplier = supplier; } + + public Object getBaseValue(Retry retry) throws X { + return supplier != null ? executeSupplier(supplier, retry) : value; + } } // ############## \\ From 9a9803bacfbe4517f929ebaf40a903de48f09922 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 1 May 2025 09:43:58 +0200 Subject: [PATCH 55/58] remove subscene in gui --- .../gui/panels/preference/SerieProvidersPanel.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 a1a7bb6e..8b675691 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 @@ -37,7 +37,7 @@ public class SerieProvidersPanel extends JPanel implements PreferencePanelIntf { private final JCheckBox chkUserOpenSubtitlesLogin; private final MyTextFieldString txtOpenSubtitlesUsername; private final MyPasswordField txtOpenSubtitlesPassword; - private final JCheckBox chkSourceSubscene; + // private final JCheckBox chkSourceSubscene; private final JCheckBox chkSourceLocal; private final JListWithImages localSourcesFoldersList; @@ -93,7 +93,7 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { MyPasswordField.builder().requireValue().build().columns(20))); // SUBSCENE - this.chkSourceSubscene = new JCheckBox("Subscene").addTo(titlePanel, "wrap"); +// this.chkSourceSubscene = new JCheckBox("Subscene").addTo(titlePanel, "wrap"); // LOCAL this.chkSourceLocal = new JCheckBox(getText("PreferenceDialog.Local")); @@ -136,7 +136,7 @@ public void loadPreferenceSettings() { chkUserOpenSubtitlesLogin.setSelected(settings.loginOpenSubtitlesEnabled); txtOpenSubtitlesUsername.setText(settings.loginOpenSubtitlesUsername); txtOpenSubtitlesPassword.setText(settings.loginOpenSubtitlesPassword); - chkSourceSubscene.setSelected(settings.serieSourceSubscene); +// chkSourceSubscene.setSelected(settings.serieSourceSubscene); chkSourceLocal.setSelected(settings.serieSourceLocal); settings.localSourcesFolders.forEach(path -> localSourcesFoldersList.addItem(PathMatchType.FOLDER.image, path)); } @@ -154,7 +154,7 @@ public void savePreferenceSettings() { settings.loginOpenSubtitlesEnabled = chkUserOpenSubtitlesLogin.isSelected(); settings.loginOpenSubtitlesUsername = txtOpenSubtitlesUsername.getText(); settings.loginOpenSubtitlesPassword = new String(txtOpenSubtitlesPassword.getPassword()); - settings.serieSourceSubscene = chkSourceSubscene.isSelected(); + settings.serieSourceSubscene = false; //chkSourceSubscene.isSelected(); settings.serieSourceLocal = chkSourceLocal.isSelected(); settings.localSourcesFolders = localSourcesFoldersList.stream().map(LabelPanel::getObject).toList(); } From 45af850a5860309d807fb986268121aa25b39312 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 1 May 2025 10:02:00 +0200 Subject: [PATCH 56/58] Value refactoring --- .../subtitleproviders/adapters/Adapter.java | 3 +- .../lodder/subtools/sublibrary/Manager.java | 112 ++++-------------- .../sublibrary/data/tvdb/TheTvdbAdapter.java | 6 +- 3 files changed, 29 insertions(+), 92 deletions(-) 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 5937ebdc..72076759 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 @@ -22,7 +22,6 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesHasher; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager.CacheKey; -import org.lodder.subtools.sublibrary.Manager.CollectionValue; import org.lodder.subtools.sublibrary.Manager.Value; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.data.ProviderSerieId; @@ -210,7 +209,7 @@ default Optional getProviderSerieId(String serieName, String serie timeToLive:serieNameCache.getTemporaryTimeToLive().map(v -> v * 2).orElse(1 day), storeAsTempValue:true, storeTempNullValue:true); - previousResultsCache.store(CollectionValue.of(providerSerieIds)); + previousResultsCache.store(Value.ofCollection(providerSerieIds)); } return Optional.empty(); } 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 9f336a9b..93cfdb1c 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -21,9 +21,9 @@ import java.util.function.Predicate; import lombok.AllArgsConstructor; -import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import manifold.science.measures.Time; +import name.falgout.jeffrey.throwing.ThrowingFunction; import name.falgout.jeffrey.throwing.ThrowingSupplier; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -278,7 +278,7 @@ public Optional getOptional( Cache cache = optionalCache.get(); boolean containsKey = cache.contains(key); if (!containsKey && storeTempNullValue) { - store(OptionalValue.of(supplier), retry, storeAsTempValue, storeTempNullValue, timeToLive); + store(Value.ofOptional(supplier), retry, storeAsTempValue, storeTempNullValue, timeToLive); return cache.get(key); } else if (containsKey && !isExpiredTemporary()) { return cache.get(key); @@ -304,7 +304,7 @@ public OptionalInt getOptionalInt( return manager.getOptionalCache(cacheType).mapThrowing(cache -> { boolean containsKey = cache.contains(key); if (!containsKey && storeTempNullValue) { - store(OptionalIntValue.of(supplier), retry, storeAsTempValue, storeTempNullValue, timeToLive); + store(Value.ofOptionalInt(supplier), retry, storeAsTempValue, storeTempNullValue, timeToLive); return cache.get(key).mapToInt(t -> (int) t); } else if (containsKey && !isExpiredTemporary()) { return cache.get(key).mapToInt(t -> (int) t); @@ -321,11 +321,11 @@ public OptionalInt getOptionalInt( }).orElseGetThrowing(supplier); } - public void store(ValueIntf value, + public void store(Value value, Retry retry=Retry.NONE, boolean storeAsTempValue=false, boolean storeTempNullValue=false, @Nullable Time timeToLive=null) throws X { - Object object = value.getBaseValue(retry); + Object object = value.getValue(retry); Time ttl = null; if (storeAsTempValue || (storeTempNullValue && object == null)) { ttl = getTemporaryTimeToLive().map(time -> time * 2) @@ -371,105 +371,43 @@ public CacheKeyFilter getCache(CacheType cacheType, Predicate keyFilter) return new CacheKeyFilter(this, cacheType, keyFilter); } - public interface ValueIntf { - @val V value; - @val ThrowingSupplier supplier; - - Object getBaseValue(Retry retry) throws X; - } - - public static class Value implements ValueIntf { - @override @val V value; - @override @val ThrowingSupplier supplier; - - public static Value of(V value) { - return new Value<>(value, null); - } - - public static Value of(ThrowingSupplier supplier) { - return new Value<>(null, supplier); - } - - private Value(V value, ThrowingSupplier supplier) { - this.value = value; - this.supplier = supplier; - } - - public Object getBaseValue(Retry retry) throws X { - return supplier != null ? executeSupplier(supplier, retry) : value; - } - } - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public static class OptionalValue implements - ValueIntf, X> { - @override @val Optional value; - @override @val ThrowingSupplier, X> supplier; - - public static OptionalValue of(Optional value) { - return new OptionalValue<>(value, null); + public record Value(ThrowingFunction supplier) { + public static Value of(V value) { + return new Value<>(_ -> value); } - public static OptionalValue of( - ThrowingSupplier, X> supplier) { - return new OptionalValue<>(null, supplier); + public static Value of(ThrowingSupplier supplier) { + return new Value<>(retry -> executeSupplier(supplier, retry)); } - private OptionalValue(Optional value, ThrowingSupplier, X> supplier) { - this.value = value; - this.supplier = supplier; + public static Value ofOptional(Optional value) { + return new Value<>(_ -> value.orElse(null)); } - public Object getBaseValue(Retry retry) throws X { - return supplier != null ? executeSupplier(supplier, retry).orElse(null) : value.orElse(null); + public static Value ofOptional(ThrowingSupplier, X> supplier) { + return new Value<>(retry -> executeSupplier(supplier, retry).orElse(null)); } - } - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public static class OptionalIntValue implements ValueIntf { - @override @val OptionalInt value; - @override @val ThrowingSupplier supplier; - public static OptionalIntValue of(OptionalInt value) { - return new OptionalIntValue<>(value, null); + public static Value ofOptionalInt(OptionalInt value) { + return ofOptional(() -> value.mapToObj(i -> i)); } - public static OptionalIntValue of(ThrowingSupplier supplier) { - return new OptionalIntValue<>(null, supplier); - } - - private OptionalIntValue(OptionalInt value, ThrowingSupplier supplier) { - this.value = value; - this.supplier = supplier; - } - - public Object getBaseValue(Retry retry) throws X { - return supplier != null ? executeSupplier(supplier, retry).mapToObj(i -> i).orElse(null) : - value.mapToObj(i -> i).orElse(null); - } - } - - - public static class CollectionValue, V, X extends Exception> implements ValueIntf { - @override @val C value; - @override @val ThrowingSupplier supplier; - - public static , V extends Serializable> CollectionValue of(C value) { - return new CollectionValue<>(value, null); + public static Value ofOptionalInt(ThrowingSupplier supplier) { + return ofOptional(() -> supplier.get().mapToObj(i -> i)); } - public static , V extends Serializable, X extends Exception> CollectionValue of( - ThrowingSupplier supplier) { - return new CollectionValue<>(null, supplier); + public static , V> Value ofCollection(C value) { + return new Value<>(_ -> value); } - private CollectionValue(C value, ThrowingSupplier supplier) { - this.value = value; - this.supplier = supplier; + public static , V, X extends Exception> Value ofCollection(ThrowingSupplier supplier) { + return new Value<>(retry -> executeSupplier(supplier, retry)); } - public Object getBaseValue(Retry retry) throws X { - return supplier != null ? executeSupplier(supplier, retry) : value; + public V getValue(Retry retry) throws X { + return supplier.apply(retry); } } 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 6224dcad..5738e2de 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 @@ -94,15 +94,15 @@ public Optional getSerie(String serieName) { } if (tvdbSerie.isEmpty()) { cache.store( - value:OptionalValue.of(tvdbSerie), + value:Value.ofOptional(tvdbSerie), timeToLive:cache.getTemporaryTimeToLive().map(v -> v * 2).orElseGet(() -> 1 day), storeAsTempValue:true, storeTempNullValue:true); } else { - cache.store(OptionalValue.of(tvdbSerie)); + cache.store(Value.ofOptional(tvdbSerie)); manager.getCache(CacheType.DISK, "$PROVIDER_NAME-serieId-$encodedSerieName") .store( - value:OptionalValue.of( + value:Value.ofOptional( tvdbSerie.map(tvdbS -> new SerieMapping(serieName, String.valueOf(tvdbS.id), tvdbS.serieName))), storeTempNullValue:true); } From d9ad3c59b209fa5889c3ecd6eb96c04c0f908548 Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 1 May 2025 10:27:46 +0200 Subject: [PATCH 57/58] clear searchproviders --- .../gui/actions/search/GuiSearchAction.java | 4 ++++ .../gui/dialog/progress/search/SearchProgressDialog.java | 1 + .../dialog/progress/search/SearchProgressTableModel.java | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) 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 69bb9f7c..bb2183ed 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 @@ -77,4 +77,8 @@ public void onFound(Release release, List subtitles) { searchPanel.inputPanel.enableSearchButton(); } } + + public void reset() { + searchProgressListener.reset(); + } } 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 e67720f4..a52e3f5b 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 @@ -85,6 +85,7 @@ public void completed() { @Override public void reset() { this.completed = false; + tableModel.clear(); } @Override 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 6df8435d..c604098c 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 @@ -2,7 +2,7 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; -import javax.swing.table.*; +import javax.swing.table.DefaultTableModel; import java.io.Serial; import java.util.HashMap; import java.util.Map; @@ -45,4 +45,9 @@ private void createRow(String source, int queue, String release) { Object[] row = { source, queue, release }; this.addRow(row); } + + public void clear() { + rowMap.clear(); + dataVector.removeAllElements(); + } } From 510be1ac88494f6e52b8f4c2deb151354be6f89f Mon Sep 17 00:00:00 2001 From: EotT123 Date: Thu, 1 May 2025 10:58:10 +0200 Subject: [PATCH 58/58] fix TVSubtitles --- .../subtitleproviders/tvsubtitles/JTVSubtitlesApi.java | 2 ++ 1 file changed, 2 insertions(+) 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 bbc27905..4ddd2cca 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 @@ -121,6 +121,8 @@ private List getSubtitles(String episodeUrl) } } } + subtitleBuilder.url(DOMAIN + "/" + subtitleElement.select("a[href^='download-']") + .attr("href")); return subtitleBuilder.build(); }).toList(); } catch (ManagerException e) {