ctbrec-5.3.2-experimental/client/src/main/java/ctbrec/ui/settings/SettingsTab2.java

291 lines
16 KiB
Java

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<Site> sites;
private Recorder recorder;
private Preferences prefs;
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<String> startTab;
private SimpleFileProperty mediaPlayer;
private SimpleStringProperty mediaPlayerParams;
private SimpleIntegerProperty maximumResolutionPlayer;
private SimpleBooleanProperty showPlayerStarting;
private SimpleBooleanProperty singlePlayer;
private SimpleListProperty<ProxyType> proxyType;
private SimpleStringProperty proxyHost;
private SimpleStringProperty proxyPort;
private SimpleStringProperty proxyUser;
private SimpleStringProperty proxyPassword;
private SimpleDirectoryProperty recordingsDir;
private SimpleListProperty<DirectoryStructure> directoryStructure;
private SimpleListProperty<SplitAfterOption> splitAfter;
private SimpleRangeProperty<Integer> resolutionRange;
private List<Integer> labels = Arrays.asList(0, 240, 360, 480, 600, 720, 960, 1080, 1440, 2160, 4320, 8640);
private List<Integer> values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
private DiscreteRange<Integer> 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<Site> 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<Category> siteCategories = new ArrayList<>();
for (Site site : sites) {
siteCategories.add(Category.of(site.getName(), SiteUiFactory.getUi(site).getConfigUI().createConfigPanel()));
}
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());
}
private List<String> getTabNames() {
return getTabPane().getTabs().stream()
.map(Tab::getText)
.collect(Collectors.toList());
}
private List<SplitAfterOption> getSplitOptions() {
List<SplitAfterOption> 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 ?!?
}
}