Add Backup Config button
This commit is contained in:
parent
bd02d27238
commit
11d7bfcdca
|
@ -7,24 +7,34 @@ import java.io.StringWriter;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javafx.animation.FadeTransition;
|
||||||
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Alert.AlertType;
|
import javafx.scene.control.Alert.AlertType;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static javafx.scene.control.ButtonType.*;
|
import static javafx.scene.control.ButtonType.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class Dialogs {
|
public class Dialogs {
|
||||||
|
|
||||||
private Dialogs() {}
|
private Dialogs() {}
|
||||||
|
@ -35,6 +45,76 @@ public class Dialogs {
|
||||||
Dialogs.scene = scene;
|
Dialogs.scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showToast(Scene parent, String message) {
|
||||||
|
if (parent == null) {
|
||||||
|
throw new IllegalArgumentException("Scene cannot be null for showing toast");
|
||||||
|
}
|
||||||
|
|
||||||
|
Runnable r = () -> {
|
||||||
|
Label toast = new Label(message);
|
||||||
|
toast.setStyle("-fx-background-color: #323232; -fx-text-fill: white; -fx-padding: 10px; -fx-border-radius: 5px; -fx-background-radius: 5px;");
|
||||||
|
toast.setOpacity(0);
|
||||||
|
toast.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
toast.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
Node root = parent.getRoot();
|
||||||
|
|
||||||
|
if (root instanceof StackPane) {
|
||||||
|
StackPane parentPane = (StackPane) root;
|
||||||
|
parentPane.getChildren().add(toast);
|
||||||
|
StackPane.setAlignment(toast, Pos.BOTTOM_CENTER);
|
||||||
|
StackPane.setMargin(toast, new Insets(0, 0, 40, 0));
|
||||||
|
} else if (root instanceof BorderPane) {
|
||||||
|
BorderPane parentPane = (BorderPane) root;
|
||||||
|
parentPane.setBottom(toast);
|
||||||
|
BorderPane.setAlignment(toast, Pos.BOTTOM_CENTER);
|
||||||
|
BorderPane.setMargin(toast, new Insets(0, 0, 40, 0));
|
||||||
|
} else if (root instanceof Pane) {
|
||||||
|
Pane parentPane = (Pane) root;
|
||||||
|
parentPane.getChildren().add(toast);
|
||||||
|
toast.setLayoutX((parentPane.getWidth() - toast.getWidth()) / 2);
|
||||||
|
toast.setLayoutY(parentPane.getHeight() - 40 - toast.getHeight());
|
||||||
|
} else {
|
||||||
|
log.error("Unsupported root node type for toast: " + root.getClass().getSimpleName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FadeTransition fadeIn = new FadeTransition(Duration.millis(500), toast);
|
||||||
|
fadeIn.setFromValue(0);
|
||||||
|
fadeIn.setToValue(1);
|
||||||
|
|
||||||
|
FadeTransition fadeOut = new FadeTransition(Duration.millis(500), toast);
|
||||||
|
fadeOut.setFromValue(1);
|
||||||
|
fadeOut.setToValue(0);
|
||||||
|
|
||||||
|
fadeIn.setOnFinished(e -> {
|
||||||
|
PauseTransition pause = new PauseTransition(Duration.seconds(3));
|
||||||
|
pause.setOnFinished(ev -> {
|
||||||
|
fadeOut.play();
|
||||||
|
});
|
||||||
|
pause.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
fadeOut.setOnFinished(e -> {
|
||||||
|
if (root instanceof StackPane) {
|
||||||
|
((StackPane) root).getChildren().remove(toast);
|
||||||
|
} else if (root instanceof BorderPane) {
|
||||||
|
((BorderPane) root).setBottom(null);
|
||||||
|
} else if (root instanceof Pane) {
|
||||||
|
((Pane) root).getChildren().remove(toast);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fadeIn.play();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Platform.isFxApplicationThread()) {
|
||||||
|
r.run();
|
||||||
|
} else {
|
||||||
|
Platform.runLater(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void showError(String header, String text, Throwable t) {
|
public static void showError(String header, String text, Throwable t) {
|
||||||
if (Objects.nonNull(t)) {
|
if (Objects.nonNull(t)) {
|
||||||
Dialogs.showError(scene, header, text, t);
|
Dialogs.showError(scene, header, text, t);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import ctbrec.docs.DocServer;
|
||||||
import ctbrec.recorder.Recorder;
|
import ctbrec.recorder.Recorder;
|
||||||
import ctbrec.sites.Site;
|
import ctbrec.sites.Site;
|
||||||
import ctbrec.ui.DesktopIntegration;
|
import ctbrec.ui.DesktopIntegration;
|
||||||
|
import ctbrec.ui.controls.Dialogs;
|
||||||
import ctbrec.ui.SiteUI;
|
import ctbrec.ui.SiteUI;
|
||||||
import ctbrec.ui.SiteUiFactory;
|
import ctbrec.ui.SiteUiFactory;
|
||||||
import ctbrec.ui.controls.range.DiscreteRange;
|
import ctbrec.ui.controls.range.DiscreteRange;
|
||||||
|
@ -32,16 +33,26 @@ import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TextInputDialog;
|
import javafx.scene.control.TextInputDialog;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.io.*;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.zip.*;
|
||||||
|
|
||||||
import static ctbrec.Settings.DirectoryStructure.*;
|
import static ctbrec.Settings.DirectoryStructure.*;
|
||||||
import static ctbrec.Settings.ProxyType.*;
|
import static ctbrec.Settings.ProxyType.*;
|
||||||
|
@ -228,6 +239,18 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
.ifPresent(configPanel -> siteCategories.add(Category.of(site.getName(), configPanel)));
|
.ifPresent(configPanel -> siteCategories.add(Category.of(site.getName(), configPanel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button openPacButton = new Button("View PAC File");
|
||||||
|
openPacButton.setOnAction(e -> {
|
||||||
|
String url = pacUrl.get();
|
||||||
|
if (url != null && !url.isEmpty()) {
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().browse(new URI(url));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Could not open PAC file in browser", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var storage = new CtbrecPreferencesStorage(config);
|
var storage = new CtbrecPreferencesStorage(config);
|
||||||
var prefs = Preferences.of(storage,
|
var prefs = Preferences.of(storage,
|
||||||
Category.of("General",
|
Category.of("General",
|
||||||
|
@ -323,7 +346,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
Setting.of("Port", proxyPort).needsRestart(),
|
Setting.of("Port", proxyPort).needsRestart(),
|
||||||
Setting.of("Username", proxyUser).needsRestart(),
|
Setting.of("Username", proxyUser).needsRestart(),
|
||||||
Setting.of("Password", proxyPassword).needsRestart(),
|
Setting.of("Password", proxyPassword).needsRestart(),
|
||||||
Setting.of("PAC URL", pacUrl, "URL to your Proxy Auto-Config (PAC) file (e.g. http://example.com/pac.js or file:///G:/path/to/pac.js)").needsRestart())),
|
Setting.of("PAC URL", pacUrl, "URL to your Proxy Auto-Config (PAC) file (e.g. http://example.com/pac.js or file:///G:/path/to/pac.js)").needsRestart(),
|
||||||
|
Setting.of("", openPacButton))),
|
||||||
Category.of("Advanced / Devtools",
|
Category.of("Advanced / Devtools",
|
||||||
Group.of("Networking",
|
Group.of("Networking",
|
||||||
Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests")),
|
Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests")),
|
||||||
|
@ -335,7 +359,9 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
Setting.of("Use hlsdl (if possible)", useHlsdl,
|
Setting.of("Use hlsdl (if possible)", useHlsdl,
|
||||||
"Use hlsdl to record the live streams. Some features might not work correctly."),
|
"Use hlsdl to record the live streams. Some features might not work correctly."),
|
||||||
Setting.of("hlsdl executable", hlsdlExecutable, "Path to the hlsdl executable"),
|
Setting.of("hlsdl executable", hlsdlExecutable, "Path to the hlsdl executable"),
|
||||||
Setting.of("Log hlsdl output", loghlsdlOutput, "Log hlsdl output to files in the system's temp directory"))),
|
Setting.of("Log hlsdl output", loghlsdlOutput, "Log hlsdl output to files in the system's temp directory")),
|
||||||
|
Group.of("Backup",
|
||||||
|
Setting.of("Backup Config", createBackupConfigButton()))),
|
||||||
Category.of("Help/Cfg/Log", (new HelpTab()).getContent()),
|
Category.of("Help/Cfg/Log", (new HelpTab()).getContent()),
|
||||||
Category.of("Donate", (new DonateTabFx()).getContent()));
|
Category.of("Donate", (new DonateTabFx()).getContent()));
|
||||||
Region preferencesView = prefs.getView();
|
Region preferencesView = prefs.getView();
|
||||||
|
@ -415,6 +441,51 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
return postProcessingHelpButton;
|
return postProcessingHelpButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Button createBackupConfigButton() {
|
||||||
|
var button = new Button("Backup Config");
|
||||||
|
button.setTooltip(new Tooltip("Excludes recordings, cache, and backup configs."));
|
||||||
|
button.setOnAction(e -> {
|
||||||
|
Path configDir = Config.getInstance().getConfigDir().toPath();
|
||||||
|
Path rootDir = configDir.getParent();
|
||||||
|
String backupName = "config_backup_" + System.currentTimeMillis() + ".zip";
|
||||||
|
Path backupZip = Paths.get(System.getProperty("user.dir")).resolve(backupName);
|
||||||
|
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(backupZip))) {
|
||||||
|
Files.walk(rootDir)
|
||||||
|
.filter(path -> !path.toString().contains("_backup_"))
|
||||||
|
.filter(path -> !path.toString().contains("cache"))
|
||||||
|
.filter(path -> !path.toString().contains("recordings"))
|
||||||
|
.forEach(path -> {
|
||||||
|
Path relativePath = rootDir.relativize(path);
|
||||||
|
try {
|
||||||
|
if (Files.isDirectory(path)) {
|
||||||
|
String dirEntry = relativePath.toString() + "/";
|
||||||
|
if (!dirEntry.equals("./")) {
|
||||||
|
ZipEntry entry = new ZipEntry(dirEntry);
|
||||||
|
zos.putNextEntry(entry);
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ZipEntry entry = new ZipEntry(relativePath.toString());
|
||||||
|
zos.putNextEntry(entry);
|
||||||
|
try (InputStream is = Files.newInputStream(path)) {
|
||||||
|
is.transferTo(zos);
|
||||||
|
}
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("Error adding path to backup: " + path, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log.info("Config backup created: " + backupZip);
|
||||||
|
Dialogs.showToast(this.getTabPane().getScene(), "Backup finished: " + backupZip.getFileName());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("Failed to create config backup", ex);
|
||||||
|
Dialogs.showToast(this.getTabPane().getScene(), "Backup failed!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
private Button createVariablePlayGroundButton() {
|
private Button createVariablePlayGroundButton() {
|
||||||
var button = new Button("Variable Playground");
|
var button = new Button("Variable Playground");
|
||||||
button.setOnAction(e -> variablePlayGroundDialogFactory.openDialog(this.getTabPane().getScene(), config, recorder));
|
button.setOnAction(e -> variablePlayGroundDialogFactory.openDialog(this.getTabPane().getScene(), config, recorder));
|
||||||
|
@ -600,6 +671,39 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showToast(String message) {
|
||||||
|
Label toast = new Label(message);
|
||||||
|
toast.setStyle("-fx-background-color: #323232; -fx-text-fill: white; -fx-padding: 10px; -fx-border-radius: 5px; -fx-background-radius: 5px;");
|
||||||
|
toast.setOpacity(0);
|
||||||
|
|
||||||
|
StackPane parent = (StackPane) getContent();
|
||||||
|
toast.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
toast.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
parent.getChildren().add(toast);
|
||||||
|
StackPane.setAlignment(toast, Pos.BOTTOM_CENTER);
|
||||||
|
StackPane.setMargin(toast, new Insets(0, 0, 40, 0));
|
||||||
|
|
||||||
|
FadeTransition fadeIn = new FadeTransition(Duration.millis(500), toast);
|
||||||
|
fadeIn.setFromValue(0);
|
||||||
|
fadeIn.setToValue(1);
|
||||||
|
|
||||||
|
FadeTransition fadeOut = new FadeTransition(Duration.millis(500), toast);
|
||||||
|
fadeOut.setFromValue(1);
|
||||||
|
fadeOut.setToValue(0);
|
||||||
|
|
||||||
|
fadeIn.setOnFinished(e -> {
|
||||||
|
PauseTransition pause = new PauseTransition(Duration.seconds(3));
|
||||||
|
pause.setOnFinished(ev -> fadeOut.play());
|
||||||
|
pause.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
fadeOut.setOnFinished(e -> parent.getChildren().remove(toast));
|
||||||
|
|
||||||
|
fadeIn.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public record SplitBiggerThanOption(String label, @Getter long value) {
|
public record SplitBiggerThanOption(String label, @Getter long value) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue