From 6117d24e138074f12664d31910b412920b4f0810 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Sun, 5 Jul 2020 15:09:58 +0200 Subject: [PATCH] Remove old settings tab and obsolete dialogs --- .../java/ctbrec/ui/CamrecApplication.java | 7 +- .../ui/settings/PlayerSettingsDialog.java | 101 -- .../PostProcessingSettingsDialog.java | 101 -- .../java/ctbrec/ui/settings/SettingsTab.java | 1005 ++++------------- .../java/ctbrec/ui/settings/SettingsTab2.java | 290 ----- 5 files changed, 224 insertions(+), 1280 deletions(-) delete mode 100644 client/src/main/java/ctbrec/ui/settings/PlayerSettingsDialog.java delete mode 100644 client/src/main/java/ctbrec/ui/settings/PostProcessingSettingsDialog.java delete mode 100644 client/src/main/java/ctbrec/ui/settings/SettingsTab2.java diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index c0881658..ca6003f0 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -54,7 +54,6 @@ import ctbrec.sites.stripchat.Stripchat; import ctbrec.ui.controls.Dialogs; import ctbrec.ui.news.NewsTab; import ctbrec.ui.settings.SettingsTab; -import ctbrec.ui.settings.SettingsTab2; import ctbrec.ui.tabs.DonateTabFx; import ctbrec.ui.tabs.HelpTab; import ctbrec.ui.tabs.RecordedModelsTab; @@ -89,7 +88,6 @@ public class CamrecApplication extends Application { private Recorder recorder; private OnlineMonitor onlineMonitor; static HostServices hostServices; - private SettingsTab settingsTab; private BorderPane rootPane = new BorderPane(); private HBox statusBar = new HBox(); private Label statusLabel = new Label(); @@ -189,9 +187,7 @@ public class CamrecApplication extends Application { tabPane.getTabs().add(modelsTab); recordingsTab = new RecordingsTab("Recordings", recorder, config, sites); tabPane.getTabs().add(recordingsTab); - settingsTab = new SettingsTab(sites, recorder); - tabPane.getTabs().add(new SettingsTab2(sites, recorder)); - tabPane.getTabs().add(settingsTab); + tabPane.getTabs().add(new SettingsTab(sites, recorder)); tabPane.getTabs().add(new NewsTab()); tabPane.getTabs().add(new DonateTabFx()); tabPane.getTabs().add(new HelpTab()); @@ -259,7 +255,6 @@ public class CamrecApplication extends Application { public void run() { modelsTab.saveState(); recordingsTab.saveState(); - settingsTab.saveConfig(); onlineMonitor.shutdown(); recorder.shutdown(); for (Site site : sites) { diff --git a/client/src/main/java/ctbrec/ui/settings/PlayerSettingsDialog.java b/client/src/main/java/ctbrec/ui/settings/PlayerSettingsDialog.java deleted file mode 100644 index 225d85cf..00000000 --- a/client/src/main/java/ctbrec/ui/settings/PlayerSettingsDialog.java +++ /dev/null @@ -1,101 +0,0 @@ -package ctbrec.ui.settings; - -import java.io.IOException; -import java.io.InputStream; - -import ctbrec.Config; -import ctbrec.Settings; -import ctbrec.ui.controls.Dialogs; -import javafx.application.Platform; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; -import javafx.stage.Modality; -import javafx.stage.Stage; - -public class PlayerSettingsDialog extends Dialog { - - private Scene parent; - private Config config; - private Settings settings; - - private TextField playerParams; - private TextField maxResolution; - - public PlayerSettingsDialog(Scene parent, Config config) { - this.parent = parent; - this.config = config; - this.settings = config.getSettings(); - - initGui(); - } - - private void initGui() { - setTitle("Player Settings"); - setHeaderText("Player Settings"); - getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - initModality(Modality.APPLICATION_MODAL); - setResizable(true); - InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); - Stage stage = (Stage) getDialogPane().getScene().getWindow(); - stage.getIcons().add(new Image(icon)); - if (parent != null) { - stage.getScene().getStylesheets().addAll(parent.getStylesheets()); - } - - GridPane grid = new GridPane(); - grid.setHgap(10); - grid.setVgap(10); - grid.setPadding(new Insets(20, 150, 10, 10)); - - grid.add(new Label("Start parameters"), 0, 0); - playerParams = new TextField(settings.mediaPlayerParams); - grid.add(playerParams, 1, 0); - getDialogPane().setContent(grid); - GridPane.setFillWidth(playerParams, true); - GridPane.setHgrow(playerParams, Priority.ALWAYS); - - Label l = new Label("Maximum resolution (0 = unlimited)"); - grid.add(l, 0, 1); - maxResolution = new TextField(Integer.toString(settings.maximumResolutionPlayer)); - Tooltip tt = new Tooltip("video height, e.g. 720 or 1080"); - l.setTooltip(tt); - maxResolution.setTooltip(tt); - grid.add(maxResolution, 1, 1); - getDialogPane().setContent(grid); - GridPane.setFillWidth(maxResolution, true); - GridPane.setHgrow(maxResolution, Priority.ALWAYS); - - Platform.runLater(playerParams::requestFocus); - - setResultConverter(dialogButton -> { - try { - if (dialogButton == ButtonType.OK) { - saveSettings(); - } - return null; - } catch (IOException e) { - return e; - } - }); - } - - public void saveSettings() throws IOException { - settings.mediaPlayerParams = playerParams.getText(); - String res = maxResolution.getText(); - if (res.matches("\\d+")) { - int newRes = Integer.parseInt(maxResolution.getText()); - if (newRes != Config.getInstance().getSettings().maximumResolutionPlayer) { - settings.maximumResolutionPlayer = newRes; - } - } - config.save(); - } -} diff --git a/client/src/main/java/ctbrec/ui/settings/PostProcessingSettingsDialog.java b/client/src/main/java/ctbrec/ui/settings/PostProcessingSettingsDialog.java deleted file mode 100644 index a008fb94..00000000 --- a/client/src/main/java/ctbrec/ui/settings/PostProcessingSettingsDialog.java +++ /dev/null @@ -1,101 +0,0 @@ -package ctbrec.ui.settings; - -import java.io.IOException; -import java.io.InputStream; - -import ctbrec.Config; -import ctbrec.Settings; -import ctbrec.ui.controls.Dialogs; -import javafx.application.Platform; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; -import javafx.stage.Modality; -import javafx.stage.Stage; - -public class PostProcessingSettingsDialog extends Dialog { - - private Scene parent; - private Config config; - private Settings settings; - - private TextField playerParams; - private TextField maxResolution; - - public PostProcessingSettingsDialog(Scene parent, Config config) { - this.parent = parent; - this.config = config; - this.settings = config.getSettings(); - - initGui(); - } - - private void initGui() { - setTitle("Player Settings"); - setHeaderText("Player Settings"); - getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - initModality(Modality.APPLICATION_MODAL); - setResizable(true); - InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); - Stage stage = (Stage) getDialogPane().getScene().getWindow(); - stage.getIcons().add(new Image(icon)); - if (parent != null) { - stage.getScene().getStylesheets().addAll(parent.getStylesheets()); - } - - GridPane grid = new GridPane(); - grid.setHgap(10); - grid.setVgap(10); - grid.setPadding(new Insets(20, 150, 10, 10)); - - grid.add(new Label("Start parameters"), 0, 0); - playerParams = new TextField(settings.mediaPlayerParams); - grid.add(playerParams, 1, 0); - getDialogPane().setContent(grid); - GridPane.setFillWidth(playerParams, true); - GridPane.setHgrow(playerParams, Priority.ALWAYS); - - Label l = new Label("Maximum resolution (0 = unlimited)"); - grid.add(l, 0, 1); - maxResolution = new TextField(Integer.toString(settings.maximumResolutionPlayer)); - Tooltip tt = new Tooltip("video height, e.g. 720 or 1080"); - l.setTooltip(tt); - maxResolution.setTooltip(tt); - grid.add(maxResolution, 1, 1); - getDialogPane().setContent(grid); - GridPane.setFillWidth(maxResolution, true); - GridPane.setHgrow(maxResolution, Priority.ALWAYS); - - Platform.runLater(playerParams::requestFocus); - - setResultConverter(dialogButton -> { - try { - if (dialogButton == ButtonType.OK) { - saveSettings(); - } - return null; - } catch (IOException e) { - return e; - } - }); - } - - public void saveSettings() throws IOException { - settings.mediaPlayerParams = playerParams.getText(); - String res = maxResolution.getText(); - if (res.matches("\\d+")) { - int newRes = Integer.parseInt(maxResolution.getText()); - if (newRes != Config.getInstance().getSettings().maximumResolutionPlayer) { - settings.maximumResolutionPlayer = newRes; - } - } - config.save(); - } -} diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java index dd226278..1914afd5 100644 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java +++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java @@ -1,808 +1,292 @@ package ctbrec.ui.settings; import static ctbrec.Settings.DirectoryStructure.*; -import static javafx.scene.control.ButtonType.*; +import static ctbrec.Settings.ProxyType.*; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.Moshi; -import com.squareup.moshi.Types; - import ctbrec.Config; import ctbrec.Hmac; -import ctbrec.Model; import ctbrec.Settings; import ctbrec.Settings.DirectoryStructure; -import ctbrec.StringUtil; -import ctbrec.io.ModelJsonAdapter; +import ctbrec.Settings.ProxyType; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; -import ctbrec.ui.AutosizeAlert; import ctbrec.ui.SiteUiFactory; -import ctbrec.ui.controls.Dialogs; -import ctbrec.ui.controls.DirectorySelectionBox; -import ctbrec.ui.controls.ProgramSelectionBox; import ctbrec.ui.controls.range.DiscreteRange; -import ctbrec.ui.controls.range.RangeSlider; +import ctbrec.ui.settings.api.Category; +import ctbrec.ui.settings.api.ExclusiveSelectionProperty; +import ctbrec.ui.settings.api.GigabytesConverter; +import ctbrec.ui.settings.api.Group; +import ctbrec.ui.settings.api.Preferences; +import ctbrec.ui.settings.api.Setting; +import ctbrec.ui.settings.api.SimpleDirectoryProperty; +import ctbrec.ui.settings.api.SimpleFileProperty; +import ctbrec.ui.settings.api.SimpleRangeProperty; import ctbrec.ui.settings.api.ValueConverter; -import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabSelectionListener; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; -import javafx.geometry.HPos; import javafx.geometry.Insets; -import javafx.scene.Node; -import javafx.scene.control.Accordion; -import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.RadioButton; -import javafx.scene.control.ScrollPane; import javafx.scene.control.Tab; -import javafx.scene.control.TextField; import javafx.scene.control.TextInputDialog; -import javafx.scene.control.TitledPane; -import javafx.scene.control.ToggleGroup; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.scene.text.Font; -import javafx.stage.FileChooser; public class SettingsTab extends Tab implements TabSelectionListener { - public static final String PATTERN_NOT_A_DIGIT = "[^\\d]"; private static final Logger LOG = LoggerFactory.getLogger(SettingsTab.class); - private static final int ONE_GIB_IN_BYTES = 1024 * 1024 * 1024; - public static final int CHECKBOX_MARGIN = 6; - private DirectorySelectionBox recordingsDirectory; - private ProgramSelectionBox postProcessing; - private TextField server; - private TextField port; - private TextField servletContext; - private TextField onlineCheckIntervalInSecs; - private TextField leaveSpaceOnDevice; - private TextField minimumLengthInSecs; - private TextField ppThreads; - private TextField userAgent; - private TextField userAgentMobile; - private TextField ffmpegParameters; - private TextField fileExtension; - private CheckBox useAuthentication = new CheckBox(); - private CheckBox useTLS = new CheckBox(); - private CheckBox chooseStreamQuality = new CheckBox(); - private CheckBox multiplePlayers = new CheckBox(); - private CheckBox updateThumbnails = new CheckBox(); - private CheckBox livePreviews = new CheckBox(); - private CheckBox showPlayerStarting = new CheckBox(); - private CheckBox removeRecordingAfterPp = new CheckBox(); - private RadioButton recordLocal; - private ProxySettingsPane proxySettingsPane; - private TextField concurrentRecordings; - private ComboBox splitAfter; - private ComboBox directoryStructure; - private ComboBox startTab; - private RangeSlider resolutionRange; + private List sites; - private Label restartLabel; - private Accordion siteConfigAccordion = new Accordion(); private Recorder recorder; + private boolean initialized = false; + private Config config; + private Settings settings; + + private SimpleStringProperty httpUserAgent; + private SimpleStringProperty httpUserAgentMobile; + private SimpleIntegerProperty overviewUpdateIntervalInSecs; + private SimpleBooleanProperty updateThumbnails; + private SimpleBooleanProperty determineResolution; + private SimpleBooleanProperty chooseStreamQuality; + private SimpleBooleanProperty livePreviews; + private SimpleListProperty startTab; + private SimpleFileProperty mediaPlayer; + private SimpleStringProperty mediaPlayerParams; + private SimpleIntegerProperty maximumResolutionPlayer; + private SimpleBooleanProperty showPlayerStarting; + private SimpleBooleanProperty singlePlayer; + private SimpleListProperty proxyType; + private SimpleStringProperty proxyHost; + private SimpleStringProperty proxyPort; + private SimpleStringProperty proxyUser; + private SimpleStringProperty proxyPassword; + private SimpleDirectoryProperty recordingsDir; + private SimpleListProperty directoryStructure; + private SimpleListProperty splitAfter; + private SimpleRangeProperty resolutionRange; + private List labels = Arrays.asList(0, 240, 360, 480, 600, 720, 960, 1080, 1440, 2160, 4320, 8640); + private List values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); + private DiscreteRange rangeValues = new DiscreteRange<>(values, labels); + private SimpleIntegerProperty concurrentRecordings; + private SimpleIntegerProperty onlineCheckIntervalInSecs; + private SimpleLongProperty leaveSpaceOnDevice; + private SimpleIntegerProperty minimumLengthInSecs; + private SimpleStringProperty ffmpegParameters; + private SimpleStringProperty fileExtension; + private SimpleStringProperty server; + private SimpleIntegerProperty port; + private SimpleStringProperty path; + private SimpleBooleanProperty requireAuthentication; + private SimpleBooleanProperty transportLayerSecurity; + private ExclusiveSelectionProperty recordLocal; + private SimpleFileProperty postProcessing; + private SimpleIntegerProperty postProcessingThreads; + private SimpleBooleanProperty removeRecordingAfterPp; + private IgnoreList ignoreList; public SettingsTab(List sites, Recorder recorder) { this.sites = sites; this.recorder = recorder; setText("Settings"); - createGui(); setClosable(false); - setRecordingMode(recordLocal.isSelected()); + config = Config.getInstance(); + settings = config.getSettings(); + } + + private void initializeProperties() { + httpUserAgent = new SimpleStringProperty(null, "httpUserAgent", settings.httpUserAgent); + httpUserAgentMobile = new SimpleStringProperty(null, "httpUserAgentMobile", settings.httpUserAgentMobile); + overviewUpdateIntervalInSecs = new SimpleIntegerProperty(null, "overviewUpdateIntervalInSecs", settings.overviewUpdateIntervalInSecs); + updateThumbnails = new SimpleBooleanProperty(null, "updateThumbnails", settings.updateThumbnails); + determineResolution = new SimpleBooleanProperty(null, "determineResolution", settings.determineResolution); + chooseStreamQuality = new SimpleBooleanProperty(null, "chooseStreamQuality", settings.chooseStreamQuality); + livePreviews = new SimpleBooleanProperty(null, "livePreviews", settings.livePreviews); + startTab = new SimpleListProperty<>(null, "startTab", FXCollections.observableList(getTabNames())); + mediaPlayer = new SimpleFileProperty(null, "mediaPlayer", settings.mediaPlayer); + mediaPlayerParams = new SimpleStringProperty(null, "mediaPlayerParams", settings.mediaPlayerParams); + maximumResolutionPlayer = new SimpleIntegerProperty(null, "maximumResolutionPlayer", settings.maximumResolutionPlayer); + showPlayerStarting = new SimpleBooleanProperty(null, "showPlayerStarting", settings.showPlayerStarting); + singlePlayer = new SimpleBooleanProperty(null, "singlePlayer", settings.singlePlayer); + proxyType = new SimpleListProperty<>(null, "proxyType", FXCollections.observableList(List.of(DIRECT, HTTP, SOCKS4, SOCKS5))); + proxyHost = new SimpleStringProperty(null, "proxyHost", settings.proxyHost); + proxyPort = new SimpleStringProperty(null, "proxyPort", settings.proxyPort); + proxyUser = new SimpleStringProperty(null, "proxyUser", settings.proxyUser); + proxyPassword = new SimpleStringProperty(null, "proxyPassword", settings.proxyPassword); + recordingsDir = new SimpleDirectoryProperty(null, "recordingsDir", settings.recordingsDir); + directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure", FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_RECORDING))); + splitAfter = new SimpleListProperty<>(null, "splitRecordings", FXCollections.observableList(getSplitOptions())); + resolutionRange = new SimpleRangeProperty<>(rangeValues, "minimumResolution", "maximumResolution", settings.minimumResolution, settings.maximumResolution); + concurrentRecordings = new SimpleIntegerProperty(null, "concurrentRecordings", settings.concurrentRecordings); + onlineCheckIntervalInSecs = new SimpleIntegerProperty(null, "onlineCheckIntervalInSecs", settings.onlineCheckIntervalInSecs); + leaveSpaceOnDevice = new SimpleLongProperty(null, "minimumSpaceLeftInBytes", (long) new GigabytesConverter().convertTo(settings.minimumSpaceLeftInBytes)); + minimumLengthInSecs = new SimpleIntegerProperty(null, "minimumLengthInSeconds", settings.minimumLengthInSeconds); + ffmpegParameters = new SimpleStringProperty(null, "ffmpegMergedDownloadArgs", settings.ffmpegMergedDownloadArgs); + fileExtension = new SimpleStringProperty(null, "ffmpegFileSuffix", settings.ffmpegFileSuffix); + server = new SimpleStringProperty(null, "httpServer", settings.httpServer); + port = new SimpleIntegerProperty(null, "httpPort", settings.httpPort); + path = new SimpleStringProperty(null, "servletContext", settings.servletContext); + requireAuthentication = new SimpleBooleanProperty(null, "requireAuthentication", settings.requireAuthentication); + requireAuthentication.addListener(this::requireAuthenticationChanged); + transportLayerSecurity = new SimpleBooleanProperty(null, "transportLayerSecurity", settings.transportLayerSecurity); + recordLocal = new ExclusiveSelectionProperty(null, "localRecording", settings.localRecording, "Local", "Remote"); + postProcessing = new SimpleFileProperty(null, "postProcessing", settings.postProcessing); + postProcessingThreads = new SimpleIntegerProperty(null, "postProcessingThreads", settings.postProcessingThreads); + removeRecordingAfterPp = new SimpleBooleanProperty(null, "removeRecordingAfterPostProcessing", settings.removeRecordingAfterPostProcessing); } private void createGui() { - // set up main layout, 2 columns with VBoxes 50/50 - GridPane mainLayout = createGridLayout(); - mainLayout.setHgap(15); - mainLayout.setVgap(15); - mainLayout.setPadding(new Insets(15)); - ColumnConstraints cc = new ColumnConstraints(); - cc.setPercentWidth(50); - mainLayout.getColumnConstraints().setAll(cc, cc); - ScrollPane scrollPane = new ScrollPane(mainLayout); - setContent(scrollPane); - GridPane.setFillHeight(scrollPane, true); - GridPane.setFillWidth(scrollPane, true); - GridPane.setHgrow(scrollPane, Priority.ALWAYS); - GridPane.setVgrow(scrollPane, Priority.ALWAYS); - VBox leftSide = new VBox(15); - leftSide.setFillWidth(true); - VBox rightSide = new VBox(15); - rightSide.setFillWidth(true); - GridPane.setHgrow(leftSide, Priority.ALWAYS); - GridPane.setHgrow(rightSide, Priority.ALWAYS); - GridPane.setFillWidth(leftSide, true); - GridPane.setFillWidth(rightSide, true); - mainLayout.add(leftSide, 0, 1); - mainLayout.add(rightSide, 1, 1); - mainLayout.prefWidthProperty().bind(scrollPane.widthProperty()); - - // restart info label - restartLabel = new Label("A restart is required to apply the changes you made!"); - restartLabel.setVisible(false); - restartLabel.setFont(Font.font(24)); - restartLabel.setTextFill(Color.RED); - mainLayout.add(restartLabel, 0, 0); - GridPane.setColumnSpan(restartLabel, 2); - GridPane.setHalignment(restartLabel, HPos.CENTER); - - // left side - leftSide.getChildren().add(createGeneralPanel()); - leftSide.getChildren().add(createRecorderPanel()); - leftSide.getChildren().add(createRecordLocationPanel()); - - //right side - rightSide.getChildren().add(siteConfigAccordion); - ActionSettingsPanel actions = new ActionSettingsPanel(recorder); - rightSide.getChildren().add(actions); - proxySettingsPane = new ProxySettingsPane(this); - rightSide.getChildren().add(createIgnoreListPanel()); - rightSide.getChildren().add(proxySettingsPane); - for (int i = 0; i < sites.size(); i++) { - Site site = sites.get(i); - ConfigUI siteConfig = SiteUiFactory.getUi(site).getConfigUI(); - if(siteConfig != null) { - TitledPane pane = new TitledPane(site.getName(), siteConfig.createConfigPanel()); - siteConfigAccordion.getPanes().add(pane); - } + ignoreList = new IgnoreList(sites); + List siteCategories = new ArrayList<>(); + for (Site site : sites) { + siteCategories.add(Category.of(site.getName(), SiteUiFactory.getUi(site).getConfigUI().createConfigPanel())); } + + Preferences prefs = Preferences.of(new CtbrecPreferencesStorage(config), + Category.of("General", + Group.of("General", + Setting.of("User-Agent", httpUserAgent), + Setting.of("User-Agent mobile", httpUserAgentMobile), + Setting.of("Update overview interval (seconds)", overviewUpdateIntervalInSecs, "Update the thumbnail overviews every x seconds"), + Setting.of("Update thumbnails", updateThumbnails, "The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."), + Setting.of("Display stream resolution in overview", determineResolution), + Setting.of("Enable live previews (experimental)", livePreviews), + Setting.of("Start Tab", startTab), + Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance())) + ), + Group.of("Player", + Setting.of("Player", mediaPlayer), + Setting.of("Start parameters", mediaPlayerParams), + Setting.of("Maximum resolution (0 = unlimited)", maximumResolutionPlayer, "video height, e.g. 720 or 1080"), + Setting.of("Show \"Player Starting\" Message", showPlayerStarting), + Setting.of("Start only one player at a time", singlePlayer) + ) + ), + Category.of("Recorder", + Group.of("Settings", + Setting.of("Recordings Directory", recordingsDir), + Setting.of("Directory Structure", directoryStructure), + Setting.of("Split recordings after (minutes)", splitAfter).converter(SplitAfterOption.converter()), + Setting.of("Manually select stream quality", chooseStreamQuality, "Opens a dialog to select the video resolution before recording"), + Setting.of("Restrict Resolution", resolutionRange, "Only record streams with resolution within the given range"), + Setting.of("Concurrent Recordings (0 = unlimited)", concurrentRecordings), + Setting.of("Check online state every (seconds)", onlineCheckIntervalInSecs, "Check every x seconds, if a model came online"), + Setting.of("Leave space on device (GiB)", leaveSpaceOnDevice, "Stop recording, if the free space on the device gets below this threshold").converter(new GigabytesConverter()), + Setting.of("FFmpeg parameters", ffmpegParameters, "FFmpeg parameters to use when merging stream segments"), + Setting.of("File Extension", fileExtension, "File extension to use for recordings") + ), + Group.of("Location", + Setting.of("Record Location", recordLocal), + Setting.of("Server", server), + Setting.of("Port", port), + Setting.of("Path", path, "Leave empty, if you didn't change the servletContext in the server config"), + Setting.of("Require authentication", requireAuthentication), + Setting.of("Use Secure Communication (TLS)", transportLayerSecurity) + ) + ), + Category.of("Post-Processing", + Group.of("Post-Processing", + Setting.of("Post-Processing", postProcessing), + Setting.of("Threads", postProcessingThreads), + Setting.of("Delete recordings shorter than (secs)", minimumLengthInSecs, "Delete recordings, which are shorter than x seconds. 0 to disable"), + Setting.of("Remove recording after post-processing", removeRecordingAfterPp) + ) + ), + Category.of("Events & Actions", new ActionSettingsPanel(recorder)), + Category.of("Ignore List", ignoreList), + Category.of("Sites", siteCategories.toArray(new Category[0])), + Category.of("Proxy", + Group.of("Proxy", + Setting.of("Type", proxyType), + Setting.of("Host", proxyHost), + Setting.of("Port", proxyPort), + Setting.of("Username", proxyUser), + Setting.of("Password", proxyPassword) + ) + ) + ); + setContent(prefs.getView()); + prefs.expandTree(); } - private Node createRecordLocationPanel() { - GridPane layout = createGridLayout(); - Label l = new Label("Record Location"); - int row = 0; - layout.add(l, 0, row); - ToggleGroup recordLocationToggleGroup = new ToggleGroup(); - recordLocal = new RadioButton("Local"); - RadioButton recordRemote = new RadioButton("Remote"); - recordLocal.setToggleGroup(recordLocationToggleGroup); - recordRemote.setToggleGroup(recordLocationToggleGroup); - recordLocal.setSelected(Config.getInstance().getSettings().localRecording); - recordRemote.setSelected(!recordLocal.isSelected()); - layout.add(recordLocal, 1, row); - layout.add(recordRemote, 2, row++); - recordLocationToggleGroup.selectedToggleProperty().addListener(e -> { - Config.getInstance().getSettings().localRecording = recordLocal.isSelected(); - setRecordingMode(recordLocal.isSelected()); - showRestartRequired(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(0, 0, CHECKBOX_MARGIN, 0)); - GridPane.setMargin(recordLocal, new Insets(0, 0, CHECKBOX_MARGIN, 0)); - GridPane.setMargin(recordRemote, new Insets(0, 0, CHECKBOX_MARGIN, 0)); - - layout.add(new Label("Server"), 0, row); - server = new TextField(Config.getInstance().getSettings().httpServer); - server.textProperty().addListener((ob, o, n) -> { - if(!server.getText().isEmpty()) { - Config.getInstance().getSettings().httpServer = server.getText(); - saveConfig(); - } - }); - GridPane.setFillWidth(server, true); - GridPane.setHgrow(server, Priority.ALWAYS); - GridPane.setColumnSpan(server, 2); - layout.add(server, 1, row++); - - layout.add(new Label("Port"), 0, row); - port = new TextField(Integer.toString(Config.getInstance().getSettings().httpPort)); - port.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - port.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if(!port.getText().isEmpty()) { - Config.getInstance().getSettings().httpPort = Integer.parseInt(port.getText()); - saveConfig(); - } - }); - GridPane.setFillWidth(port, true); - GridPane.setHgrow(port, Priority.ALWAYS); - GridPane.setColumnSpan(port, 2); - layout.add(port, 1, row++); - - layout.add(new Label("Path"), 0, row); - servletContext = new TextField(Config.getInstance().getSettings().servletContext); - servletContext.setPromptText("e.g. /ctbrec"); - servletContext.setTooltip(new Tooltip("Leave empty, if you didn't change the servletContext in the server config")); - servletContext.textProperty().addListener((observable, oldValue, newValue) -> { - Config.getInstance().getSettings().servletContext = servletContext.getText(); - saveConfig(); - }); - GridPane.setFillWidth(servletContext, true); - GridPane.setHgrow(servletContext, Priority.ALWAYS); - GridPane.setColumnSpan(servletContext, 2); - layout.add(servletContext, 1, row++); - - l = new Label("Require authentication"); - layout.add(l, 0, row); - useAuthentication.setSelected(Config.getInstance().getSettings().requireAuthentication); - useAuthentication.setOnAction(e -> { - Config.getInstance().getSettings().requireAuthentication = useAuthentication.isSelected(); - if(useAuthentication.isSelected()) { - byte[] key = Config.getInstance().getSettings().key; - if(key == null) { - key = Hmac.generateKey(); - Config.getInstance().getSettings().key = key; - saveConfig(); - } - TextInputDialog keyDialog = new TextInputDialog(); - keyDialog.setResizable(true); - keyDialog.setTitle("Server Authentication"); - keyDialog.setHeaderText("A key has been generated"); - keyDialog.setContentText("Add this setting to your server's config.json:\n"); - keyDialog.getEditor().setText("\"key\": " + Arrays.toString(key)); - keyDialog.getEditor().setEditable(false); - keyDialog.setWidth(800); - keyDialog.setHeight(200); - keyDialog.show(); - } - }); - GridPane.setMargin(l, new Insets(4, CHECKBOX_MARGIN, 0, 0)); - GridPane.setMargin(useAuthentication, new Insets(4, 0, 0, 0)); - layout.add(useAuthentication, 1, row++); - - l = new Label("Use Secure Communication (TLS)"); - layout.add(l, 0, row); - useTLS.setSelected(Config.getInstance().getSettings().transportLayerSecurity); - useTLS.setOnAction(e -> { - Config.getInstance().getSettings().transportLayerSecurity = useTLS.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(4, CHECKBOX_MARGIN, 0, 0)); - GridPane.setMargin(useTLS, new Insets(4, 0, 0, 0)); - layout.add(useTLS, 1, row); - - TitledPane recordLocation = new TitledPane("Record Location", layout); - recordLocation.setCollapsible(false); - return recordLocation; + private List getTabNames() { + return getTabPane().getTabs().stream() + .map(Tab::getText) + .collect(Collectors.toList()); } - private Node createRecorderPanel() { - int row = 0; - GridPane layout = createGridLayout(); - layout.getColumnConstraints().add(new ColumnConstraints(270)); - layout.getColumnConstraints().add(new ColumnConstraints(100, 400, Double.MAX_VALUE, Priority.ALWAYS, HPos.LEFT, true)); - layout.getColumnConstraints().add(new ColumnConstraints(80)); - layout.getColumnConstraints().add(new ColumnConstraints(40)); - - layout.add(new Label("Recordings Directory"), 0, row); - recordingsDirectory = new DirectorySelectionBox(Config.getInstance().getSettings().recordingsDir); - recordingsDirectory.prefWidth(400); - recordingsDirectory.fileProperty().addListener((obs, o, n) -> { - String path = n; - if(!Objects.equals(path, Config.getInstance().getSettings().recordingsDir)) { - Config.getInstance().getSettings().recordingsDir = path; - saveConfig(); - } - }); - GridPane.setFillWidth(recordingsDirectory, true); - GridPane.setHgrow(recordingsDirectory, Priority.ALWAYS); - GridPane.setMargin(recordingsDirectory, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - GridPane.setColumnSpan(recordingsDirectory, 3); - layout.add(recordingsDirectory, 1, row++); - - layout.add(new Label("Directory Structure"), 0, row); - List options = new ArrayList<>(); - options.add(FLAT); - options.add(ONE_PER_MODEL); - options.add(ONE_PER_RECORDING); - directoryStructure = new ComboBox<>(FXCollections.observableList(options)); - directoryStructure.setValue(Config.getInstance().getSettings().recordingsDirStructure); - directoryStructure.setOnAction(evt -> { - Config.getInstance().getSettings().recordingsDirStructure = directoryStructure.getValue(); - saveConfig(); - }); - GridPane.setMargin(directoryStructure, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(directoryStructure, 1, row++); - recordingsDirectory.prefWidthProperty().bind(directoryStructure.widthProperty()); - - Label l = new Label("Split recordings after (minutes)"); - layout.add(l, 0, row); + private List getSplitOptions() { List splitOptions = new ArrayList<>(); splitOptions.add(new SplitAfterOption("disabled", 0)); - if(Config.isDevMode()) { - splitOptions.add(new SplitAfterOption( "1 min", 1 * 60)); - splitOptions.add(new SplitAfterOption( "3 min", 3 * 60)); + if (Config.isDevMode()) { + splitOptions.add(new SplitAfterOption("1 min", 1 * 60)); + splitOptions.add(new SplitAfterOption("3 min", 3 * 60)); } - splitOptions.add(new SplitAfterOption( "5 min", 5 * 60)); + splitOptions.add(new SplitAfterOption("5 min", 5 * 60)); splitOptions.add(new SplitAfterOption("10 min", 10 * 60)); splitOptions.add(new SplitAfterOption("15 min", 15 * 60)); splitOptions.add(new SplitAfterOption("20 min", 20 * 60)); splitOptions.add(new SplitAfterOption("30 min", 30 * 60)); splitOptions.add(new SplitAfterOption("60 min", 60 * 60)); - splitAfter = new ComboBox<>(FXCollections.observableList(splitOptions)); - layout.add(splitAfter, 1, row++); - setSplitAfterValue(); - splitAfter.setOnAction(e -> { - Config.getInstance().getSettings().splitRecordings = splitAfter.getSelectionModel().getSelectedItem().getValue(); - saveConfig(); - }); - splitAfter.prefWidthProperty().bind(directoryStructure.widthProperty()); - GridPane.setMargin(l, new Insets(0, 0, 0, 0)); - GridPane.setMargin(splitAfter, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - - l = new Label("Restrict Resolution"); - Tooltip tt = new Tooltip("Only record streams with resolution within the given range"); - l.setTooltip(tt); - layout.add(l, 0, row); - List labels = Arrays.asList(0, 240, 360, 480, 600, 720, 960, 1080, 1440, 2160, 4320, 8640); - List values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); - DiscreteRange rangeValues = new DiscreteRange<>(values, labels); - resolutionRange = new RangeSlider<>(rangeValues); - resolutionRange.prefWidthProperty().bind(directoryStructure.widthProperty()); - resolutionRange.setShowTickMarks(true); - resolutionRange.setShowTickLabels(true); - int lowValue = getRangeSliderValue(values, labels, Config.getInstance().getSettings().minimumResolution); - resolutionRange.setLow(lowValue >= 0 ? lowValue : values.get(0)); - int highValue = getRangeSliderValue(values, labels, Config.getInstance().getSettings().maximumResolution); - resolutionRange.setHigh(highValue >= 0 ? highValue : values.get(values.size() - 1)); - layout.add(resolutionRange, 1, row++); - GridPane.setMargin(l, new Insets(0, 0, 0, 0)); - GridPane.setColumnSpan(resolutionRange, 3); - GridPane.setMargin(resolutionRange, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - resolutionRange.getLow().addListener((obs, oldV, newV) -> { - Config.getInstance().getSettings().minimumResolution = labels.get(newV.intValue()); - saveConfig(); - }); - resolutionRange.getHigh().addListener((obs, oldV, newV) -> { - Config.getInstance().getSettings().maximumResolution = labels.get(newV.intValue()); - saveConfig(); - }); - - - l = new Label("Concurrent Recordings (0 = unlimited)"); - layout.add(l, 0, row); - concurrentRecordings = new TextField(Integer.toString(Config.getInstance().getSettings().concurrentRecordings)); - concurrentRecordings.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - concurrentRecordings.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if (!concurrentRecordings.getText().isEmpty()) { - int newConcurrentRecordings = Integer.parseInt(concurrentRecordings.getText()); - if (newConcurrentRecordings != Config.getInstance().getSettings().concurrentRecordings) { - Config.getInstance().getSettings().concurrentRecordings = newConcurrentRecordings; - saveConfig(); - } - } - }); - concurrentRecordings.prefWidthProperty().bind(directoryStructure.widthProperty()); - layout.add(concurrentRecordings, 1, row++); - GridPane.setMargin(l, new Insets(0, 0, 0, 0)); - GridPane.setColumnSpan(concurrentRecordings, 3); - GridPane.setMargin(concurrentRecordings, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - - tt = new Tooltip("Check every x seconds, if a model came online"); - l = new Label("Check online state every (seconds)"); - l.setTooltip(tt); - layout.add(l, 0, row); - onlineCheckIntervalInSecs = new TextField(Integer.toString(Config.getInstance().getSettings().onlineCheckIntervalInSecs)); - onlineCheckIntervalInSecs.setTooltip(tt); - onlineCheckIntervalInSecs.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - onlineCheckIntervalInSecs.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if(!onlineCheckIntervalInSecs.getText().isEmpty()) { - Config.getInstance().getSettings().onlineCheckIntervalInSecs = Integer.parseInt(onlineCheckIntervalInSecs.getText()); - saveConfig(); - } - }); - GridPane.setColumnSpan(onlineCheckIntervalInSecs, 3); - GridPane.setMargin(onlineCheckIntervalInSecs, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(onlineCheckIntervalInSecs, 1, row++); - - tt = new Tooltip("Stop recording, if the free space on the device gets below this threshold"); - l = new Label("Leave space on device (GiB)"); - l.setTooltip(tt); - layout.add(l, 0, row); - long minimumSpaceLeftInBytes = Config.getInstance().getSettings().minimumSpaceLeftInBytes; - int minimumSpaceLeftInGiB = (int) (minimumSpaceLeftInBytes / ONE_GIB_IN_BYTES); - leaveSpaceOnDevice = new TextField(Integer.toString(minimumSpaceLeftInGiB)); - leaveSpaceOnDevice.setTooltip(tt); - leaveSpaceOnDevice.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - leaveSpaceOnDevice.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if(!leaveSpaceOnDevice.getText().isEmpty()) { - long spaceLeftInGiB = Long.parseLong(leaveSpaceOnDevice.getText()); - Config.getInstance().getSettings().minimumSpaceLeftInBytes = spaceLeftInGiB * ONE_GIB_IN_BYTES; - saveConfig(); - } - }); - GridPane.setColumnSpan(leaveSpaceOnDevice, 3); - GridPane.setMargin(leaveSpaceOnDevice, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(leaveSpaceOnDevice, 1, row++); - - tt = new Tooltip("Delete recordings, which are shorter than x seconds. 0 to disable."); - l = new Label("Delete recordings shorter than (secs)"); - l.setTooltip(tt); - layout.add(l, 0, row); - int minimumLengthInSeconds = Config.getInstance().getSettings().minimumLengthInSeconds; - minimumLengthInSecs = new TextField(Integer.toString(minimumLengthInSeconds)); - minimumLengthInSecs.setTooltip(tt); - minimumLengthInSecs.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - minimumLengthInSecs.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if(!minimumLengthInSecs.getText().isEmpty()) { - int minimumLength = Integer.parseInt(minimumLengthInSecs.getText()); - Config.getInstance().getSettings().minimumLengthInSeconds = minimumLength; - saveConfig(); - } - }); - GridPane.setColumnSpan(minimumLengthInSecs, 3); - GridPane.setMargin(minimumLengthInSecs, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(minimumLengthInSecs, 1, row++); - - tt = new Tooltip("FFmpeg parameters to use when merging stream segments"); - l = new Label("FFmpeg parameters"); - l.setTooltip(tt); - layout.add(l, 0, row); - String ffmpegParams = Config.getInstance().getSettings().ffmpegMergedDownloadArgs; - ffmpegParameters = new TextField(ffmpegParams); - ffmpegParameters.setTooltip(tt); - ffmpegParameters.textProperty().addListener((observable, oldValue, newValue) -> { - if(!ffmpegParameters.getText().isEmpty()) { - Config.getInstance().getSettings().ffmpegMergedDownloadArgs = ffmpegParameters.getText(); - saveConfig(); - } - }); - GridPane.setColumnSpan(ffmpegParameters, 3); - GridPane.setMargin(ffmpegParameters, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(ffmpegParameters, 1, row++); - - tt = new Tooltip("File extension to use for recordings"); - l = new Label("File Extension"); - l.setTooltip(tt); - layout.add(l, 0, row); - String fileSuffix = Config.getInstance().getSettings().ffmpegFileSuffix; - fileExtension = new TextField(fileSuffix); - fileExtension.setTooltip(tt); - fileExtension.textProperty().addListener((observable, oldValue, newValue) -> { - if(!fileExtension.getText().isEmpty()) { - Config.getInstance().getSettings().ffmpegFileSuffix = fileExtension.getText(); - saveConfig(); - } - }); - GridPane.setColumnSpan(fileExtension, 3); - GridPane.setMargin(fileExtension, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(fileExtension, 1, row++); - - layout.add(new Label("Post-Processing"), 0, row); - postProcessing = new ProgramSelectionBox(Config.getInstance().getSettings().postProcessing); - postProcessing.allowEmptyValue(); - postProcessing.fileProperty().addListener((obs, o, n) -> { - String path = n; - if(!Objects.equals(path, Config.getInstance().getSettings().postProcessing)) { - Config.getInstance().getSettings().postProcessing = path; - saveConfig(); - } - }); - GridPane.setFillWidth(postProcessing, true); - GridPane.setHgrow(postProcessing, Priority.ALWAYS); - GridPane.setMargin(postProcessing, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(postProcessing, 1, row); - - l = new Label("PP-Threads"); - layout.add(l, 2, row); - GridPane.setMargin(l, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - GridPane.setHalignment(l, HPos.RIGHT); - ppThreads = new TextField(Integer.toString(Config.getInstance().getSettings().postProcessingThreads)); - ppThreads.prefWidth(40); - ppThreads.minWidth(40); - ppThreads.maxWidth(40); - ppThreads.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - ppThreads.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if (!ppThreads.getText().isEmpty()) { - int newPpThreads = Integer.parseInt(ppThreads.getText()); - if (newPpThreads != Config.getInstance().getSettings().postProcessingThreads) { - Config.getInstance().getSettings().postProcessingThreads = newPpThreads; - saveConfig(); - showRestartRequired(); - } - } - }); - layout.add(ppThreads, 3, row++); - - l = new Label("Remove recording after post-processing"); - layout.add(l, 0, row); - removeRecordingAfterPp.setSelected(Config.getInstance().getSettings().removeRecordingAfterPostProcessing); - removeRecordingAfterPp.setOnAction(e -> { - Config.getInstance().getSettings().removeRecordingAfterPostProcessing = removeRecordingAfterPp.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(4, CHECKBOX_MARGIN, 0, 0)); - GridPane.setMargin(removeRecordingAfterPp, new Insets(4, 0, 0, CHECKBOX_MARGIN)); - layout.add(removeRecordingAfterPp, 1, row); - - TitledPane locations = new TitledPane("Recorder", layout); - locations.setCollapsible(false); - return locations; + return splitOptions; } - private int getRangeSliderValue(List values, List labels, int value) { - for (int i = 0; i < labels.size(); i++) { - int label = labels.get(i).intValue(); - if(label == value) { - return values.get(i); - } - } - return -1; - } - - private Node createIgnoreListPanel() { - GridPane layout = createGridLayout(); - Button editIgnoreList = new Button("Edit"); - //editIgnoreList.setOnAction(e -> new IgnoreList(editIgnoreList.getScene()).showAndWait()); - layout.add(editIgnoreList, 0, 0); - Button exportIgnoreList = new Button("Export"); - exportIgnoreList.setOnAction(e -> exportIgnoreList()); - layout.add(exportIgnoreList, 1, 0); - Button importIgnoreList = new Button("Import"); - importIgnoreList.setOnAction(e -> importIgnoreList()); - layout.add(importIgnoreList, 2, 0); - TitledPane ignoreList = new TitledPane("Ignore List", layout); - ignoreList.setCollapsible(false); - return ignoreList; - } - - private void exportIgnoreList() { - FileChooser chooser = new FileChooser(); - chooser.setTitle("Export ignore list"); - chooser.setInitialFileName("ctbrec-ignorelist.json"); - File file = chooser.showSaveDialog(null); - if (file != null) { - Moshi moshi = new Moshi.Builder().add(Model.class, new ModelJsonAdapter(sites)).build(); - Type modelListType = Types.newParameterizedType(List.class, Model.class); - JsonAdapter> adapter = moshi.adapter(modelListType); - adapter = adapter.indent(" "); - try (FileOutputStream out = new FileOutputStream(file)) { - String json = adapter.toJson(Config.getInstance().getSettings().modelsIgnored); - out.write(json.getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - Dialogs.showError(getTabPane().getScene(), "Couldn't export ignore list", e.getLocalizedMessage(), e); + private void requireAuthenticationChanged(ObservableValue obs, Boolean oldV, Boolean newV) { // NOSONAR + boolean requiresAuthentication = newV; + Config.getInstance().getSettings().requireAuthentication = requiresAuthentication; + if (requiresAuthentication) { + byte[] key = Config.getInstance().getSettings().key; + if (key == null) { + key = Hmac.generateKey(); + Config.getInstance().getSettings().key = key; + saveConfig(); } + TextInputDialog keyDialog = new TextInputDialog(); + keyDialog.setResizable(true); + keyDialog.setTitle("Server Authentication"); + keyDialog.setHeaderText("A key has been generated"); + keyDialog.setContentText("Add this setting to your server's config.json:\n"); + keyDialog.getEditor().setText("\"key\": " + Arrays.toString(key)); + keyDialog.getEditor().setEditable(false); + keyDialog.setWidth(800); + keyDialog.setHeight(200); + keyDialog.show(); } } - private void importIgnoreList() { - FileChooser chooser = new FileChooser(); - chooser.setTitle("Import ignore list"); - File file = chooser.showOpenDialog(null); - if (file != null) { - Moshi moshi = new Moshi.Builder().add(Model.class, new ModelJsonAdapter(sites)).build(); - Type modelListType = Types.newParameterizedType(List.class, Model.class); - JsonAdapter> adapter = moshi.adapter(modelListType); - try { - byte[] fileContent = Files.readAllBytes(file.toPath()); - List ignoredModels = adapter.fromJson(new String(fileContent, StandardCharsets.UTF_8)); - boolean confirmed = true; - if (!Config.getInstance().getSettings().modelsIgnored.isEmpty()) { - String msg = "This will replace the existing ignore list! Continue?"; - AutosizeAlert confirm = new AutosizeAlert(AlertType.CONFIRMATION, msg, getTabPane().getScene(), YES, NO); - confirm.setTitle("Import ignore list"); - confirm.setHeaderText("Overwrite ignore list"); - confirm.showAndWait(); - confirmed = confirm.getResult() == ButtonType.YES; - } - if (confirmed) { - Config.getInstance().getSettings().modelsIgnored = ignoredModels; - } - } catch (IOException e) { - Dialogs.showError(getTabPane().getScene(), "Couldn't import ignore list", e.getLocalizedMessage(), e); - } + public void saveConfig() { + try { + Config.getInstance().save(); + } catch (IOException e) { + LOG.error("Couldn't save config", e); } } - private Node createGeneralPanel() { - Settings settings = Config.getInstance().getSettings(); - GridPane layout = createGridLayout(); - int row = 0; - - layout.add(new Label("Player"), 0, row); - ProgramSelectionBox mediaPlayer = new ProgramSelectionBox(settings.mediaPlayer); - mediaPlayer.fileProperty().addListener((obs, o, n) -> { - String path = n; - if (!Objects.equals(path, settings.mediaPlayer)) { - settings.mediaPlayer = path; - saveConfig(); - } - }); - GridPane.setFillWidth(mediaPlayer, true); - GridPane.setHgrow(mediaPlayer, Priority.ALWAYS); - GridPane.setMargin(mediaPlayer, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - GridPane.setColumnSpan(mediaPlayer, 2); - layout.add(mediaPlayer, 1, row); - Button mediaPlayerParamsButton = new Button("⚙"); - mediaPlayerParamsButton.setOnAction(e -> { - PlayerSettingsDialog dialog = new PlayerSettingsDialog(getTabPane().getScene(), Config.getInstance()); - Optional exception = dialog.showAndWait(); - if (exception.isPresent()) { - Dialogs.showError("Saving player parameters", "Player parameters couldn't be saved", exception.get()); - } - }); - layout.add(mediaPlayerParamsButton, 3, row++); - - Label l = new Label("Allow multiple players"); - layout.add(l, 0, row); - multiplePlayers.setSelected(!Config.getInstance().getSettings().singlePlayer); - multiplePlayers.setOnAction(e -> { - Config.getInstance().getSettings().singlePlayer = !multiplePlayers.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(multiplePlayers, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - GridPane.setHgrow(multiplePlayers, Priority.ALWAYS); - layout.add(multiplePlayers, 1, row); - - l = new Label("Show \"Player Starting\" Message"); - layout.add(l, 2, row); - showPlayerStarting.setSelected(Config.getInstance().getSettings().showPlayerStarting); - showPlayerStarting.setOnAction(e -> { - Config.getInstance().getSettings().showPlayerStarting = showPlayerStarting.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(showPlayerStarting, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - layout.add(showPlayerStarting, 3, row++); - - l = new Label("Display stream resolution in overview"); - layout.add(l, 0, row); - CheckBox loadResolution = new CheckBox(); - loadResolution.setSelected(Config.getInstance().getSettings().determineResolution); - loadResolution.setOnAction(e -> { - Config.getInstance().getSettings().determineResolution = loadResolution.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(loadResolution, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - layout.add(loadResolution, 1, row); - - l = new Label("Manually select stream quality"); - layout.add(l, 2, row); - chooseStreamQuality.setSelected(Config.getInstance().getSettings().chooseStreamQuality); - chooseStreamQuality.setOnAction(e -> { - Config.getInstance().getSettings().chooseStreamQuality = chooseStreamQuality.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(chooseStreamQuality, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - layout.add(chooseStreamQuality, 3, row++); - - l = new Label("Enable live previews (experimental)"); - layout.add(l, 0, row); - livePreviews.setSelected(Config.getInstance().getSettings().livePreviews); - livePreviews.setOnAction(e -> { - Config.getInstance().getSettings().livePreviews = livePreviews.isSelected(); - saveConfig(); - showRestartRequired(); - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(livePreviews, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - layout.add(livePreviews, 1, row); - - Tooltip tt = new Tooltip("The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."); - l = new Label("Update thumbnails"); - l.setTooltip(tt); - layout.add(l, 2, row); - updateThumbnails.setTooltip(tt); - updateThumbnails.setSelected(Config.getInstance().getSettings().updateThumbnails); - updateThumbnails.setOnAction(e -> { - Config.getInstance().getSettings().updateThumbnails = updateThumbnails.isSelected(); - saveConfig(); - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(updateThumbnails, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - layout.add(updateThumbnails, 3, row++); - - tt = new Tooltip("Update the thumbnail overviews every x seconds"); - l = new Label("Update overview interval (seconds)"); - l.setTooltip(tt); - layout.add(l, 0, row); - TextField overviewUpdateIntervalInSecs = new TextField(Integer.toString(Config.getInstance().getSettings().overviewUpdateIntervalInSecs)); - overviewUpdateIntervalInSecs.setTooltip(tt); - overviewUpdateIntervalInSecs.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("\\d*")) { - overviewUpdateIntervalInSecs.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, "")); - } - if(!overviewUpdateIntervalInSecs.getText().isEmpty()) { - Config.getInstance().getSettings().overviewUpdateIntervalInSecs = Integer.parseInt(overviewUpdateIntervalInSecs.getText()); - saveConfig(); - showRestartRequired(); - } - }); - GridPane.setMargin(l, new Insets(3, 0, 0, 0)); - GridPane.setMargin(overviewUpdateIntervalInSecs, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN)); - layout.add(overviewUpdateIntervalInSecs, 1, row++); - - l = new Label("Start Tab"); - layout.add(l, 0, row); - startTab = new ComboBox<>(); - startTab.setOnAction(e -> { - Config.getInstance().getSettings().startTab = startTab.getSelectionModel().getSelectedItem(); - saveConfig(); - }); - layout.add(startTab, 1, row++); - GridPane.setMargin(l, new Insets(0, 0, 0, 0)); - GridPane.setMargin(startTab, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - overviewUpdateIntervalInSecs.maxWidthProperty().bind(startTab.widthProperty()); - - l = new Label("Colors (Base / Accent)"); - layout.add(l, 0, row); - ColorSettingsPane colorSettingsPane = new ColorSettingsPane(Config.getInstance()); - layout.add(colorSettingsPane, 1, row++); - GridPane.setMargin(l, new Insets(0, 0, 0, 0)); - GridPane.setMargin(colorSettingsPane, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - - layout.add(new Label("User-Agent"), 0, row); - userAgent = new TextField(Config.getInstance().getSettings().httpUserAgent); - userAgent.textProperty().addListener((observable, oldValue, newValue) -> { - if(!userAgent.getText().isEmpty()) { - Config.getInstance().getSettings().httpUserAgent = userAgent.getText(); - saveConfig(); - } - }); - GridPane.setColumnSpan(userAgent, 3); - GridPane.setMargin(userAgent, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(userAgent, 1, row++); - - layout.add(new Label("User-Agent mobile"), 0, row); - userAgentMobile = new TextField(Config.getInstance().getSettings().httpUserAgentMobile); - userAgentMobile.textProperty().addListener((observable, oldValue, newValue) -> { - if(!userAgentMobile.getText().isEmpty()) { - Config.getInstance().getSettings().httpUserAgentMobile = userAgentMobile.getText(); - saveConfig(); - } - }); - GridPane.setColumnSpan(userAgentMobile, 3); - GridPane.setMargin(userAgentMobile, new Insets(0, 0, 0, CHECKBOX_MARGIN)); - layout.add(userAgentMobile, 1, row); - - TitledPane general = new TitledPane("General", layout); - general.setCollapsible(false); - return general; - } - - private void setSplitAfterValue() { - int value = Config.getInstance().getSettings().splitRecordings; - for (SplitAfterOption option : splitAfter.getItems()) { - if(option.getValue() == value) { - splitAfter.getSelectionModel().select(option); - } + @Override + public void selected() { + if (!initialized) { + initializeProperties(); + createGui(); + initialized = true; } + ignoreList.refresh(); } - void showRestartRequired() { - restartLabel.setVisible(true); + @Override + public void deselected() { + saveConfig(); } public static GridPane createGridLayout() { @@ -813,51 +297,8 @@ public class SettingsTab extends Tab implements TabSelectionListener { return layout; } - private void setRecordingMode(boolean local) { - server.setDisable(local); - port.setDisable(local); - servletContext.setDisable(local); - useAuthentication.setDisable(local); - useTLS.setDisable(local); - recordingsDirectory.setDisable(!local); - splitAfter.setDisable(!local); - resolutionRange.setDisable(!local); - directoryStructure.setDisable(!local); - onlineCheckIntervalInSecs.setDisable(!local); - leaveSpaceOnDevice.setDisable(!local); - postProcessing.setDisable(!local); - minimumLengthInSecs.setDisable(!local); - concurrentRecordings.setDisable(!local); - ppThreads.setDisable(!local); - } - - @Override - public void selected() { - if(startTab.getItems().isEmpty()) { - for(Tab tab : getTabPane().getTabs()) { - startTab.getItems().add(tab.getText()); - } - } - String startTabName = Config.getInstance().getSettings().startTab; - if(StringUtil.isNotBlank(startTabName)) { - startTab.getSelectionModel().select(startTabName); - } - } - - @Override - public void deselected() { - saveConfig(); - } - - public void saveConfig() { - if(proxySettingsPane != null) { - //proxySettingsPane.saveConfig(); - } - try { - Config.getInstance().save(); - } catch (IOException e) { - LOG.error("Couldn't save config", e); - } + void showRestartRequired() { + // TODO restartLabel.setVisible(true); } public static class SplitAfterOption { diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab2.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab2.java deleted file mode 100644 index a1213c1d..00000000 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab2.java +++ /dev/null @@ -1,290 +0,0 @@ -package ctbrec.ui.settings; - -import static ctbrec.Settings.DirectoryStructure.*; -import static ctbrec.Settings.ProxyType.*; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ctbrec.Config; -import ctbrec.Hmac; -import ctbrec.Settings; -import ctbrec.Settings.DirectoryStructure; -import ctbrec.Settings.ProxyType; -import ctbrec.recorder.Recorder; -import ctbrec.sites.Site; -import ctbrec.ui.SiteUiFactory; -import ctbrec.ui.controls.range.DiscreteRange; -import ctbrec.ui.settings.SettingsTab.SplitAfterOption; -import ctbrec.ui.settings.api.Category; -import ctbrec.ui.settings.api.ExclusiveSelectionProperty; -import ctbrec.ui.settings.api.GigabytesConverter; -import ctbrec.ui.settings.api.Group; -import ctbrec.ui.settings.api.Preferences; -import ctbrec.ui.settings.api.Setting; -import ctbrec.ui.settings.api.SimpleDirectoryProperty; -import ctbrec.ui.settings.api.SimpleFileProperty; -import ctbrec.ui.settings.api.SimpleRangeProperty; -import ctbrec.ui.tabs.TabSelectionListener; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.scene.control.Tab; -import javafx.scene.control.TextInputDialog; - -public class SettingsTab2 extends Tab implements TabSelectionListener { - - private static final Logger LOG = LoggerFactory.getLogger(SettingsTab2.class); - - private List sites; - private Recorder recorder; - private boolean initialized = false; - private Config config; - private Settings settings; - - private SimpleStringProperty httpUserAgent; - private SimpleStringProperty httpUserAgentMobile; - private SimpleIntegerProperty overviewUpdateIntervalInSecs; - private SimpleBooleanProperty updateThumbnails; - private SimpleBooleanProperty determineResolution; - private SimpleBooleanProperty chooseStreamQuality; - private SimpleBooleanProperty livePreviews; - private SimpleListProperty startTab; - private SimpleFileProperty mediaPlayer; - private SimpleStringProperty mediaPlayerParams; - private SimpleIntegerProperty maximumResolutionPlayer; - private SimpleBooleanProperty showPlayerStarting; - private SimpleBooleanProperty singlePlayer; - private SimpleListProperty proxyType; - private SimpleStringProperty proxyHost; - private SimpleStringProperty proxyPort; - private SimpleStringProperty proxyUser; - private SimpleStringProperty proxyPassword; - private SimpleDirectoryProperty recordingsDir; - private SimpleListProperty directoryStructure; - private SimpleListProperty splitAfter; - private SimpleRangeProperty resolutionRange; - private List labels = Arrays.asList(0, 240, 360, 480, 600, 720, 960, 1080, 1440, 2160, 4320, 8640); - private List values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); - private DiscreteRange rangeValues = new DiscreteRange<>(values, labels); - private SimpleIntegerProperty concurrentRecordings; - private SimpleIntegerProperty onlineCheckIntervalInSecs; - private SimpleLongProperty leaveSpaceOnDevice; - private SimpleIntegerProperty minimumLengthInSecs; - private SimpleStringProperty ffmpegParameters; - private SimpleStringProperty fileExtension; - private SimpleStringProperty server; - private SimpleIntegerProperty port; - private SimpleStringProperty path; - private SimpleBooleanProperty requireAuthentication; - private SimpleBooleanProperty transportLayerSecurity; - private ExclusiveSelectionProperty recordLocal; - private SimpleFileProperty postProcessing; - private SimpleIntegerProperty postProcessingThreads; - private SimpleBooleanProperty removeRecordingAfterPp; - private IgnoreList ignoreList; - - public SettingsTab2(List sites, Recorder recorder) { - this.sites = sites; - this.recorder = recorder; - setText("Settings"); - setClosable(false); - config = Config.getInstance(); - settings = config.getSettings(); - } - - private void initializeProperties() { - httpUserAgent = new SimpleStringProperty(null, "httpUserAgent", settings.httpUserAgent); - httpUserAgentMobile = new SimpleStringProperty(null, "httpUserAgentMobile", settings.httpUserAgentMobile); - overviewUpdateIntervalInSecs = new SimpleIntegerProperty(null, "overviewUpdateIntervalInSecs", settings.overviewUpdateIntervalInSecs); - updateThumbnails = new SimpleBooleanProperty(null, "updateThumbnails", settings.updateThumbnails); - determineResolution = new SimpleBooleanProperty(null, "determineResolution", settings.determineResolution); - chooseStreamQuality = new SimpleBooleanProperty(null, "chooseStreamQuality", settings.chooseStreamQuality); - livePreviews = new SimpleBooleanProperty(null, "livePreviews", settings.livePreviews); - startTab = new SimpleListProperty<>(null, "startTab", FXCollections.observableList(getTabNames())); - mediaPlayer = new SimpleFileProperty(null, "mediaPlayer", settings.mediaPlayer); - mediaPlayerParams = new SimpleStringProperty(null, "mediaPlayerParams", settings.mediaPlayerParams); - maximumResolutionPlayer = new SimpleIntegerProperty(null, "maximumResolutionPlayer", settings.maximumResolutionPlayer); - showPlayerStarting = new SimpleBooleanProperty(null, "showPlayerStarting", settings.showPlayerStarting); - singlePlayer = new SimpleBooleanProperty(null, "singlePlayer", settings.singlePlayer); - proxyType = new SimpleListProperty<>(null, "proxyType", FXCollections.observableList(List.of(DIRECT, HTTP, SOCKS4, SOCKS5))); - proxyHost = new SimpleStringProperty(null, "proxyHost", settings.proxyHost); - proxyPort = new SimpleStringProperty(null, "proxyPort", settings.proxyPort); - proxyUser = new SimpleStringProperty(null, "proxyUser", settings.proxyUser); - proxyPassword = new SimpleStringProperty(null, "proxyPassword", settings.proxyPassword); - recordingsDir = new SimpleDirectoryProperty(null, "recordingsDir", settings.recordingsDir); - directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure", FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_RECORDING))); - splitAfter = new SimpleListProperty<>(null, "splitRecordings", FXCollections.observableList(getSplitOptions())); - resolutionRange = new SimpleRangeProperty<>(rangeValues, "minimumResolution", "maximumResolution", settings.minimumResolution, settings.maximumResolution); - concurrentRecordings = new SimpleIntegerProperty(null, "concurrentRecordings", settings.concurrentRecordings); - onlineCheckIntervalInSecs = new SimpleIntegerProperty(null, "onlineCheckIntervalInSecs", settings.onlineCheckIntervalInSecs); - leaveSpaceOnDevice = new SimpleLongProperty(null, "minimumSpaceLeftInBytes", (long) new GigabytesConverter().convertTo(settings.minimumSpaceLeftInBytes)); - minimumLengthInSecs = new SimpleIntegerProperty(null, "minimumLengthInSeconds", settings.minimumLengthInSeconds); - ffmpegParameters = new SimpleStringProperty(null, "ffmpegMergedDownloadArgs", settings.ffmpegMergedDownloadArgs); - fileExtension = new SimpleStringProperty(null, "ffmpegFileSuffix", settings.ffmpegFileSuffix); - server = new SimpleStringProperty(null, "httpServer", settings.httpServer); - port = new SimpleIntegerProperty(null, "httpPort", settings.httpPort); - path = new SimpleStringProperty(null, "servletContext", settings.servletContext); - requireAuthentication = new SimpleBooleanProperty(null, "requireAuthentication", settings.requireAuthentication); - requireAuthentication.addListener(this::requireAuthenticationChanged); - transportLayerSecurity = new SimpleBooleanProperty(null, "transportLayerSecurity", settings.transportLayerSecurity); - recordLocal = new ExclusiveSelectionProperty(null, "localRecording", settings.localRecording, "Local", "Remote"); - postProcessing = new SimpleFileProperty(null, "postProcessing", settings.postProcessing); - postProcessingThreads = new SimpleIntegerProperty(null, "postProcessingThreads", settings.postProcessingThreads); - removeRecordingAfterPp = new SimpleBooleanProperty(null, "removeRecordingAfterPostProcessing", settings.removeRecordingAfterPostProcessing); - } - - private void createGui() { - ignoreList = new IgnoreList(sites); - List siteCategories = new ArrayList<>(); - for (Site site : sites) { - siteCategories.add(Category.of(site.getName(), SiteUiFactory.getUi(site).getConfigUI().createConfigPanel())); - } - - Preferences prefs = Preferences.of(new CtbrecPreferencesStorage(config), - Category.of("General", - Group.of("General", - Setting.of("User-Agent", httpUserAgent), - Setting.of("User-Agent mobile", httpUserAgentMobile), - Setting.of("Update overview interval (seconds)", overviewUpdateIntervalInSecs, "Update the thumbnail overviews every x seconds"), - Setting.of("Update thumbnails", updateThumbnails, "The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."), - Setting.of("Display stream resolution in overview", determineResolution), - Setting.of("Enable live previews (experimental)", livePreviews), - Setting.of("Start Tab", startTab), - Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance())) - ), - Group.of("Player", - Setting.of("Player", mediaPlayer), - Setting.of("Start parameters", mediaPlayerParams), - Setting.of("Maximum resolution (0 = unlimited)", maximumResolutionPlayer, "video height, e.g. 720 or 1080"), - Setting.of("Show \"Player Starting\" Message", showPlayerStarting), - Setting.of("Start only one player at a time", singlePlayer) - ) - ), - Category.of("Recorder", - Group.of("Settings", - Setting.of("Recordings Directory", recordingsDir), - Setting.of("Directory Structure", directoryStructure), - Setting.of("Split recordings after (minutes)", splitAfter).converter(SplitAfterOption.converter()), - Setting.of("Manually select stream quality", chooseStreamQuality, "Opens a dialog to select the video resolution before recording"), - Setting.of("Restrict Resolution", resolutionRange, "Only record streams with resolution within the given range"), - Setting.of("Concurrent Recordings (0 = unlimited)", concurrentRecordings), - Setting.of("Check online state every (seconds)", onlineCheckIntervalInSecs, "Check every x seconds, if a model came online"), - Setting.of("Leave space on device (GiB)", leaveSpaceOnDevice, "Stop recording, if the free space on the device gets below this threshold").converter(new GigabytesConverter()), - Setting.of("FFmpeg parameters", ffmpegParameters, "FFmpeg parameters to use when merging stream segments"), - Setting.of("File Extension", fileExtension, "File extension to use for recordings") - ), - Group.of("Location", - Setting.of("Record Location", recordLocal), - Setting.of("Server", server), - Setting.of("Port", port), - Setting.of("Path", path, "Leave empty, if you didn't change the servletContext in the server config"), - Setting.of("Require authentication", requireAuthentication), - Setting.of("Use Secure Communication (TLS)", transportLayerSecurity) - ) - ), - Category.of("Post-Processing", - Group.of("Post-Processing", - Setting.of("Post-Processing", postProcessing), - Setting.of("Threads", postProcessingThreads), - Setting.of("Delete recordings shorter than (secs)", minimumLengthInSecs, "Delete recordings, which are shorter than x seconds. 0 to disable"), - Setting.of("Remove recording after post-processing", removeRecordingAfterPp) - ) - ), - Category.of("Events & Actions", new ActionSettingsPanel(recorder)), - Category.of("Ignore List", ignoreList), - Category.of("Sites", siteCategories.toArray(new Category[0])), - Category.of("Proxy", - Group.of("Proxy", - Setting.of("Type", proxyType), - Setting.of("Host", proxyHost), - Setting.of("Port", proxyPort), - Setting.of("Username", proxyUser), - Setting.of("Password", proxyPassword) - ) - ) - ); - setContent(prefs.getView()); - prefs.expandTree(); - } - - private List getTabNames() { - return getTabPane().getTabs().stream() - .map(Tab::getText) - .collect(Collectors.toList()); - } - - private List getSplitOptions() { - List splitOptions = new ArrayList<>(); - splitOptions.add(new SplitAfterOption("disabled", 0)); - if (Config.isDevMode()) { - splitOptions.add(new SplitAfterOption("1 min", 1 * 60)); - splitOptions.add(new SplitAfterOption("3 min", 3 * 60)); - } - splitOptions.add(new SplitAfterOption("5 min", 5 * 60)); - splitOptions.add(new SplitAfterOption("10 min", 10 * 60)); - splitOptions.add(new SplitAfterOption("15 min", 15 * 60)); - splitOptions.add(new SplitAfterOption("20 min", 20 * 60)); - splitOptions.add(new SplitAfterOption("30 min", 30 * 60)); - splitOptions.add(new SplitAfterOption("60 min", 60 * 60)); - return splitOptions; - } - - private void requireAuthenticationChanged(ObservableValue obs, Boolean oldV, Boolean newV) { // NOSONAR - boolean requiresAuthentication = newV; - Config.getInstance().getSettings().requireAuthentication = requiresAuthentication; - if (requiresAuthentication) { - byte[] key = Config.getInstance().getSettings().key; - if (key == null) { - key = Hmac.generateKey(); - Config.getInstance().getSettings().key = key; - saveConfig(); - } - TextInputDialog keyDialog = new TextInputDialog(); - keyDialog.setResizable(true); - keyDialog.setTitle("Server Authentication"); - keyDialog.setHeaderText("A key has been generated"); - keyDialog.setContentText("Add this setting to your server's config.json:\n"); - keyDialog.getEditor().setText("\"key\": " + Arrays.toString(key)); - keyDialog.getEditor().setEditable(false); - keyDialog.setWidth(800); - keyDialog.setHeight(200); - keyDialog.show(); - } - } - - public void saveConfig() { - try { - Config.getInstance().save(); - } catch (IOException e) { - LOG.error("Couldn't save config", e); - } - } - - @Override - public void selected() { - if (!initialized) { - initializeProperties(); - createGui(); - initialized = true; - } - ignoreList.refresh(); - } - - @Override - public void deselected() { - // TODO maybe save the config ?!? - } - - -}