291 lines
16 KiB
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 ?!?
|
|
}
|
|
|
|
|
|
}
|