mirror of
https://github.com/octopusYan/alist-gui.git
synced 2024-11-24 04:56:42 +08:00
feat: 主界面调整、日志打印、代理测试、关于页、弹窗工具、清理代码
This commit is contained in:
parent
d65990791a
commit
7c051bbf44
15
pom.xml
15
pom.xml
@ -79,20 +79,6 @@
|
|||||||
<version>${logback.version}</version>
|
<version>${logback.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- junit -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
|
||||||
<version>${junit.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
|
||||||
<version>${junit.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- common -->
|
<!-- common -->
|
||||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -205,6 +191,7 @@
|
|||||||
<arg>${cssSrcPath}/main-view.scss:${cssTargetPath}/main-view.css</arg>
|
<arg>${cssSrcPath}/main-view.scss:${cssTargetPath}/main-view.css</arg>
|
||||||
<arg>${cssSrcPath}/setup-view.scss:${cssTargetPath}/setup-view.css</arg>
|
<arg>${cssSrcPath}/setup-view.scss:${cssTargetPath}/setup-view.css</arg>
|
||||||
<arg>${cssSrcPath}/about-view.scss:${cssTargetPath}/about-view.css</arg>
|
<arg>${cssSrcPath}/about-view.scss:${cssTargetPath}/about-view.css</arg>
|
||||||
|
<arg>${cssSrcPath}/admin-panel.scss:${cssTargetPath}/admin-panel.css</arg>
|
||||||
<arg>--no-source-map</arg>
|
<arg>--no-source-map</arg>
|
||||||
</args>
|
</args>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -7,7 +7,7 @@ import cn.octopusyan.alistgui.manager.http.HttpConfig;
|
|||||||
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
||||||
import cn.octopusyan.alistgui.manager.thread.ThreadPoolManager;
|
import cn.octopusyan.alistgui.manager.thread.ThreadPoolManager;
|
||||||
import cn.octopusyan.alistgui.util.ProcessesUtil;
|
import cn.octopusyan.alistgui.util.ProcessesUtil;
|
||||||
import cn.octopusyan.alistgui.util.alert.AlertUtil;
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
@ -61,6 +61,8 @@ public class Application extends javafx.application.Application {
|
|||||||
|
|
||||||
Application.primaryStage = primaryStage;
|
Application.primaryStage = primaryStage;
|
||||||
|
|
||||||
|
Context.setApplication(this);
|
||||||
|
|
||||||
// 初始化弹窗工具
|
// 初始化弹窗工具
|
||||||
AlertUtil.initOwner(primaryStage);
|
AlertUtil.initOwner(primaryStage);
|
||||||
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
package cn.octopusyan.alistgui.util.alert;
|
package cn.octopusyan.alistgui.base;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.manager.ConfigManager;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Dialog;
|
import javafx.scene.control.Dialog;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
public abstract class BaseBuilder<T extends BaseBuilder<T, ?>, D extends Dialog<?>> {
|
public abstract class BaseBuilder<T extends BaseBuilder<T, ?>, D extends Dialog<?>> {
|
||||||
protected D dialog;
|
protected D dialog;
|
||||||
|
|
||||||
@ -30,12 +35,19 @@ public abstract class BaseBuilder<T extends BaseBuilder<T, ?>, D extends Dialog<
|
|||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public D getDialog() {
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
dialog.showAndWait();
|
|
||||||
|
Node dialogPane = dialog.getDialogPane().getContent();
|
||||||
|
if (dialogPane != null && ConfigManager.theme().isDarkMode()) {
|
||||||
|
dialogPane.setStyle(STR."""
|
||||||
|
\{dialogPane.getStyle()}
|
||||||
|
-fx-border-color: rgb(209, 209, 214, 0.5);
|
||||||
|
-fx-border-width: 1;
|
||||||
|
-fx-border-radius: 10;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.runLater(() -> dialog.showAndWait());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
@ -2,15 +2,18 @@ package cn.octopusyan.alistgui.base;
|
|||||||
|
|
||||||
import cn.octopusyan.alistgui.Application;
|
import cn.octopusyan.alistgui.Application;
|
||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
import cn.octopusyan.alistgui.manager.WindowsUtil;
|
|
||||||
import cn.octopusyan.alistgui.util.FxmlUtil;
|
import cn.octopusyan.alistgui.util.FxmlUtil;
|
||||||
|
import cn.octopusyan.alistgui.util.WindowsUtil;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import lombok.Getter;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@ -19,19 +22,34 @@ import java.util.ResourceBundle;
|
|||||||
*
|
*
|
||||||
* @author octopus_yan@foxmail.com
|
* @author octopus_yan@foxmail.com
|
||||||
*/
|
*/
|
||||||
public abstract class BaseController<P extends Pane> implements Initializable {
|
public abstract class BaseController<VM extends BaseViewModel> implements Initializable {
|
||||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
@Getter
|
||||||
|
protected final VM viewModel;
|
||||||
|
|
||||||
public BaseController() {
|
public BaseController() {
|
||||||
//初始化时保存当前Controller实例
|
//初始化时保存当前Controller实例
|
||||||
Context.getControllers().put(this.getClass().getSimpleName(), this);
|
Context.getControllers().put(this.getClass().getSimpleName(), this);
|
||||||
|
|
||||||
|
// view model
|
||||||
|
VM vm = null;
|
||||||
|
Type superclass = getClass().getGenericSuperclass();
|
||||||
|
if (superclass instanceof ParameterizedType type) {
|
||||||
|
Class<VM> clazz = (Class<VM>) type.getActualTypeArguments()[0];
|
||||||
|
try {
|
||||||
|
vm = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModel = vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
// 全局窗口拖拽
|
// 全局窗口拖拽
|
||||||
if (dragWindow()) {
|
if (dragWindow() && getRootPanel() != null) {
|
||||||
// 窗口拖拽
|
// 窗口拖拽
|
||||||
WindowsUtil.bindDragged(getRootPanel());
|
WindowsUtil.bindDragged(getRootPanel());
|
||||||
}
|
}
|
||||||
@ -60,7 +78,7 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
|||||||
*
|
*
|
||||||
* @return 根布局对象
|
* @return 根布局对象
|
||||||
*/
|
*/
|
||||||
public abstract P getRootPanel();
|
public abstract Pane getRootPanel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取根布局
|
* 获取根布局
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package cn.octopusyan.alistgui.base;
|
package cn.octopusyan.alistgui.base;
|
||||||
|
|
||||||
import cn.octopusyan.alistgui.manager.thread.ThreadPoolManager;
|
import cn.octopusyan.alistgui.manager.thread.ThreadPoolManager;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
@ -10,6 +10,12 @@ import javafx.concurrent.Task;
|
|||||||
public abstract class BaseTask extends Task<Void> {
|
public abstract class BaseTask extends Task<Void> {
|
||||||
private final ThreadPoolManager Executor = ThreadPoolManager.getInstance();
|
private final ThreadPoolManager Executor = ThreadPoolManager.getInstance();
|
||||||
protected Listener listener;
|
protected Listener listener;
|
||||||
|
@Getter
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
protected BaseTask(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void call() throws Exception {
|
protected Void call() throws Exception {
|
||||||
@ -18,10 +24,6 @@ public abstract class BaseTask extends Task<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void runLater(Runnable runnable) {
|
|
||||||
Platform.runLater(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void task() throws Exception;
|
protected abstract void task() throws Exception;
|
||||||
|
|
||||||
public void onListen(Listener listener) {
|
public void onListen(Listener listener) {
|
||||||
|
13
src/main/java/cn/octopusyan/alistgui/base/BaseViewModel.java
Normal file
13
src/main/java/cn/octopusyan/alistgui/base/BaseViewModel.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package cn.octopusyan.alistgui.base;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View Model
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public abstract class BaseViewModel {
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package cn.octopusyan.alistgui.config;
|
package cn.octopusyan.alistgui.config;
|
||||||
|
|
||||||
import atlantafx.base.theme.Theme;
|
import atlantafx.base.theme.Theme;
|
||||||
|
import cn.octopusyan.alistgui.Application;
|
||||||
import cn.octopusyan.alistgui.base.BaseController;
|
import cn.octopusyan.alistgui.base.BaseController;
|
||||||
import cn.octopusyan.alistgui.controller.AboutController;
|
import cn.octopusyan.alistgui.controller.AboutController;
|
||||||
import cn.octopusyan.alistgui.controller.MainController;
|
import cn.octopusyan.alistgui.controller.MainController;
|
||||||
@ -9,6 +10,7 @@ import cn.octopusyan.alistgui.controller.SetupController;
|
|||||||
import cn.octopusyan.alistgui.manager.ConfigManager;
|
import cn.octopusyan.alistgui.manager.ConfigManager;
|
||||||
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
||||||
import cn.octopusyan.alistgui.util.FxmlUtil;
|
import cn.octopusyan.alistgui.util.FxmlUtil;
|
||||||
|
import cn.octopusyan.alistgui.util.ProcessesUtil;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.StringBinding;
|
import javafx.beans.binding.StringBinding;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
@ -24,7 +26,7 @@ import lombok.Getter;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -34,9 +36,11 @@ import java.util.*;
|
|||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
public class Context {
|
public class Context {
|
||||||
|
@Getter
|
||||||
|
private static Application application;
|
||||||
private static final Logger log = LoggerFactory.getLogger(Context.class);
|
private static final Logger log = LoggerFactory.getLogger(Context.class);
|
||||||
private static Scene scene;
|
private static Scene scene;
|
||||||
private static final IntegerProperty currentViewIndexProperty = new SimpleIntegerProperty(0);
|
private static final IntegerProperty currentViewIndex = new SimpleIntegerProperty(0);
|
||||||
private static final ObjectProperty<Theme> theme = new SimpleObjectProperty<>(ConfigManager.theme());
|
private static final ObjectProperty<Theme> theme = new SimpleObjectProperty<>(ConfigManager.theme());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +88,10 @@ public class Context {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setApplication(Application application) {
|
||||||
|
Context.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
public static ObjectProperty<Theme> themeProperty() {
|
public static ObjectProperty<Theme> themeProperty() {
|
||||||
return theme;
|
return theme;
|
||||||
}
|
}
|
||||||
@ -121,8 +129,7 @@ public class Context {
|
|||||||
LANGUAGE_RESOURCE_FACTORY.setResourceBundle(ResourceBundle.getBundle(LANGUAGE_RESOURCE_NAME, locale));
|
LANGUAGE_RESOURCE_FACTORY.setResourceBundle(ResourceBundle.getBundle(LANGUAGE_RESOURCE_NAME, locale));
|
||||||
|
|
||||||
log.info("language changed to {}", locale);
|
log.info("language changed to {}", locale);
|
||||||
if (ConsoleLog.isInit())
|
ConsoleLog.info(STR."language changed to \{locale}");
|
||||||
ConsoleLog.info(STR."language changed to \{locale}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,13 +154,6 @@ public class Context {
|
|||||||
* 初始化 语言
|
* 初始化 语言
|
||||||
*/
|
*/
|
||||||
private static void initI18n() {
|
private static void initI18n() {
|
||||||
currentLocaleProperty().addListener((_, _, locale) -> Platform.runLater(() -> {
|
|
||||||
try {
|
|
||||||
loadScene();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("", e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,45 +171,58 @@ public class Context {
|
|||||||
* 初始化场景
|
* 初始化场景
|
||||||
*
|
*
|
||||||
* @return Scene
|
* @return Scene
|
||||||
* @throws IOException 如果在加载过程中发生错误
|
|
||||||
*/
|
*/
|
||||||
public static Scene initScene() throws IOException {
|
public static Scene initScene() {
|
||||||
initI18n();
|
// locale监听; 切换后,重新加载界面
|
||||||
|
currentLocaleProperty().addListener((_, _, locale) -> Platform.runLater(Context::loadScene));
|
||||||
|
// 加载
|
||||||
loadScene();
|
loadScene();
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadScene() throws IOException {
|
private static void loadScene() {
|
||||||
FXMLLoader loader = FxmlUtil.load("root-view");
|
try {
|
||||||
loader.setControllerFactory(Context.getControlFactory());
|
FXMLLoader loader = FxmlUtil.load("root-view");
|
||||||
//底层面板
|
loader.setControllerFactory(Context.getControlFactory());
|
||||||
Pane root = loader.load();
|
//底层面板
|
||||||
Optional.ofNullable(scene).ifPresentOrElse(
|
Pane root = loader.load();
|
||||||
s -> s.setRoot(root),
|
Optional.ofNullable(scene).ifPresentOrElse(
|
||||||
() -> {
|
s -> s.setRoot(root),
|
||||||
scene = new Scene(root, root.getPrefWidth() + 20, root.getPrefHeight() + 20, Color.TRANSPARENT);
|
() -> {
|
||||||
URL resource = Objects.requireNonNull(Context.class.getResource("/css/root-view.css"));
|
scene = new Scene(root, root.getPrefWidth() + 20, root.getPrefHeight() + 20, Color.TRANSPARENT);
|
||||||
scene.getStylesheets().addAll(resource.toExternalForm());
|
URL resource = Objects.requireNonNull(Context.class.getResource("/css/root-view.css"));
|
||||||
scene.setFill(Color.TRANSPARENT);
|
scene.getStylesheets().addAll(resource.toExternalForm());
|
||||||
}
|
scene.setFill(Color.TRANSPARENT);
|
||||||
);
|
}
|
||||||
|
);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.error("loadScene error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static int currentViewIndex() {
|
||||||
* 设置当前展示的界面
|
return currentViewIndex.get();
|
||||||
*
|
|
||||||
* @param index 界面Index
|
|
||||||
*/
|
|
||||||
public static void setCurrentViewIndex(Number index) {
|
|
||||||
currentViewIndexProperty.setValue(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static IntegerProperty currentViewIndexProperty() {
|
||||||
* 获取当前展示的界面Index
|
return currentViewIndex;
|
||||||
*
|
}
|
||||||
* @return 界面Index
|
|
||||||
*/
|
public static void openUrl(String url) {
|
||||||
public static Integer getCurrentViewIndex() {
|
getApplication().getHostServices().showDocument(url);
|
||||||
return currentViewIndexProperty.get();
|
}
|
||||||
|
|
||||||
|
public static void openFolder(File file) {
|
||||||
|
openFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openFile(File file) {
|
||||||
|
if (!file.exists()) return;
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
ProcessesUtil.init(file.getAbsolutePath()).exec("explorer.exe .");
|
||||||
|
} else {
|
||||||
|
ProcessesUtil.init(file.getParentFile().getAbsolutePath()).exec(STR."explorer.exe /select,\{file.getName()}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,9 +2,9 @@ package cn.octopusyan.alistgui.controller;
|
|||||||
|
|
||||||
import cn.octopusyan.alistgui.base.BaseController;
|
import cn.octopusyan.alistgui.base.BaseController;
|
||||||
import cn.octopusyan.alistgui.manager.ConfigManager;
|
import cn.octopusyan.alistgui.manager.ConfigManager;
|
||||||
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
import cn.octopusyan.alistgui.viewModel.AboutViewModule;
|
import cn.octopusyan.alistgui.viewModel.AboutViewModule;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
*
|
*
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
public class AboutController extends BaseController<VBox> {
|
public class AboutController extends BaseController<AboutViewModule> {
|
||||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@ -23,10 +23,6 @@ public class AboutController extends BaseController<VBox> {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public Label aListVersion;
|
public Label aListVersion;
|
||||||
@FXML
|
|
||||||
public Button checkAppVersion;
|
|
||||||
|
|
||||||
private final AboutViewModule viewModule = new AboutViewModule();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VBox getRootPanel() {
|
public VBox getRootPanel() {
|
||||||
@ -45,11 +41,18 @@ public class AboutController extends BaseController<VBox> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initViewAction() {
|
public void initViewAction() {
|
||||||
aListVersion.textProperty().bindBidirectional(viewModule.aListVersionProperty());
|
aListVersion.textProperty().bindBidirectional(viewModel.aListVersionProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void checkAListUpdate() {
|
public void checkAListUpdate() {
|
||||||
viewModule.checkUpdate(ConfigManager.aList());
|
viewModel.checkUpdate(ConfigManager.aList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void checkGuiUpdate() {
|
||||||
|
// TODO 检查 gui 版本
|
||||||
|
AlertUtil.info("待开发。。。").show();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,25 @@ import cn.octopusyan.alistgui.base.BaseController;
|
|||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
import cn.octopusyan.alistgui.manager.AListManager;
|
import cn.octopusyan.alistgui.manager.AListManager;
|
||||||
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
||||||
import javafx.application.Platform;
|
import cn.octopusyan.alistgui.util.FxmlUtil;
|
||||||
|
import cn.octopusyan.alistgui.viewModel.MainViewModel;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主界面控制器
|
* 主界面控制器
|
||||||
*
|
*
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
public class MainController extends BaseController<VBox> {
|
public class MainController extends BaseController<MainViewModel> {
|
||||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@ -30,6 +35,10 @@ public class MainController extends BaseController<VBox> {
|
|||||||
public Button statusLabel;
|
public Button statusLabel;
|
||||||
@FXML
|
@FXML
|
||||||
public Button startButton;
|
public Button startButton;
|
||||||
|
@FXML
|
||||||
|
public MenuItem browserButton;
|
||||||
|
|
||||||
|
private PasswordController controller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VBox getRootPanel() {
|
public VBox getRootPanel() {
|
||||||
@ -43,31 +52,18 @@ public class MainController extends BaseController<VBox> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initViewStyle() {
|
public void initViewStyle() {
|
||||||
AListManager.runningProperty().addListener(_ -> setStartButton(AListManager.isRunning()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initViewAction() {
|
public void initViewAction() {
|
||||||
AListManager.runningProperty().addListener((_, _, running) -> setStartButton(running));
|
viewModel.startBtnStyleCssProperty().bindContentBidirectional(startButton.getStyleClass());
|
||||||
}
|
viewModel.statusLabelStyleCssProperty().bindContentBidirectional(statusLabel.getStyleClass());
|
||||||
|
viewModel.startBtnTextProperty().bindBidirectional(startButton.textProperty());
|
||||||
private void setStartButton(boolean running) {
|
viewModel.statusLabelTextProperty().bindBidirectional(statusLabel.textProperty());
|
||||||
String removeStyle = running ? "success" : "danger";
|
viewModel.browserButtonDisableProperty().bindBidirectional(browserButton.disableProperty());
|
||||||
String addStyle = running ? "danger" : "success";
|
|
||||||
String button = Context.getLanguageBinding(STR."main.control.\{running ? "stop" : "start"}").get();
|
|
||||||
String status = Context.getLanguageBinding(STR."main.status.label-\{running ? "running" : "stop"}").get();
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
startButton.getStyleClass().remove(removeStyle);
|
|
||||||
startButton.getStyleClass().add(addStyle);
|
|
||||||
startButton.textProperty().set(button);
|
|
||||||
|
|
||||||
statusLabel.getStyleClass().remove(addStyle);
|
|
||||||
statusLabel.getStyleClass().add(removeStyle);
|
|
||||||
statusLabel.textProperty().set(status);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start button
|
||||||
@FXML
|
@FXML
|
||||||
public void start() {
|
public void start() {
|
||||||
if (AListManager.isRunning()) {
|
if (AListManager.isRunning()) {
|
||||||
@ -77,8 +73,41 @@ public class MainController extends BaseController<VBox> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// password button
|
||||||
|
@FXML
|
||||||
|
public void adminPassword() throws IOException {
|
||||||
|
if (controller == null) {
|
||||||
|
FXMLLoader load = FxmlUtil.load("admin-panel");
|
||||||
|
load.load();
|
||||||
|
controller = load.getController();
|
||||||
|
}
|
||||||
|
controller.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart button
|
||||||
@FXML
|
@FXML
|
||||||
public void restart() {
|
public void restart() {
|
||||||
AListManager.restart();
|
AListManager.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// more button
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void openInBrowser() {
|
||||||
|
AListManager.openScheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void openLogFolder() {
|
||||||
|
AListManager.openLogFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void openConfig() {
|
||||||
|
AListManager.openConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getText(String key) {
|
||||||
|
return Context.getLanguageBinding(key).get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
package cn.octopusyan.alistgui.controller;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Popover;
|
||||||
|
import cn.hutool.core.swing.clipboard.ClipboardUtil;
|
||||||
|
import cn.octopusyan.alistgui.base.BaseController;
|
||||||
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
|
import cn.octopusyan.alistgui.manager.AListManager;
|
||||||
|
import cn.octopusyan.alistgui.viewModel.AdminPanelViewModel;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.PasswordField;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员密码
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
public class PasswordController extends BaseController<AdminPanelViewModel> {
|
||||||
|
@FXML
|
||||||
|
private AnchorPane adminPanel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public TextField usernameField;
|
||||||
|
@FXML
|
||||||
|
public Button copyUsername;
|
||||||
|
@FXML
|
||||||
|
public PasswordField passwordField;
|
||||||
|
@FXML
|
||||||
|
public Button refreshPassword;
|
||||||
|
@FXML
|
||||||
|
public Button savePassword;
|
||||||
|
@FXML
|
||||||
|
public Button copyPassword;
|
||||||
|
|
||||||
|
private RootController root;
|
||||||
|
|
||||||
|
private final Popover pop = new Popover(new Text(Context.getLanguageBinding("msg.alist.pwd.copy").get()));
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pane getRootPanel() {
|
||||||
|
return adminPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initData() {
|
||||||
|
root = (RootController) Context.getControllers().get("RootController");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initViewStyle() {
|
||||||
|
pop.setArrowLocation(Popover.ArrowLocation.BOTTOM_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initViewAction() {
|
||||||
|
passwordField.textProperty().bindBidirectional(viewModel.passwordProperty());
|
||||||
|
passwordField.setOnMouseClicked(event -> {
|
||||||
|
// 点击密码框时,设置为可修改状态
|
||||||
|
passwordField.setEditable(true);
|
||||||
|
refreshPassword.setVisible(true);
|
||||||
|
refreshPassword.setManaged(true);
|
||||||
|
});
|
||||||
|
ChangeListener<Boolean> changeListener = (_, _, focused) -> {
|
||||||
|
if (!focused && !refreshPassword.isFocused()
|
||||||
|
&& !copyPassword.isFocused()
|
||||||
|
&& StringUtils.equals(passwordField.getText(), AListManager.passwordProperty().get())) {
|
||||||
|
// 当密码栏失去焦点,如果密码未变更,设置为不可用状态
|
||||||
|
passwordField.setEditable(false);
|
||||||
|
refreshPassword.setVisible(false);
|
||||||
|
refreshPassword.setManaged(false);
|
||||||
|
savePassword.setVisible(false);
|
||||||
|
savePassword.setManaged(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
passwordField.focusedProperty().addListener(changeListener);
|
||||||
|
refreshPassword.focusedProperty().addListener(changeListener);
|
||||||
|
savePassword.focusedProperty().addListener(changeListener);
|
||||||
|
copyPassword.focusedProperty().addListener(changeListener);
|
||||||
|
// 监听密码修改,展示保存按钮
|
||||||
|
passwordField.textProperty().addListener((_, _, newValue) -> {
|
||||||
|
boolean equals = StringUtils.equals(newValue, AListManager.passwordProperty().get());
|
||||||
|
savePassword.setVisible(!equals);
|
||||||
|
savePassword.setManaged(!equals);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
root.showModal(getRootPanel(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void close() {
|
||||||
|
passwordField.setText(AListManager.passwordProperty().get());
|
||||||
|
root.hideModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void copyUsername() {
|
||||||
|
usernameField.copy();
|
||||||
|
pop.show(copyUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void savePassword(ActionEvent event) {
|
||||||
|
Object source = event.getSource();
|
||||||
|
if (refreshPassword.equals(source)) {
|
||||||
|
AListManager.resetPassword();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AListManager.resetPassword(passwordField.getText());
|
||||||
|
savePassword.setVisible(false);
|
||||||
|
savePassword.setManaged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void copyPassword() {
|
||||||
|
ClipboardUtil.setStr(AListManager.password());
|
||||||
|
pop.show(copyPassword);
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,35 @@
|
|||||||
package cn.octopusyan.alistgui.controller;
|
package cn.octopusyan.alistgui.controller;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.ModalPane;
|
||||||
import cn.octopusyan.alistgui.base.BaseController;
|
import cn.octopusyan.alistgui.base.BaseController;
|
||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
|
import cn.octopusyan.alistgui.util.WindowsUtil;
|
||||||
|
import cn.octopusyan.alistgui.viewModel.RootViewModel;
|
||||||
import com.gluonhq.emoji.EmojiData;
|
import com.gluonhq.emoji.EmojiData;
|
||||||
import com.gluonhq.emoji.util.EmojiImageUtils;
|
import com.gluonhq.emoji.util.EmojiImageUtils;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.TabPane;
|
import javafx.scene.control.TabPane;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.StackPane;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主页面控制器
|
* Root 页面控制器
|
||||||
*
|
*
|
||||||
* @author octopus_yan@foxmail.com
|
* @author octopus_yan@foxmail.com
|
||||||
*/
|
*/
|
||||||
public class RootController extends BaseController<VBox> {
|
public class RootController extends BaseController<RootViewModel> {
|
||||||
|
|
||||||
private double xOffset;
|
|
||||||
private double yOffset;
|
|
||||||
|
|
||||||
// 布局
|
// 布局
|
||||||
@FXML
|
@FXML
|
||||||
private VBox rootPane;
|
private StackPane rootPane;
|
||||||
@FXML
|
@FXML
|
||||||
private HBox windowHeader;
|
private HBox windowHeader;
|
||||||
@FXML
|
@FXML
|
||||||
@ -48,13 +51,15 @@ public class RootController extends BaseController<VBox> {
|
|||||||
@FXML
|
@FXML
|
||||||
public Button sponsor;
|
public Button sponsor;
|
||||||
|
|
||||||
|
private final ModalPane modalPane = new ModalPane();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取根布局
|
* 获取根布局
|
||||||
*
|
*
|
||||||
* @return 根布局对象
|
* @return 根布局对象
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public VBox getRootPanel() {
|
public StackPane getRootPanel() {
|
||||||
return rootPane;
|
return rootPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +68,7 @@ public class RootController extends BaseController<VBox> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initData() {
|
public void initData() {
|
||||||
tabPane.getSelectionModel().select(Context.getCurrentViewIndex());
|
tabPane.getSelectionModel().select(viewModel.currentViewIndexProperty().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,6 +90,16 @@ public class RootController extends BaseController<VBox> {
|
|||||||
ImageView juice = EmojiImageUtils.emojiView(icon, 25);
|
ImageView juice = EmojiImageUtils.emojiView(icon, 25);
|
||||||
sponsor.setGraphic(juice);
|
sponsor.setGraphic(juice);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getRootPanel().getChildren().add(modalPane);
|
||||||
|
modalPane.setId("modalPane");
|
||||||
|
// reset side and transition to reuse a single modal pane between different examples
|
||||||
|
modalPane.displayProperty().addListener((obs, old, val) -> {
|
||||||
|
if (!val) {
|
||||||
|
modalPane.setAlignment(Pos.CENTER);
|
||||||
|
modalPane.usePredefinedTransitionFactories(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,17 +115,29 @@ public class RootController extends BaseController<VBox> {
|
|||||||
getWindow().setAlwaysOnTop(newVal);
|
getWindow().setAlwaysOnTop(newVal);
|
||||||
});
|
});
|
||||||
|
|
||||||
windowHeader.setOnMousePressed(event -> {
|
WindowsUtil.bindDragged(windowHeader);
|
||||||
xOffset = getWindow().getX() - event.getScreenX();
|
|
||||||
yOffset = getWindow().getY() - event.getScreenY();
|
|
||||||
});
|
|
||||||
windowHeader.setOnMouseDragged(event -> {
|
|
||||||
getWindow().setX(event.getScreenX() + xOffset);
|
|
||||||
getWindow().setY(event.getScreenY() + yOffset);
|
|
||||||
});
|
|
||||||
|
|
||||||
tabPane.getSelectionModel()
|
viewModel.currentViewIndexProperty().bind(tabPane.getSelectionModel().selectedIndexProperty());
|
||||||
.selectedIndexProperty()
|
}
|
||||||
.addListener((_, _, newValue) -> Context.setCurrentViewIndex(newValue));
|
|
||||||
|
@FXML
|
||||||
|
public void openDocument() {
|
||||||
|
String locale = Context.getCurrentLocale().equals(Locale.ENGLISH) ? "" : "zh/";
|
||||||
|
Context.openUrl(STR."https://alist.nn.ci/\{locale}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void openGithub() {
|
||||||
|
Context.openUrl("https://github.com/alist-org/alist");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showModal(Node node, boolean persistent) {
|
||||||
|
modalPane.show(node);
|
||||||
|
modalPane.setPersistent(persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideModal() {
|
||||||
|
modalPane.hide(false);
|
||||||
|
modalPane.setPersistent(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import java.util.Locale;
|
|||||||
*
|
*
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
public class SetupController extends BaseController<VBox> implements Initializable {
|
public class SetupController extends BaseController<SetupViewModel> implements Initializable {
|
||||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@ -51,8 +51,6 @@ public class SetupController extends BaseController<VBox> implements Initializab
|
|||||||
@FXML
|
@FXML
|
||||||
public TextField proxyPort;
|
public TextField proxyPort;
|
||||||
|
|
||||||
private final SetupViewModel viewModule = new SetupViewModel();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VBox getRootPanel() {
|
public VBox getRootPanel() {
|
||||||
return setupView;
|
return setupView;
|
||||||
@ -91,20 +89,19 @@ public class SetupController extends BaseController<VBox> implements Initializab
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initViewAction() {
|
public void initViewAction() {
|
||||||
autoStartCheckBox.selectedProperty().bindBidirectional(viewModule.autoStartProperty());
|
//
|
||||||
silentStartupCheckBox.selectedProperty().bindBidirectional(viewModule.silentStartupProperty());
|
autoStartCheckBox.selectedProperty().bindBidirectional(viewModel.autoStartProperty());
|
||||||
languageComboBox.getSelectionModel().selectedItemProperty()
|
silentStartupCheckBox.selectedProperty().bindBidirectional(viewModel.silentStartupProperty());
|
||||||
.subscribe(locale -> viewModule.languageProperty().set(locale));
|
proxyHost.textProperty().bindBidirectional(viewModel.proxyHostProperty());
|
||||||
themeComboBox.getSelectionModel().selectedItemProperty()
|
proxyPort.textProperty().bindBidirectional(viewModel.proxyPortProperty());
|
||||||
.subscribe(theme -> viewModule.themeProperty().set(theme));
|
|
||||||
proxySetupComboBox.getSelectionModel().selectedItemProperty()
|
viewModel.languageProperty().bind(languageComboBox.getSelectionModel().selectedItemProperty());
|
||||||
.subscribe((setup) -> viewModule.proxySetupProperty().set(setup));
|
viewModel.themeProperty().bind(themeComboBox.getSelectionModel().selectedItemProperty());
|
||||||
proxyHost.textProperty().bindBidirectional(viewModule.proxyHostProperty());
|
viewModel.proxySetupProperty().bind(proxySetupComboBox.getSelectionModel().selectedItemProperty());
|
||||||
proxyPort.textProperty().bindBidirectional(viewModule.proxyPortProperty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void proxyTest() {
|
public void proxyTest() {
|
||||||
viewModule.proxyTest();
|
viewModel.proxyTest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,87 @@ package cn.octopusyan.alistgui.manager;
|
|||||||
|
|
||||||
import cn.octopusyan.alistgui.config.Constants;
|
import cn.octopusyan.alistgui.config.Constants;
|
||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
|
import cn.octopusyan.alistgui.model.AListConfig;
|
||||||
|
import cn.octopusyan.alistgui.task.CheckUpdateTask;
|
||||||
|
import cn.octopusyan.alistgui.task.DownloadTask;
|
||||||
|
import cn.octopusyan.alistgui.task.listener.TaskListener;
|
||||||
|
import cn.octopusyan.alistgui.util.DownloadUtil;
|
||||||
import cn.octopusyan.alistgui.util.ProcessesUtil;
|
import cn.octopusyan.alistgui.util.ProcessesUtil;
|
||||||
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AList 管理
|
* AList 管理
|
||||||
*
|
*
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class AListManager {
|
public class AListManager {
|
||||||
|
public static final String DATA_DIR = STR."\{Constants.BIN_DIR_PATH}\{File.separator}data";
|
||||||
|
public static final String LOG_DIR = STR."\{DATA_DIR}\{File.separator}log";
|
||||||
|
public static final String CONFIG_FILE = STR."\{DATA_DIR}\{File.separator}config.json";
|
||||||
|
|
||||||
public static final String START_COMMAND = STR."\{Constants.ALIST_FILE} server";
|
public static final String START_COMMAND = STR."\{Constants.ALIST_FILE} server";
|
||||||
|
public static final String PWD_SET_COMMAND = STR."\{Constants.ALIST_FILE} admin set";
|
||||||
|
public static final String PWD_RANDOM_COMMAND = STR."\{Constants.ALIST_FILE} admin random";
|
||||||
|
|
||||||
|
public static final String DEFAULT_SCHEME = "0.0.0.0:5244";
|
||||||
|
public static final String PASSWORD_MSG_REG = ".*password( is)?: (.*)$";
|
||||||
|
public static AListConfig aListConfig;
|
||||||
|
|
||||||
|
public static final File configFile = new File(CONFIG_FILE);
|
||||||
|
|
||||||
private static final ProcessesUtil util;
|
private static final ProcessesUtil util;
|
||||||
private static final BooleanProperty running = new SimpleBooleanProperty(false);
|
private static final BooleanProperty running = new SimpleBooleanProperty(false);
|
||||||
|
private static final StringProperty password = new SimpleStringProperty("******");
|
||||||
|
private static DownloadTask downloadTask;
|
||||||
|
|
||||||
|
private static final ProcessesUtil.OnExecuteListener runningListener;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
util = ProcessesUtil.init(Constants.BIN_DIR_PATH);
|
util = ProcessesUtil.init(Constants.BIN_DIR_PATH);
|
||||||
|
loadConfig();
|
||||||
|
runningListener = new ProcessesUtil.OnExecuteListener() {
|
||||||
|
@Override
|
||||||
|
public void onExecute(String msg) {
|
||||||
|
if (hasConfig() && aListConfig == null) loadConfig();
|
||||||
|
|
||||||
|
if (msg.contains("start HTTP server")) {
|
||||||
|
Platform.runLater(() -> running.set(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleLog.msg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExecuteSuccess(boolean success) {
|
||||||
|
Platform.runLater(() -> running.set(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExecuteError(Exception e) {
|
||||||
|
Platform.runLater(() -> running.set(false));
|
||||||
|
log.error("AList error", e);
|
||||||
|
ConsoleLog.error("AList", e.getMessage());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================={ Property }====================================
|
||||||
|
|
||||||
|
private static void loadConfig() {
|
||||||
|
if (hasConfig()) {
|
||||||
|
aListConfig = ConfigManager.loadConfig(CONFIG_FILE, AListConfig.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BooleanProperty runningProperty() {
|
public static BooleanProperty runningProperty() {
|
||||||
@ -29,31 +93,50 @@ public class AListManager {
|
|||||||
return running.get();
|
return running.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasConfig() {
|
||||||
|
return configFile.exists() && aListConfig != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String scheme() {
|
||||||
|
return hasConfig() ?
|
||||||
|
STR."\{aListConfig.getScheme().getAddress()}:\{aListConfig.getScheme().getHttpPort()}"
|
||||||
|
: DEFAULT_SCHEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringProperty passwordProperty() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String password() {
|
||||||
|
return password.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================{ action }====================================
|
||||||
|
|
||||||
|
public static void openConfig() {
|
||||||
|
Context.openFile(new File(CONFIG_FILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openLogFolder() {
|
||||||
|
Context.openFolder(new File(LOG_DIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openScheme() {
|
||||||
|
Context.openUrl(STR."http://\{scheme()}");
|
||||||
|
}
|
||||||
|
|
||||||
public static void start() {
|
public static void start() {
|
||||||
ConsoleLog.info(getText("alist.status.start"));
|
if (!checkAList()) return;
|
||||||
if (running.get()) {
|
|
||||||
|
if (running.get() || util.isRunning()) {
|
||||||
ConsoleLog.warning(getText("alist.status.start.running"));
|
ConsoleLog.warning(getText("alist.status.start.running"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
running.set(true);
|
ConsoleLog.info(getText("alist.status.start"));
|
||||||
util.exec(START_COMMAND, new ProcessesUtil.OnExecuteListener() {
|
|
||||||
@Override
|
|
||||||
public void onExecute(String msg) {
|
|
||||||
if (ConsoleLog.isInit())
|
|
||||||
ConsoleLog.msg(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
loadConfig();
|
||||||
public void onExecuteSuccess(int exitValue) {
|
util.exec(START_COMMAND, runningListener);
|
||||||
running.set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExecuteError(Exception e) {
|
|
||||||
running.set(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void stop() {
|
public static void stop() {
|
||||||
@ -65,21 +148,96 @@ public class AListManager {
|
|||||||
util.destroy();
|
util.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ChangeListener<Boolean> changeListener;
|
static ChangeListener<Boolean> restartListener;
|
||||||
|
|
||||||
public static void restart() {
|
public static void restart() {
|
||||||
stop();
|
if (!running.get()) {
|
||||||
changeListener = (_, _, run) -> {
|
|
||||||
if (run) return;
|
|
||||||
start();
|
start();
|
||||||
if (changeListener != null) {
|
} else {
|
||||||
running.removeListener(changeListener);
|
stop();
|
||||||
|
|
||||||
|
restartListener = (_, _, run) -> {
|
||||||
|
if (run) return;
|
||||||
|
running.removeListener(restartListener);
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
running.addListener(restartListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetPassword() {
|
||||||
|
resetPassword("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChangeListener<Boolean> resetPasswordListener;
|
||||||
|
|
||||||
|
public static void resetPassword(String pwd) {
|
||||||
|
String command = StringUtils.isNoneEmpty(pwd) ?
|
||||||
|
STR."\{PWD_SET_COMMAND} \{pwd}" : PWD_RANDOM_COMMAND;
|
||||||
|
|
||||||
|
if (isRunning()) {
|
||||||
|
util.exec(command, ConsoleLog::msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start();
|
||||||
|
resetPasswordListener = (_, _, newValue) -> {
|
||||||
|
if (newValue) {
|
||||||
|
running.removeListener(resetPasswordListener);
|
||||||
|
util.exec(command, ConsoleLog::msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
running.addListener(changeListener);
|
running.addListener(resetPasswordListener);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================={ private }====================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO 点击开始时检查 aList 执行文件
|
||||||
|
*/
|
||||||
|
private static boolean checkAList() {
|
||||||
|
if (new File(Constants.ALIST_FILE).exists()) return true;
|
||||||
|
|
||||||
|
if (downloadTask != null && downloadTask.isRunning()) {
|
||||||
|
ConsoleLog.warning("AList Downloading ...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var task = new CheckUpdateTask(ConfigManager.aList());
|
||||||
|
task.onListen(new TaskListener.UpgradeUpgradeListener(task) {
|
||||||
|
@Override
|
||||||
|
public void onChecked(boolean hasUpgrade, String version) {
|
||||||
|
Platform.runLater(() -> showDownload(version));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.execute();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void showDownload(String version) {
|
||||||
|
String content = STR."""
|
||||||
|
\{getText("msg.alist.download.notfile")}
|
||||||
|
\{Context.getLanguageBinding("update.remote").get()} : \{version}
|
||||||
|
""";
|
||||||
|
downloadTask = DownloadUtil.startDownload(ConfigManager.aList(), version, () -> {
|
||||||
|
DownloadUtil.unzip(ConfigManager.aList());
|
||||||
|
Platform.runLater(() -> ConfigManager.aListVersion(version));
|
||||||
|
restart();
|
||||||
|
});
|
||||||
|
AlertUtil.confirm()
|
||||||
|
.title("Download ALst")
|
||||||
|
.header(null)
|
||||||
|
.content(content)
|
||||||
|
.show(downloadTask::execute);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getText(String code) {
|
private static String getText(String code) {
|
||||||
return Context.getLanguageBinding(code).get();
|
return Context.getLanguageBinding(code).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void tmpPassword(String pwd) {
|
||||||
|
Platform.runLater(() -> password.set(pwd));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import org.apache.commons.lang3.LocaleUtils;
|
import org.apache.commons.lang3.LocaleUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -66,7 +67,7 @@ public class ConfigManager {
|
|||||||
guiConfig = loadConfig(Constants.GUI_CONFIG_PATH, GuiConfig.class);
|
guiConfig = loadConfig(Constants.GUI_CONFIG_PATH, GuiConfig.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T loadConfig(String path, Class<T> clazz) {
|
public static <T> T loadConfig(String path, Class<T> clazz) {
|
||||||
File src = new File(path);
|
File src = new File(path);
|
||||||
try {
|
try {
|
||||||
if (!src.exists()) {
|
if (!src.exists()) {
|
||||||
@ -250,8 +251,12 @@ public class ConfigManager {
|
|||||||
return aList().getVersion();
|
return aList().getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static StringProperty aListVersionProperty() {
|
||||||
|
return aList().versionProperty();
|
||||||
|
}
|
||||||
|
|
||||||
public static void aListVersion(String version) {
|
public static void aListVersion(String version) {
|
||||||
aList().setVersion(version);
|
aListVersionProperty().set(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Gui gui() {
|
public static Gui gui() {
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package cn.octopusyan.alistgui.manager;
|
package cn.octopusyan.alistgui.manager;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Popover;
|
||||||
import atlantafx.base.util.BBCodeParser;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import cn.hutool.core.swing.clipboard.ClipboardUtil;
|
||||||
import cn.hutool.core.util.ReUtil;
|
import cn.hutool.core.util.ReUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.control.Hyperlink;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -14,6 +19,7 @@ import org.apache.commons.lang3.time.DateFormatUtils;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -27,8 +33,9 @@ public class ConsoleLog {
|
|||||||
public static final String format = "yyyy/MM/dd hh:mm:ss";
|
public static final String format = "yyyy/MM/dd hh:mm:ss";
|
||||||
private volatile static ConsoleLog log;
|
private volatile static ConsoleLog log;
|
||||||
private final VBox textArea;
|
private final VBox textArea;
|
||||||
private final static String CONSOLE_COLOR_PREFIX = "^\033\\[(\\d+)m(.*)\033\\[0m(.*)$";
|
private final static String CONSOLE_COLOR_PREFIX = "^\033[";
|
||||||
private final static String CONSOLE_COLOR_SUFFIX = "\033[0m";
|
private final static String CONSOLE_MSG_REX = "^\033\\[(\\d+)m(.*)\033\\[0m(.*)$";
|
||||||
|
private final static String URL_IP_REX = "^((ht|f)tps?:\\/\\/)?[\\w-+&@#/%?=~_|!:,.;]*[\\w-+&@#/%=~_|]+(:\\d{1,5})?\\/?$";
|
||||||
|
|
||||||
private ConsoleLog(ScrollPane logAreaSp, VBox textArea) {
|
private ConsoleLog(ScrollPane logAreaSp, VBox textArea) {
|
||||||
this.textArea = textArea;
|
this.textArea = textArea;
|
||||||
@ -78,37 +85,104 @@ public class ConsoleLog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void msg(String message, Object... param) {
|
public static void msg(String message, Object... param) {
|
||||||
if (message.contains("\033[")) {
|
if (StringUtils.isEmpty(message) || !isInit()) return;
|
||||||
message = resetConsoleColor(message);
|
message = message.strip();
|
||||||
}
|
message = StrUtil.format(message, param);
|
||||||
|
|
||||||
Node text = BBCodeParser.createFormattedText(STR."\{StrUtil.format(message, param)}");
|
// 多颜色消息处理
|
||||||
Platform.runLater(() -> log.textArea.getChildren().add(text));
|
if (StringUtils.countMatches(message, CONSOLE_COLOR_PREFIX) > 1) {
|
||||||
|
String[] split = message.replace(CONSOLE_MSG_REX, "\n%s".formatted(CONSOLE_COLOR_PREFIX)).split("\n");
|
||||||
|
List<String> msgs = Arrays.stream(split).toList();
|
||||||
|
for (String msg : msgs) {
|
||||||
|
msg(msg);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
message = setPwdText(message);
|
||||||
|
message = resetConsoleColor(message);
|
||||||
|
|
||||||
|
print(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printLog(String tag, Level level, String message, Object... param) {
|
public static void printLog(String tag, Level level, String message, Object... param) {
|
||||||
|
if (!isInit()) return;
|
||||||
|
|
||||||
|
// 时间
|
||||||
String time = DateFormatUtils.format(new Date(), format);
|
String time = DateFormatUtils.format(new Date(), format);
|
||||||
time = STR."[color=-color-accent-emphasis]\{time}[/color]";
|
time = STR."[color=-color-accent-emphasis]\{time}[/color]";
|
||||||
|
// 级别
|
||||||
String levelStr = resetLevelColor(level);
|
String levelStr = resetLevelColor(level);
|
||||||
|
// 标签
|
||||||
tag = StringUtils.isEmpty(tag) ? "" : STR."\{tag}: ";
|
tag = StringUtils.isEmpty(tag) ? "" : STR."\{tag}: ";
|
||||||
|
// 消息
|
||||||
message = STR."[color=-color-fg-muted]\{StrUtil.format(message, param)}[/color]";
|
message = STR."[color=-color-fg-muted]\{StrUtil.format(message, param)}[/color]";
|
||||||
|
|
||||||
Node text;
|
// 拼接后输出
|
||||||
String input = STR."\{time} \{levelStr} \{tag}\{message}";
|
String input = STR."\{time} \{levelStr} - \{tag}\{message}";
|
||||||
|
|
||||||
if (input.contains("\n")) {
|
print(input);
|
||||||
text = BBCodeParser.createLayout(input);
|
}
|
||||||
} else {
|
|
||||||
text = BBCodeParser.createFormattedText(input);
|
private static void print(String message) {
|
||||||
|
|
||||||
|
// 标记链接
|
||||||
|
String regex = STR.".*(\{AListManager.scheme()}|\{URL_IP_REX}).*";
|
||||||
|
if (ReUtil.isMatch(regex, message)) {
|
||||||
|
String text = ReUtil.get(regex, message, 1);
|
||||||
|
String url = text.startsWith("http") ? STR."http://\{text}" : text;
|
||||||
|
url = url.replace("0.0.0.0", "127.0.0.1");
|
||||||
|
message = message.replace(text, STR."[url=\{url}]\{text}[/url]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextFlow text = BBCodeParser.createFormattedText(STR."\{message}");
|
||||||
|
// 处理链接
|
||||||
|
setLink(text);
|
||||||
|
|
||||||
Platform.runLater(() -> log.textArea.getChildren().add(text));
|
Platform.runLater(() -> log.textArea.getChildren().add(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================{ 私有方法 }================================
|
//==========================================={ 私有方法 }===================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将密码标记为link,一起处理点击事件
|
||||||
|
*
|
||||||
|
* @param msg 输出信息
|
||||||
|
* @return 处理后的信息
|
||||||
|
*/
|
||||||
|
private static String setPwdText(String msg) {
|
||||||
|
if (!ReUtil.isMatch(AListManager.PASSWORD_MSG_REG, msg)) return msg;
|
||||||
|
|
||||||
|
String password = ReUtil.get(AListManager.PASSWORD_MSG_REG, msg, 2);
|
||||||
|
AListManager.tmpPassword(password);
|
||||||
|
return msg.replace(password, STR."[url=\{password}]\{password}[/url]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理文本流中的链接
|
||||||
|
*
|
||||||
|
* @param text 文本流
|
||||||
|
*/
|
||||||
|
private static void setLink(TextFlow text) {
|
||||||
|
|
||||||
|
text.getChildren().forEach(child -> {
|
||||||
|
switch (child) {
|
||||||
|
case Hyperlink link -> link.setOnAction(_ -> {
|
||||||
|
String linkText = link.getUserData().toString();
|
||||||
|
if (ReUtil.isMatch(URL_IP_REX, linkText)) {
|
||||||
|
Context.getApplication().getHostServices().showDocument(linkText);
|
||||||
|
} else {
|
||||||
|
ClipboardUtil.setStr(linkText);
|
||||||
|
var pop = new Popover(new Text(Context.getLanguageBinding("msg.alist.pwd.copy").get()));
|
||||||
|
pop.show(link);
|
||||||
|
}
|
||||||
|
link.setVisited(false);
|
||||||
|
});
|
||||||
|
case TextFlow flow -> setLink(flow);
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 控制台输出颜色
|
* 控制台输出颜色
|
||||||
@ -117,16 +191,13 @@ public class ConsoleLog {
|
|||||||
* @return bbcode 颜色文本
|
* @return bbcode 颜色文本
|
||||||
*/
|
*/
|
||||||
private static String resetConsoleColor(String msg) {
|
private static String resetConsoleColor(String msg) {
|
||||||
try {
|
if (!msg.contains("\033[")) return msg;
|
||||||
String colorCode = ReUtil.get(CONSOLE_COLOR_PREFIX, msg, 1);
|
|
||||||
String color = StringUtils.lowerCase(Color.valueOf(Integer.parseInt(colorCode)).getColor());
|
String colorCode = ReUtil.get(CONSOLE_MSG_REX, msg, 1);
|
||||||
String colorMsg = ReUtil.get(CONSOLE_COLOR_PREFIX, msg, 2);
|
String color = StringUtils.lowerCase(Color.valueOf(Integer.parseInt(colorCode)).getColor());
|
||||||
msg = ReUtil.get(CONSOLE_COLOR_PREFIX, msg, 3);
|
String colorMsg = ReUtil.get(CONSOLE_MSG_REX, msg, 2);
|
||||||
return color(color, colorMsg) + msg;
|
msg = ReUtil.get(CONSOLE_MSG_REX, msg, 3);
|
||||||
} catch (Throwable e) {
|
return color(color, colorMsg) + msg;
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,8 +220,8 @@ public class ConsoleLog {
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public enum Level {
|
public enum Level {
|
||||||
INFO("INFO", null),
|
INFO("INFO", null),
|
||||||
WARN("WARN", "color=-color-chart-2"),
|
WARN("WARN", "-color-danger-emphasis"),
|
||||||
ERROR("ERROR", "color=-color-chart-3"),
|
ERROR("ERROR", "-color-danger-fg"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package cn.octopusyan.alistgui.manager.http;
|
package cn.octopusyan.alistgui.manager.http;
|
||||||
|
|
||||||
import cn.octopusyan.alistgui.enums.ProxySetup;
|
import cn.octopusyan.alistgui.enums.ProxySetup;
|
||||||
|
import cn.octopusyan.alistgui.manager.http.handler.BodyHandler;
|
||||||
import cn.octopusyan.alistgui.model.ProxyInfo;
|
import cn.octopusyan.alistgui.model.ProxyInfo;
|
||||||
import cn.octopusyan.alistgui.util.JsonUtil;
|
import cn.octopusyan.alistgui.util.JsonUtil;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ProxySelector;
|
import java.net.ProxySelector;
|
||||||
@ -15,15 +18,19 @@ import java.net.http.HttpClient;
|
|||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网络请求封装
|
* 网络请求封装
|
||||||
*
|
*
|
||||||
* @author octopus_yan@foxmail.com
|
* @author octopus_yan@foxmail.com
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class HttpUtil {
|
public class HttpUtil {
|
||||||
private volatile static HttpUtil util;
|
private volatile static HttpUtil util;
|
||||||
@Getter
|
@Getter
|
||||||
@ -107,6 +114,30 @@ public class HttpUtil {
|
|||||||
return response.body();
|
return response.body();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void download(String url, String savePath, BiConsumer<Long, Long> listener) throws IOException, InterruptedException {
|
||||||
|
HttpRequest request = getRequest(url, null).build();
|
||||||
|
// 检查bin目录
|
||||||
|
File binDir = new File(savePath);
|
||||||
|
if (!binDir.exists()) {
|
||||||
|
log.debug(STR."dir [\{savePath}] not exists");
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
binDir.mkdirs();
|
||||||
|
log.debug(STR."created dir [\{savePath}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载处理器
|
||||||
|
var handler = BodyHandler.create(
|
||||||
|
Path.of(savePath),
|
||||||
|
StandardOpenOption.CREATE, StandardOpenOption.WRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
// 下载监听
|
||||||
|
if (listener != null)
|
||||||
|
handler.listener(listener);
|
||||||
|
|
||||||
|
HttpResponse<Path> response = httpClient.send(request, handler);
|
||||||
|
}
|
||||||
|
|
||||||
private HttpRequest.Builder getRequest(String uri, JsonNode header) {
|
private HttpRequest.Builder getRequest(String uri, JsonNode header) {
|
||||||
HttpRequest.Builder request = HttpRequest.newBuilder();
|
HttpRequest.Builder request = HttpRequest.newBuilder();
|
||||||
// 请求地址
|
// 请求地址
|
||||||
@ -141,7 +172,7 @@ public class HttpUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!formParams.isEmpty()) {
|
if (!formParams.isEmpty()) {
|
||||||
formParams = new StringBuilder("?" + formParams.substring(1));
|
formParams = new StringBuilder(STR."?\{formParams.substring(1)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return formParams.toString();
|
return formParams.toString();
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
package cn.octopusyan.alistgui.manager.http.handler;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.file.OpenOption;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.Flow;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载处理
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BodyHandler implements HttpResponse.BodyHandler<Path> {
|
||||||
|
private final HttpResponse.BodyHandler<Path> handler;
|
||||||
|
private BiConsumer<Long, Long> consumer;
|
||||||
|
|
||||||
|
private BodyHandler(HttpResponse.BodyHandler<Path> handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BodyHandler create(Path directory, OpenOption... openOptions) {
|
||||||
|
return new BodyHandler(HttpResponse.BodyHandlers.ofFileDownload(directory, openOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponse.BodySubscriber<Path> apply(HttpResponse.ResponseInfo responseInfo) {
|
||||||
|
AtomicLong length = new AtomicLong(-1);
|
||||||
|
// 获取文件大小
|
||||||
|
Optional<String> string = responseInfo.headers().firstValue("content-length");
|
||||||
|
string.ifPresentOrElse(s -> {
|
||||||
|
length.set(Long.parseLong(s));
|
||||||
|
log.debug(STR."========={content-length = \{s}}=========");
|
||||||
|
}, () -> {
|
||||||
|
String msg = "response not has header [content-length]";
|
||||||
|
log.error(msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
BodySubscriber subscriber = new BodySubscriber(handler.apply(responseInfo));
|
||||||
|
subscriber.setConsumer(progress -> consumer.accept(length.get(), progress));
|
||||||
|
|
||||||
|
return subscriber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listener(BiConsumer<Long, Long> consumer) {
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BodySubscriber implements HttpResponse.BodySubscriber<Path> {
|
||||||
|
private final HttpResponse.BodySubscriber<Path> subscriber;
|
||||||
|
private final AtomicLong progress = new AtomicLong(0);
|
||||||
|
@Setter
|
||||||
|
private Consumer<Long> consumer;
|
||||||
|
|
||||||
|
public BodySubscriber(HttpResponse.BodySubscriber<Path> subscriber) {
|
||||||
|
this.subscriber = subscriber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletionStage<Path> getBody() {
|
||||||
|
return subscriber.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Flow.Subscription subscription) {
|
||||||
|
subscriber.onSubscribe(subscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(List<ByteBuffer> item) {
|
||||||
|
subscriber.onNext(item);
|
||||||
|
|
||||||
|
// 记录进度
|
||||||
|
for (ByteBuffer byteBuffer : item) {
|
||||||
|
progress.addAndGet(byteBuffer.limit());
|
||||||
|
}
|
||||||
|
consumer.accept(progress.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable throwable) {
|
||||||
|
subscriber.onError(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
subscriber.onComplete();
|
||||||
|
|
||||||
|
consumer.accept(progress.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
207
src/main/java/cn/octopusyan/alistgui/model/AListConfig.java
Normal file
207
src/main/java/cn/octopusyan/alistgui/model/AListConfig.java
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package cn.octopusyan.alistgui.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* alist 配置文件 model
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class AListConfig {
|
||||||
|
|
||||||
|
@JsonProperty("force")
|
||||||
|
private Boolean force;
|
||||||
|
@JsonProperty("site_url")
|
||||||
|
private String siteUrl;
|
||||||
|
@JsonProperty("cdn")
|
||||||
|
private String cdn;
|
||||||
|
@JsonProperty("jwt_secret")
|
||||||
|
private String jwtSecret;
|
||||||
|
@JsonProperty("token_expires_in")
|
||||||
|
private Integer tokenExpiresIn;
|
||||||
|
@JsonProperty("database")
|
||||||
|
private Database database;
|
||||||
|
@JsonProperty("meilisearch")
|
||||||
|
private MeiliSearch meilisearch;
|
||||||
|
@JsonProperty("scheme")
|
||||||
|
private Scheme scheme;
|
||||||
|
@JsonProperty("temp_dir")
|
||||||
|
private String tempDir;
|
||||||
|
@JsonProperty("bleve_dir")
|
||||||
|
private String bleveDir;
|
||||||
|
@JsonProperty("dist_dir")
|
||||||
|
private String distDir;
|
||||||
|
@JsonProperty("log")
|
||||||
|
private Log log;
|
||||||
|
@JsonProperty("delayed_start")
|
||||||
|
private Integer delayedStart;
|
||||||
|
@JsonProperty("max_connections")
|
||||||
|
private Integer maxConnections;
|
||||||
|
@JsonProperty("tls_insecure_skip_verify")
|
||||||
|
private Boolean tlsInsecureSkipVerify;
|
||||||
|
@JsonProperty("tasks")
|
||||||
|
private Tasks tasks;
|
||||||
|
@JsonProperty("cors")
|
||||||
|
private Cors cors;
|
||||||
|
@JsonProperty("s3")
|
||||||
|
private S3 s3;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Database {
|
||||||
|
@JsonProperty("type")
|
||||||
|
private String type;
|
||||||
|
@JsonProperty("host")
|
||||||
|
private String host;
|
||||||
|
@JsonProperty("port")
|
||||||
|
private Integer port;
|
||||||
|
@JsonProperty("user")
|
||||||
|
private String user;
|
||||||
|
@JsonProperty("password")
|
||||||
|
private String password;
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("db_file")
|
||||||
|
private String dbFile;
|
||||||
|
@JsonProperty("table_prefix")
|
||||||
|
private String tablePrefix;
|
||||||
|
@JsonProperty("ssl_mode")
|
||||||
|
private String sslMode;
|
||||||
|
@JsonProperty("dsn")
|
||||||
|
private String dsn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class MeiliSearch {
|
||||||
|
@JsonProperty("host")
|
||||||
|
private String host;
|
||||||
|
@JsonProperty("api_key")
|
||||||
|
private String apiKey;
|
||||||
|
@JsonProperty("index_prefix")
|
||||||
|
private String indexPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Scheme {
|
||||||
|
@JsonProperty("address")
|
||||||
|
private String address;
|
||||||
|
@JsonProperty("http_port")
|
||||||
|
private Integer httpPort;
|
||||||
|
@JsonProperty("https_port")
|
||||||
|
private Integer httpsPort;
|
||||||
|
@JsonProperty("force_https")
|
||||||
|
private Boolean forceHttps;
|
||||||
|
@JsonProperty("cert_file")
|
||||||
|
private String certFile;
|
||||||
|
@JsonProperty("key_file")
|
||||||
|
private String keyFile;
|
||||||
|
@JsonProperty("unix_file")
|
||||||
|
private String unixFile;
|
||||||
|
@JsonProperty("unix_file_perm")
|
||||||
|
private String unixFilePerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Log {
|
||||||
|
@JsonProperty("enable")
|
||||||
|
private Boolean enable;
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("max_size")
|
||||||
|
private Integer maxSize;
|
||||||
|
@JsonProperty("max_backups")
|
||||||
|
private Integer maxBackups;
|
||||||
|
@JsonProperty("max_age")
|
||||||
|
private Integer maxAge;
|
||||||
|
@JsonProperty("compress")
|
||||||
|
private Boolean compress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Tasks {
|
||||||
|
@JsonProperty("download")
|
||||||
|
private Download download;
|
||||||
|
@JsonProperty("transfer")
|
||||||
|
private Transfer transfer;
|
||||||
|
@JsonProperty("upload")
|
||||||
|
private Upload upload;
|
||||||
|
@JsonProperty("copy")
|
||||||
|
private Copy copy;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Download {
|
||||||
|
@JsonProperty("workers")
|
||||||
|
private Integer workers;
|
||||||
|
@JsonProperty("max_retry")
|
||||||
|
private Integer maxRetry;
|
||||||
|
@JsonProperty("task_persistant")
|
||||||
|
private Boolean taskPersistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Transfer {
|
||||||
|
@JsonProperty("workers")
|
||||||
|
private Integer workers;
|
||||||
|
@JsonProperty("max_retry")
|
||||||
|
private Integer maxRetry;
|
||||||
|
@JsonProperty("task_persistant")
|
||||||
|
private Boolean taskPersistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Upload {
|
||||||
|
@JsonProperty("workers")
|
||||||
|
private Integer workers;
|
||||||
|
@JsonProperty("max_retry")
|
||||||
|
private Integer maxRetry;
|
||||||
|
@JsonProperty("task_persistant")
|
||||||
|
private Boolean taskPersistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Copy {
|
||||||
|
@JsonProperty("workers")
|
||||||
|
private Integer workers;
|
||||||
|
@JsonProperty("max_retry")
|
||||||
|
private Integer maxRetry;
|
||||||
|
@JsonProperty("task_persistant")
|
||||||
|
private Boolean taskPersistant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class Cors {
|
||||||
|
@JsonProperty("allow_origins")
|
||||||
|
private List<String> allowOrigins;
|
||||||
|
@JsonProperty("allow_methods")
|
||||||
|
private List<String> allowMethods;
|
||||||
|
@JsonProperty("allow_headers")
|
||||||
|
private List<String> allowHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class S3 {
|
||||||
|
@JsonProperty("enable")
|
||||||
|
private Boolean enable;
|
||||||
|
@JsonProperty("port")
|
||||||
|
private Integer port;
|
||||||
|
@JsonProperty("ssl")
|
||||||
|
private Boolean ssl;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package cn.octopusyan.alistgui.model.upgrade;
|
package cn.octopusyan.alistgui.model.upgrade;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,5 +16,17 @@ public class AList implements UpgradeApp {
|
|||||||
private final String repo = "alist";
|
private final String repo = "alist";
|
||||||
|
|
||||||
private String releaseFile = "alist-windows-amd64.zip";
|
private String releaseFile = "alist-windows-amd64.zip";
|
||||||
private String version = "unknown";
|
private StringProperty version = new SimpleStringProperty("unknown");
|
||||||
|
|
||||||
|
public StringProperty versionProperty() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version.set(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package cn.octopusyan.alistgui.task;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseTask;
|
||||||
|
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
||||||
|
import cn.octopusyan.alistgui.model.upgrade.UpgradeApp;
|
||||||
|
import cn.octopusyan.alistgui.util.JsonUtil;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查更新任务
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
public class CheckUpdateTask extends BaseTask {
|
||||||
|
private final UpgradeApp app;
|
||||||
|
|
||||||
|
public CheckUpdateTask(UpgradeApp app) {
|
||||||
|
super(STR."check update \{app.getRepo()}");
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void task() throws Exception {
|
||||||
|
String responseStr = HttpUtil.getInstance().get(app.getReleaseApi(), null, null);
|
||||||
|
JsonNode response = JsonUtil.parseJsonObject(responseStr);
|
||||||
|
|
||||||
|
// TODO 校验返回内容
|
||||||
|
String newVersion = response.get("tag_name").asText();
|
||||||
|
|
||||||
|
if (listener != null && listener instanceof UpgradeListener lis)
|
||||||
|
lis.onChecked(!StringUtils.equals(app.getVersion(), newVersion), newVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface UpgradeListener extends BaseTask.Listener {
|
||||||
|
@Override
|
||||||
|
default void onSucceeded() {
|
||||||
|
// do nothing ...
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChecked(boolean hasUpgrade, String version);
|
||||||
|
}
|
||||||
|
}
|
@ -3,26 +3,8 @@ package cn.octopusyan.alistgui.task;
|
|||||||
import cn.octopusyan.alistgui.base.BaseTask;
|
import cn.octopusyan.alistgui.base.BaseTask;
|
||||||
import cn.octopusyan.alistgui.config.Constants;
|
import cn.octopusyan.alistgui.config.Constants;
|
||||||
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.file.OpenOption;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletionStage;
|
|
||||||
import java.util.concurrent.Flow;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO 下载任务
|
* TODO 下载任务
|
||||||
*
|
*
|
||||||
@ -33,126 +15,24 @@ public class DownloadTask extends BaseTask {
|
|||||||
private final String downloadUrl;
|
private final String downloadUrl;
|
||||||
|
|
||||||
public DownloadTask(String downloadUrl) {
|
public DownloadTask(String downloadUrl) {
|
||||||
|
super(STR."Download \{downloadUrl}");
|
||||||
this.downloadUrl = downloadUrl;
|
this.downloadUrl = downloadUrl;
|
||||||
log.info(STR."downlaod url : \{downloadUrl}");
|
}
|
||||||
|
|
||||||
|
public void onListen(DownloadListener listener) {
|
||||||
|
super.onListen(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void task() throws Exception {
|
protected void task() throws Exception {
|
||||||
HttpClient client = HttpUtil.getInstance().getHttpClient();
|
HttpUtil.getInstance().download(
|
||||||
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(downloadUrl)).build();
|
downloadUrl,
|
||||||
|
Constants.BIN_DIR_PATH,
|
||||||
// 检查bin目录
|
listener instanceof DownloadListener ? ((DownloadListener) listener)::onProgress : null
|
||||||
File binDir = new File(Constants.BIN_DIR_PATH);
|
|
||||||
if (!binDir.exists()) {
|
|
||||||
log.debug(STR."dir [\{Constants.BIN_DIR_PATH}] not exists");
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
binDir.mkdirs();
|
|
||||||
log.debug(STR."created dir [\{Constants.BIN_DIR_PATH}]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载处理器
|
|
||||||
var handler = BodyHandler.create(
|
|
||||||
Path.of(Constants.BIN_DIR_PATH),
|
|
||||||
StandardOpenOption.CREATE, StandardOpenOption.WRITE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// 下载监听
|
|
||||||
handler.listener((total, progress) -> {
|
|
||||||
// 输出进度
|
|
||||||
if (listener != null && listener instanceof Listener lis)
|
|
||||||
lis.onProgress(total, progress);
|
|
||||||
});
|
|
||||||
|
|
||||||
log.info("download start");
|
|
||||||
HttpResponse<Path> response = client.send(request, handler);
|
|
||||||
log.info("download success");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Listener extends BaseTask.Listener {
|
public interface DownloadListener extends BaseTask.Listener {
|
||||||
void onProgress(Long total, Long progress);
|
void onProgress(Long total, Long progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义下载返回处理
|
|
||||||
*/
|
|
||||||
private static class BodyHandler implements HttpResponse.BodyHandler<Path> {
|
|
||||||
private final HttpResponse.BodyHandler<Path> handler;
|
|
||||||
private BiConsumer<Long, Long> consumer;
|
|
||||||
|
|
||||||
private BodyHandler(HttpResponse.BodyHandler<Path> handler) {
|
|
||||||
this.handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BodyHandler create(Path directory, OpenOption... openOptions) {
|
|
||||||
return new BodyHandler(HttpResponse.BodyHandlers.ofFileDownload(directory, openOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpResponse.BodySubscriber<Path> apply(HttpResponse.ResponseInfo responseInfo) {
|
|
||||||
AtomicLong length = new AtomicLong(-1);
|
|
||||||
// 获取文件大小
|
|
||||||
Optional<String> string = responseInfo.headers().firstValue("content-length");
|
|
||||||
string.ifPresentOrElse(s -> {
|
|
||||||
length.set(Long.parseLong(s));
|
|
||||||
log.debug(STR."========={content-length = \{s}}=========");
|
|
||||||
}, () -> {
|
|
||||||
String msg = "response not has header [content-length]";
|
|
||||||
log.error(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
BodySubscriber subscriber = new BodySubscriber(handler.apply(responseInfo));
|
|
||||||
subscriber.setConsumer(progress -> consumer.accept(length.get(), progress));
|
|
||||||
|
|
||||||
return subscriber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void listener(BiConsumer<Long, Long> consumer) {
|
|
||||||
this.consumer = consumer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class BodySubscriber implements HttpResponse.BodySubscriber<Path> {
|
|
||||||
private final HttpResponse.BodySubscriber<Path> subscriber;
|
|
||||||
private final AtomicLong progress = new AtomicLong(0);
|
|
||||||
@Setter
|
|
||||||
private Consumer<Long> consumer;
|
|
||||||
|
|
||||||
public BodySubscriber(HttpResponse.BodySubscriber<Path> subscriber) {
|
|
||||||
this.subscriber = subscriber;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletionStage<Path> getBody() {
|
|
||||||
return subscriber.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(Flow.Subscription subscription) {
|
|
||||||
subscriber.onSubscribe(subscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNext(List<ByteBuffer> item) {
|
|
||||||
subscriber.onNext(item);
|
|
||||||
|
|
||||||
// 记录进度
|
|
||||||
for (ByteBuffer byteBuffer : item) {
|
|
||||||
progress.addAndGet(byteBuffer.limit());
|
|
||||||
}
|
|
||||||
consumer.accept(progress.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable throwable) {
|
|
||||||
subscriber.onError(throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onComplete() {
|
|
||||||
subscriber.onComplete();
|
|
||||||
|
|
||||||
consumer.accept(progress.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ public class ProxyCheckTask extends BaseTask {
|
|||||||
private final String checkUrl;
|
private final String checkUrl;
|
||||||
|
|
||||||
public ProxyCheckTask(String checkUrl) {
|
public ProxyCheckTask(String checkUrl) {
|
||||||
|
super(STR."ProxyCheck[\{checkUrl}]");
|
||||||
this.checkUrl = checkUrl;
|
this.checkUrl = checkUrl;
|
||||||
this.updateProgress(0d, 1d);
|
this.updateProgress(0d, 1d);
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package cn.octopusyan.alistgui.task;
|
|
||||||
|
|
||||||
import cn.octopusyan.alistgui.base.BaseTask;
|
|
||||||
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
|
||||||
import cn.octopusyan.alistgui.model.upgrade.AList;
|
|
||||||
import cn.octopusyan.alistgui.model.upgrade.UpgradeApp;
|
|
||||||
import cn.octopusyan.alistgui.util.JsonUtil;
|
|
||||||
import cn.octopusyan.alistgui.viewModel.AboutViewModule;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查更新任务
|
|
||||||
*
|
|
||||||
* @author octopus_yan
|
|
||||||
*/
|
|
||||||
public class UpgradeTask extends BaseTask {
|
|
||||||
private final AboutViewModule viewModule;
|
|
||||||
private final UpgradeApp app;
|
|
||||||
|
|
||||||
public UpgradeTask(AboutViewModule viewModel, UpgradeApp app) {
|
|
||||||
this.viewModule = viewModel;
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void task() throws Exception {
|
|
||||||
String responseStr = HttpUtil.getInstance().get(app.getReleaseApi(), null, null);
|
|
||||||
JsonNode response = JsonUtil.parseJsonObject(responseStr);
|
|
||||||
|
|
||||||
// TODO 校验返回内容
|
|
||||||
String newVersion = response.get("tag_name").asText();
|
|
||||||
|
|
||||||
runLater(() -> {
|
|
||||||
if (app instanceof AList) {
|
|
||||||
viewModule.aListUpgradeProperty().setValue(!StringUtils.equals(app.getVersion(), newVersion));
|
|
||||||
viewModule.aListNewVersionProperty().setValue(newVersion);
|
|
||||||
} else {
|
|
||||||
viewModule.guiUpgradeProperty().setValue(!StringUtils.equals(app.getVersion(), newVersion));
|
|
||||||
viewModule.guiNewVersionProperty().setValue(newVersion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,102 @@
|
|||||||
|
package cn.octopusyan.alistgui.task.listener;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseTask;
|
||||||
|
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
||||||
|
import cn.octopusyan.alistgui.task.CheckUpdateTask;
|
||||||
|
import cn.octopusyan.alistgui.task.DownloadTask;
|
||||||
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
|
import cn.octopusyan.alistgui.view.alert.builder.ProgressBuilder;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务监听器默认实现
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public abstract class TaskListener implements BaseTask.Listener {
|
||||||
|
private final BaseTask task;
|
||||||
|
// 加载弹窗
|
||||||
|
final ProgressBuilder progress = AlertUtil.progress();
|
||||||
|
|
||||||
|
public TaskListener(BaseTask task) {
|
||||||
|
this.task = task;
|
||||||
|
progress.onCancel(task::cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
log.info(STR."\{task.getName()} start ...");
|
||||||
|
ConsoleLog.info(task.getName(), "start ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRunning() {
|
||||||
|
progress.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled() {
|
||||||
|
progress.close();
|
||||||
|
log.info(STR."\{task.getName()} cancel ...");
|
||||||
|
ConsoleLog.info(task.getName(), "cancel ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailed(Throwable throwable) {
|
||||||
|
progress.close();
|
||||||
|
log.error(STR."\{task.getName()} fail ...", throwable);
|
||||||
|
ConsoleLog.error(task.getName(), STR."fail : \{throwable.getMessage()}");
|
||||||
|
onFail(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSucceeded() {
|
||||||
|
progress.close();
|
||||||
|
log.info(STR."\{task.getName()} success ...");
|
||||||
|
ConsoleLog.info(task.getName(), "success ...");
|
||||||
|
onSucceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void onSucceed();
|
||||||
|
|
||||||
|
protected void onFail(Throwable throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载任务监听默认实现
|
||||||
|
*/
|
||||||
|
public static abstract class DownloadListener extends TaskListener implements DownloadTask.DownloadListener {
|
||||||
|
|
||||||
|
private volatile int lastProgress = 0;
|
||||||
|
|
||||||
|
public DownloadListener(BaseTask task) {
|
||||||
|
super(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgress(Long total, Long progress) {
|
||||||
|
int a = (int) (((double) progress / total) * 100);
|
||||||
|
if (a % 10 == 0) {
|
||||||
|
if (a != lastProgress) {
|
||||||
|
lastProgress = a;
|
||||||
|
ConsoleLog.info(STR."\{lastProgress} %");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查更新监听默认实现
|
||||||
|
*/
|
||||||
|
public static abstract class UpgradeUpgradeListener extends TaskListener implements CheckUpdateTask.UpgradeListener {
|
||||||
|
public UpgradeUpgradeListener(BaseTask task) {
|
||||||
|
super(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSucceed() {
|
||||||
|
// do nothing ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
src/main/java/cn/octopusyan/alistgui/util/DownloadUtil.java
Normal file
84
src/main/java/cn/octopusyan/alistgui/util/DownloadUtil.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package cn.octopusyan.alistgui.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.core.util.ZipUtil;
|
||||||
|
import cn.octopusyan.alistgui.config.Constants;
|
||||||
|
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
||||||
|
import cn.octopusyan.alistgui.model.upgrade.AList;
|
||||||
|
import cn.octopusyan.alistgui.model.upgrade.UpgradeApp;
|
||||||
|
import cn.octopusyan.alistgui.task.DownloadTask;
|
||||||
|
import cn.octopusyan.alistgui.task.listener.TaskListener;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载工具
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DownloadUtil {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
*
|
||||||
|
* @param app 应用
|
||||||
|
* @param version 下载版本
|
||||||
|
*/
|
||||||
|
public static DownloadTask startDownload(UpgradeApp app, String version, Runnable runnable) {
|
||||||
|
var task = new DownloadTask(app.getDownloadUrl(version));
|
||||||
|
task.onListen(new TaskListener.DownloadListener(task) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRunning() {
|
||||||
|
// 不展示进度条
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSucceed() {
|
||||||
|
String msg = STR."download \{app.getRepo()} success";
|
||||||
|
log.info(msg);
|
||||||
|
ConsoleLog.info(msg);
|
||||||
|
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unzip(UpgradeApp app) {
|
||||||
|
String parentPath = app instanceof AList ? Constants.BIN_DIR_PATH : Constants.DATA_DIR_PATH;
|
||||||
|
|
||||||
|
File file = new File(parentPath + File.separator + app.getReleaseFile());
|
||||||
|
ZipFile zipFile = ZipUtil.toZipFile(file, CharsetUtil.defaultCharset());
|
||||||
|
ZipUtil.read(zipFile, zipEntry -> {
|
||||||
|
String path = zipEntry.getName();
|
||||||
|
if (FileUtil.isWindows()) {
|
||||||
|
// Win系统下
|
||||||
|
path = StrUtil.replace(path, "*", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
final File outItemFile = FileUtil.file(parentPath, path);
|
||||||
|
if (zipEntry.isDirectory()) {
|
||||||
|
// 目录
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
outItemFile.mkdirs();
|
||||||
|
} else {
|
||||||
|
InputStream in = ZipUtil.getStream(zipFile, zipEntry);
|
||||||
|
// 文件
|
||||||
|
FileUtil.writeFromStream(in, outItemFile, false);
|
||||||
|
|
||||||
|
log.info(STR."unzip ==> \{outItemFile.getAbsoluteFile()}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 解压完成后删除
|
||||||
|
FileUtil.del(file);
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.apache.commons.exec.*;
|
import org.apache.commons.exec.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -16,20 +15,12 @@ import java.util.Set;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ProcessesUtil {
|
public class ProcessesUtil {
|
||||||
private static final String NEW_LINE = System.lineSeparator();
|
private static final String NEW_LINE = System.lineSeparator();
|
||||||
|
private static final int EXIT_VALUE = 1;
|
||||||
|
|
||||||
private final Executor executor = DefaultExecutor.builder().get();
|
private final Executor executor = DefaultExecutor.builder().get();
|
||||||
private final ShutdownHookProcessDestroyer processDestroyer;
|
private final ShutdownHookProcessDestroyer processDestroyer;
|
||||||
private DefaultExecuteResultHandler handler;
|
|
||||||
private final PumpStreamHandler streamHandler;
|
|
||||||
private OnExecuteListener listener;
|
private OnExecuteListener listener;
|
||||||
private CommandLine commandLine;
|
private CommandLine commandLine;
|
||||||
private final LogOutputStream logout = new LogOutputStream() {
|
|
||||||
@Override
|
|
||||||
protected void processLine(String line, int logLevel) {
|
|
||||||
if (listener != null)
|
|
||||||
listener.onExecute(line + NEW_LINE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final Set<ProcessesUtil> set = new HashSet<>();
|
private static final Set<ProcessesUtil> set = new HashSet<>();
|
||||||
|
|
||||||
@ -37,10 +28,17 @@ public class ProcessesUtil {
|
|||||||
* Prevent construction.
|
* Prevent construction.
|
||||||
*/
|
*/
|
||||||
private ProcessesUtil(String workingDirectory) {
|
private ProcessesUtil(String workingDirectory) {
|
||||||
streamHandler = new PumpStreamHandler(logout, logout);
|
LogOutputStream logout = new LogOutputStream() {
|
||||||
|
@Override
|
||||||
|
protected void processLine(String line, int logLevel) {
|
||||||
|
if (listener != null)
|
||||||
|
listener.onExecute(line + NEW_LINE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PumpStreamHandler streamHandler = new PumpStreamHandler(logout, logout);
|
||||||
executor.setStreamHandler(streamHandler);
|
executor.setStreamHandler(streamHandler);
|
||||||
executor.setWorkingDirectory(new File(workingDirectory));
|
executor.setWorkingDirectory(new File(workingDirectory));
|
||||||
executor.setExitValue(1);
|
executor.setExitValue(EXIT_VALUE);
|
||||||
processDestroyer = new ShutdownHookProcessDestroyer();
|
processDestroyer = new ShutdownHookProcessDestroyer();
|
||||||
executor.setProcessDestroyer(processDestroyer);
|
executor.setProcessDestroyer(processDestroyer);
|
||||||
}
|
}
|
||||||
@ -51,20 +49,25 @@ public class ProcessesUtil {
|
|||||||
return util;
|
return util;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exec(String command) throws IOException {
|
public boolean exec(String command) {
|
||||||
commandLine = CommandLine.parse(command);
|
commandLine = CommandLine.parse(command);
|
||||||
int execute = executor.execute(commandLine);
|
int execute = 0;
|
||||||
return 0 == execute;
|
try {
|
||||||
|
execute = executor.execute(commandLine);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("exec", e);
|
||||||
|
}
|
||||||
|
return execute == EXIT_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void exec(String command, OnExecuteListener listener) {
|
public void exec(String command, OnExecuteListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
commandLine = CommandLine.parse(command);
|
commandLine = CommandLine.parse(command);
|
||||||
handler = new DefaultExecuteResultHandler() {
|
DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onProcessComplete(int exitValue) {
|
public void onProcessComplete(int exitValue) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onExecuteSuccess(exitValue);
|
listener.onExecuteSuccess(exitValue == EXIT_VALUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +80,7 @@ public class ProcessesUtil {
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
executor.execute(commandLine, handler);
|
executor.execute(commandLine, handler);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
if (listener != null) listener.onExecuteError(e);
|
if (listener != null) listener.onExecuteError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +90,10 @@ public class ProcessesUtil {
|
|||||||
processDestroyer.run();
|
processDestroyer.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return !processDestroyer.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public static void destroyAll() {
|
public static void destroyAll() {
|
||||||
set.forEach(ProcessesUtil::destroy);
|
set.forEach(ProcessesUtil::destroy);
|
||||||
}
|
}
|
||||||
@ -94,7 +101,7 @@ public class ProcessesUtil {
|
|||||||
public interface OnExecuteListener {
|
public interface OnExecuteListener {
|
||||||
void onExecute(String msg);
|
void onExecute(String msg);
|
||||||
|
|
||||||
default void onExecuteSuccess(int exitValue) {
|
default void onExecuteSuccess(boolean success) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void onExecuteError(Exception e) {
|
default void onExecuteError(Exception e) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package cn.octopusyan.alistgui.manager;
|
package cn.octopusyan.alistgui.util;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.Application;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -25,7 +25,11 @@ public class WindowsUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void bindDragged(Pane pane) {
|
public static void bindDragged(Pane pane) {
|
||||||
Window stage = getStage(pane);
|
Stage stage = getStage(pane);
|
||||||
|
bindDragged(pane, stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void bindDragged(Pane pane, Stage stage) {
|
||||||
pane.setOnMousePressed(event -> {
|
pane.setOnMousePressed(event -> {
|
||||||
paneXOffset.put(pane, stage.getX() - event.getScreenX());
|
paneXOffset.put(pane, stage.getX() - event.getScreenX());
|
||||||
paneYOffset.put(pane, stage.getY() - event.getScreenY());
|
paneYOffset.put(pane, stage.getY() - event.getScreenY());
|
||||||
@ -37,6 +41,10 @@ public class WindowsUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Stage getStage(Pane pane) {
|
public static Stage getStage(Pane pane) {
|
||||||
return (Stage) pane.getScene().getWindow();
|
try {
|
||||||
|
return (Stage) pane.getScene().getWindow();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return Application.getPrimaryStage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package cn.octopusyan.alistgui.util.alert;
|
package cn.octopusyan.alistgui.view.alert;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.view.alert.builder.*;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@ -17,6 +18,10 @@ public class AlertUtil {
|
|||||||
AlertUtil.mOwner = stage;
|
AlertUtil.mOwner = stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DefaultBuilder builder() {
|
||||||
|
return new DefaultBuilder(mOwner);
|
||||||
|
}
|
||||||
|
|
||||||
public static AlertBuilder info(String content) {
|
public static AlertBuilder info(String content) {
|
||||||
return info().content(content).header(null);
|
return info().content(content).header(null);
|
||||||
}
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package cn.octopusyan.alistgui.util.alert;
|
package cn.octopusyan.alistgui.view.alert.builder;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseBuilder;
|
||||||
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
@ -1,9 +1,9 @@
|
|||||||
package cn.octopusyan.alistgui.util.alert;
|
package cn.octopusyan.alistgui.view.alert.builder;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseBuilder;
|
||||||
import javafx.scene.control.ChoiceDialog;
|
import javafx.scene.control.ChoiceDialog;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
@ -0,0 +1,50 @@
|
|||||||
|
package cn.octopusyan.alistgui.view.alert.builder;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseBuilder;
|
||||||
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
|
import cn.octopusyan.alistgui.util.WindowsUtil;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ButtonBar;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Dialog;
|
||||||
|
import javafx.scene.control.DialogPane;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.stage.StageStyle;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认弹窗
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
public class DefaultBuilder extends BaseBuilder<DefaultBuilder, Dialog<?>> {
|
||||||
|
|
||||||
|
public DefaultBuilder(Window mOwner) {
|
||||||
|
super(new Dialog<>(), mOwner);
|
||||||
|
|
||||||
|
header(null);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dialog.getDialogPane();
|
||||||
|
dialogPane.getScene().setFill(Color.TRANSPARENT);
|
||||||
|
WindowsUtil.bindDragged(dialogPane);
|
||||||
|
WindowsUtil.bindShadow(dialogPane);
|
||||||
|
WindowsUtil.getStage(dialogPane).initStyle(StageStyle.TRANSPARENT);
|
||||||
|
|
||||||
|
dialogPane.getButtonTypes().add(new ButtonType(
|
||||||
|
Context.getLanguageBinding("label.cancel").get(),
|
||||||
|
ButtonType.CANCEL.getButtonData()
|
||||||
|
));
|
||||||
|
|
||||||
|
for (Node child : dialogPane.getChildren()) {
|
||||||
|
if (child instanceof ButtonBar) {
|
||||||
|
dialogPane.getChildren().remove(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultBuilder content(Node content) {
|
||||||
|
dialog.getDialogPane().setContent(content);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,13 @@
|
|||||||
package cn.octopusyan.alistgui.util.alert;
|
package cn.octopusyan.alistgui.view.alert.builder;
|
||||||
|
|
||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
import cn.octopusyan.alistgui.manager.WindowsUtil;
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.ProgressBar;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.stage.StageStyle;
|
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,34 +15,11 @@ import javafx.stage.Window;
|
|||||||
*
|
*
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
public class ProgressBuilder extends BaseBuilder<ProgressBuilder, Dialog<Void>> {
|
public class ProgressBuilder extends DefaultBuilder {
|
||||||
|
|
||||||
public ProgressBuilder(Window mOwner) {
|
public ProgressBuilder(Window mOwner) {
|
||||||
this(new Dialog<>(), mOwner);
|
super(mOwner);
|
||||||
}
|
content(getContent());
|
||||||
|
|
||||||
public ProgressBuilder(Dialog<Void> dialog, Window mOwner) {
|
|
||||||
super(dialog, mOwner);
|
|
||||||
|
|
||||||
DialogPane dialogPane = dialog.getDialogPane();
|
|
||||||
dialogPane.getScene().setFill(Color.TRANSPARENT);
|
|
||||||
WindowsUtil.bindDragged(dialogPane);
|
|
||||||
WindowsUtil.bindShadow(dialogPane);
|
|
||||||
WindowsUtil.getStage(dialogPane).initStyle(StageStyle.TRANSPARENT);
|
|
||||||
var content = getContent();
|
|
||||||
|
|
||||||
dialogPane.setContent(content);
|
|
||||||
dialogPane.getButtonTypes().add(new ButtonType(
|
|
||||||
Context.getLanguageBinding("label.cancel").get(),
|
|
||||||
ButtonType.CANCEL.getButtonData()
|
|
||||||
));
|
|
||||||
|
|
||||||
for (Node child : dialogPane.getChildren()) {
|
|
||||||
if (child instanceof ButtonBar) {
|
|
||||||
dialogPane.getChildren().remove(child);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pane getContent() {
|
private Pane getContent() {
|
@ -1,5 +1,6 @@
|
|||||||
package cn.octopusyan.alistgui.util.alert;
|
package cn.octopusyan.alistgui.view.alert.builder;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseBuilder;
|
||||||
import javafx.scene.control.TextInputDialog;
|
import javafx.scene.control.TextInputDialog;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
|
@ -1,35 +1,27 @@
|
|||||||
package cn.octopusyan.alistgui.viewModel;
|
package cn.octopusyan.alistgui.viewModel;
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.octopusyan.alistgui.base.BaseViewModel;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.hutool.core.util.ZipUtil;
|
|
||||||
import cn.octopusyan.alistgui.base.BaseTask;
|
|
||||||
import cn.octopusyan.alistgui.config.Constants;
|
|
||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
import cn.octopusyan.alistgui.manager.ConfigManager;
|
import cn.octopusyan.alistgui.manager.ConfigManager;
|
||||||
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
import cn.octopusyan.alistgui.manager.ConsoleLog;
|
||||||
import cn.octopusyan.alistgui.model.upgrade.AList;
|
import cn.octopusyan.alistgui.model.upgrade.AList;
|
||||||
import cn.octopusyan.alistgui.model.upgrade.UpgradeApp;
|
import cn.octopusyan.alistgui.model.upgrade.UpgradeApp;
|
||||||
import cn.octopusyan.alistgui.task.DownloadTask;
|
import cn.octopusyan.alistgui.task.CheckUpdateTask;
|
||||||
import cn.octopusyan.alistgui.task.UpgradeTask;
|
import cn.octopusyan.alistgui.task.listener.TaskListener;
|
||||||
import cn.octopusyan.alistgui.util.alert.AlertBuilder;
|
import cn.octopusyan.alistgui.util.DownloadUtil;
|
||||||
import cn.octopusyan.alistgui.util.alert.AlertUtil;
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
|
import cn.octopusyan.alistgui.view.alert.builder.AlertBuilder;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关于
|
* 关于
|
||||||
*
|
*
|
||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AboutViewModule {
|
public class AboutViewModule extends BaseViewModel {
|
||||||
private final StringProperty aListVersion = new SimpleStringProperty(ConfigManager.aListVersion());
|
private final StringProperty aListVersion = new SimpleStringProperty(ConfigManager.aListVersion());
|
||||||
private final StringProperty aListNewVersion = new SimpleStringProperty("");
|
private final StringProperty aListNewVersion = new SimpleStringProperty("");
|
||||||
private final BooleanProperty aListUpgrade = new SimpleBooleanProperty(false);
|
private final BooleanProperty aListUpgrade = new SimpleBooleanProperty(false);
|
||||||
@ -38,7 +30,7 @@ public class AboutViewModule {
|
|||||||
private final BooleanProperty guiUpgrade = new SimpleBooleanProperty(false);
|
private final BooleanProperty guiUpgrade = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
public AboutViewModule() {
|
public AboutViewModule() {
|
||||||
aListVersion.addListener((_, _, newValue) -> ConfigManager.aListVersion(newValue));
|
aListVersion.bindBidirectional(ConfigManager.aListVersionProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Property<String> aListVersionProperty() {
|
public Property<String> aListVersionProperty() {
|
||||||
@ -83,10 +75,12 @@ public class AboutViewModule {
|
|||||||
String newLabel = Context.getLanguageBinding("update.remote").get();
|
String newLabel = Context.getLanguageBinding("update.remote").get();
|
||||||
String header = Context.getLanguageBinding(STR."update.upgrade.\{upgrade ? "new" : "not"}").get();
|
String header = Context.getLanguageBinding(STR."update.upgrade.\{upgrade ? "new" : "not"}").get();
|
||||||
|
|
||||||
|
// 版本检查消息
|
||||||
String msg = STR."\{app.getRepo()}\{upgrade ? "" : STR." \{version}"} \{header} \{upgrade ? newVersion : ""}";
|
String msg = STR."\{app.getRepo()}\{upgrade ? "" : STR." \{version}"} \{header} \{upgrade ? newVersion : ""}";
|
||||||
log.info(msg);
|
log.info(msg);
|
||||||
ConsoleLog.info(msg);
|
ConsoleLog.info(msg);
|
||||||
|
|
||||||
|
// 弹窗
|
||||||
AlertBuilder builder = upgrade ? AlertUtil.confirm() : AlertUtil.info();
|
AlertBuilder builder = upgrade ? AlertUtil.confirm() : AlertUtil.info();
|
||||||
builder.title(title)
|
builder.title(title)
|
||||||
.header(header)
|
.header(header)
|
||||||
@ -95,131 +89,50 @@ public class AboutViewModule {
|
|||||||
\{newLabel} : \{newVersion}
|
\{newLabel} : \{newVersion}
|
||||||
""")
|
""")
|
||||||
.show(() -> {
|
.show(() -> {
|
||||||
if (upgrade) startDownload(app, newVersion);
|
// 可升级,且点击了确定后,开始下载任务
|
||||||
|
if (upgrade)
|
||||||
|
DownloadUtil.startDownload(app, newVersion, () -> {
|
||||||
|
// 下载完成后,解压并删除文件
|
||||||
|
DownloadUtil.unzip(app);
|
||||||
|
// 设置应用版本
|
||||||
|
Platform.runLater(() -> aListVersion.setValue(aListNewVersion.getValue()));
|
||||||
|
}).execute();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startUpgrade(UpgradeApp app, Runnable runnable) {
|
private void startUpgrade(UpgradeApp app, Runnable runnable) {
|
||||||
// 检查更新的任务
|
// 检查更新的任务
|
||||||
var task = new UpgradeTask(this, app);
|
var task = new CheckUpdateTask(app);
|
||||||
|
|
||||||
// 加载弹窗
|
|
||||||
final var progress = AlertUtil.progress();
|
|
||||||
progress.title(Context.getLanguageBinding("proxy.test.title").get());
|
|
||||||
progress.onCancel(task::cancel);
|
|
||||||
|
|
||||||
// 任务监听
|
// 任务监听
|
||||||
task.onListen(new BaseTask.Listener() {
|
task.onListen(new TaskListener.UpgradeUpgradeListener(task) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
protected void onSucceed() {
|
||||||
String msg = STR."start update \{app.getRepo()}...";
|
|
||||||
log.info(msg);
|
|
||||||
ConsoleLog.info(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRunning() {
|
|
||||||
progress.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSucceeded() {
|
|
||||||
progress.close();
|
|
||||||
if (runnable != null) runnable.run();
|
if (runnable != null) runnable.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailed(Throwable throwable) {
|
public void onChecked(boolean hasUpgrade, String version) {
|
||||||
String msg = STR."\{app.getRepo()} check version error";
|
// 版本检查结果
|
||||||
log.error(msg, throwable);
|
Platform.runLater(() -> {
|
||||||
ConsoleLog.error(STR."\{msg} : \{throwable.getMessage()}");
|
if (app instanceof AList) {
|
||||||
|
aListUpgrade.setValue(hasUpgrade);
|
||||||
|
aListNewVersion.setValue(version);
|
||||||
|
} else {
|
||||||
|
guiUpgrade.setValue(hasUpgrade);
|
||||||
|
guiNewVersion.setValue(version);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFail(Throwable throwable) {
|
||||||
AlertUtil.exception(new Exception(throwable)).show();
|
AlertUtil.exception(new Exception(throwable)).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 执行任务
|
// 执行任务
|
||||||
task.execute();
|
task.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 下载文件
|
|
||||||
*
|
|
||||||
* @param app 应用
|
|
||||||
* @param version 下载版本
|
|
||||||
*/
|
|
||||||
private void startDownload(UpgradeApp app, String version) {
|
|
||||||
DownloadTask downloadTask = new DownloadTask(app.getDownloadUrl(version));
|
|
||||||
downloadTask.onListen(new DownloadTask.Listener() {
|
|
||||||
|
|
||||||
private volatile int lastProgress = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
String msg = STR."download \{app.getRepo()} start";
|
|
||||||
log.info(msg);
|
|
||||||
ConsoleLog.info(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgress(Long total, Long progress) {
|
|
||||||
int a = (int) (((double) progress / total) * 100);
|
|
||||||
if (a % 10 == 0) {
|
|
||||||
if (a != lastProgress) {
|
|
||||||
lastProgress = a;
|
|
||||||
String msg = STR."\{app.getRepo()} \{a} %";
|
|
||||||
log.info(STR."download \{msg}");
|
|
||||||
ConsoleLog.info(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSucceeded() {
|
|
||||||
String msg = STR."download \{app.getRepo()} success";
|
|
||||||
log.info(msg);
|
|
||||||
ConsoleLog.info(msg);
|
|
||||||
|
|
||||||
// 解压并删除文件
|
|
||||||
unzip(app);
|
|
||||||
|
|
||||||
Platform.runLater(() -> aListVersion.setValue(aListNewVersion.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailed(Throwable throwable) {
|
|
||||||
log.error("下载失败", throwable);
|
|
||||||
ConsoleLog.error(STR."下载失败 => \{throwable.getMessage()}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
downloadTask.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unzip(UpgradeApp app) {
|
|
||||||
File file = new File(Constants.BIN_DIR_PATH + File.separator + app.getReleaseFile());
|
|
||||||
ZipFile zipFile = ZipUtil.toZipFile(file, CharsetUtil.defaultCharset());
|
|
||||||
ZipUtil.read(zipFile, zipEntry -> {
|
|
||||||
String path = zipEntry.getName();
|
|
||||||
if (FileUtil.isWindows()) {
|
|
||||||
// Win系统下
|
|
||||||
path = StrUtil.replace(path, "*", "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
final File outItemFile = FileUtil.file(Constants.BIN_DIR_PATH, path);
|
|
||||||
if (zipEntry.isDirectory()) {
|
|
||||||
// 目录
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
outItemFile.mkdirs();
|
|
||||||
} else {
|
|
||||||
InputStream in = ZipUtil.getStream(zipFile, zipEntry);
|
|
||||||
// 文件
|
|
||||||
FileUtil.writeFromStream(in, outItemFile, false);
|
|
||||||
|
|
||||||
log.info(STR."unzip ==> \{outItemFile.getAbsoluteFile()}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 解压完成后删除
|
|
||||||
FileUtil.del(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package cn.octopusyan.alistgui.viewModel;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseViewModel;
|
||||||
|
import cn.octopusyan.alistgui.manager.AListManager;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin Panel VM
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
public class AdminPanelViewModel extends BaseViewModel {
|
||||||
|
private final StringProperty password = new SimpleStringProperty(AListManager.passwordProperty().get());
|
||||||
|
|
||||||
|
public AdminPanelViewModel() {
|
||||||
|
AListManager.passwordProperty().subscribe(password::set);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty passwordProperty() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package cn.octopusyan.alistgui.viewModel;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseViewModel;
|
||||||
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
|
import cn.octopusyan.alistgui.manager.AListManager;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主界面VM
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
public class MainViewModel extends BaseViewModel {
|
||||||
|
private final ListProperty<String> startBtnStyleCss = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
private final StringProperty startBtnText = new SimpleStringProperty();
|
||||||
|
private final ListProperty<String> statusLabelStyleCss = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
private final StringProperty statusLabelText = new SimpleStringProperty();
|
||||||
|
private final BooleanProperty browserButtonDisable = new SimpleBooleanProperty();
|
||||||
|
private final BooleanProperty running = new SimpleBooleanProperty();
|
||||||
|
|
||||||
|
public MainViewModel() {
|
||||||
|
running.addListener((_, _, running) -> {
|
||||||
|
resetStatus(running);
|
||||||
|
browserButtonDisable.set(!running);
|
||||||
|
});
|
||||||
|
// 先添加监听再绑定,解决切换locale后,界面状态显示错误的问题
|
||||||
|
running.bind(AListManager.runningProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListProperty<String> startBtnStyleCssProperty() {
|
||||||
|
return startBtnStyleCss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty startBtnTextProperty() {
|
||||||
|
return startBtnText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListProperty<String> statusLabelStyleCssProperty() {
|
||||||
|
return statusLabelStyleCss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty statusLabelTextProperty() {
|
||||||
|
return statusLabelText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty browserButtonDisableProperty() {
|
||||||
|
return browserButtonDisable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetStatus(boolean running) {
|
||||||
|
String removeStyle = running ? "success" : "danger";
|
||||||
|
String addStyle = running ? "danger" : "success";
|
||||||
|
String button = Context.getLanguageBinding(STR."main.control.\{running ? "stop" : "start"}").get();
|
||||||
|
String status = Context.getLanguageBinding(STR."main.status.label-\{running ? "running" : "stop"}").get();
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
startBtnStyleCss.remove(removeStyle);
|
||||||
|
startBtnStyleCss.add(addStyle);
|
||||||
|
startBtnText.set(button);
|
||||||
|
|
||||||
|
statusLabelStyleCss.remove(addStyle);
|
||||||
|
statusLabelStyleCss.add(removeStyle);
|
||||||
|
statusLabelText.set(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package cn.octopusyan.alistgui.viewModel;
|
||||||
|
|
||||||
|
import cn.octopusyan.alistgui.base.BaseViewModel;
|
||||||
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root VM
|
||||||
|
*
|
||||||
|
* @author octopus_yan
|
||||||
|
*/
|
||||||
|
public class RootViewModel extends BaseViewModel {
|
||||||
|
private final IntegerProperty currentViewIndex = new SimpleIntegerProperty(Context.currentViewIndex()) {
|
||||||
|
{
|
||||||
|
Context.currentViewIndexProperty().bind(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public IntegerProperty currentViewIndexProperty() {
|
||||||
|
return currentViewIndex;
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
package cn.octopusyan.alistgui.viewModel;
|
package cn.octopusyan.alistgui.viewModel;
|
||||||
|
|
||||||
import atlantafx.base.theme.Theme;
|
import atlantafx.base.theme.Theme;
|
||||||
import cn.octopusyan.alistgui.base.BaseTask;
|
import cn.octopusyan.alistgui.base.BaseViewModel;
|
||||||
import cn.octopusyan.alistgui.config.Context;
|
import cn.octopusyan.alistgui.config.Context;
|
||||||
import cn.octopusyan.alistgui.enums.ProxySetup;
|
import cn.octopusyan.alistgui.enums.ProxySetup;
|
||||||
import cn.octopusyan.alistgui.manager.ConfigManager;
|
import cn.octopusyan.alistgui.manager.ConfigManager;
|
||||||
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
import cn.octopusyan.alistgui.manager.http.HttpUtil;
|
||||||
import cn.octopusyan.alistgui.task.ProxyCheckTask;
|
import cn.octopusyan.alistgui.task.ProxyCheckTask;
|
||||||
import cn.octopusyan.alistgui.util.alert.AlertUtil;
|
import cn.octopusyan.alistgui.task.listener.TaskListener;
|
||||||
import javafx.application.Platform;
|
import cn.octopusyan.alistgui.view.alert.AlertUtil;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -21,7 +21,7 @@ import java.util.Locale;
|
|||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SetupViewModel {
|
public class SetupViewModel extends BaseViewModel {
|
||||||
private final BooleanProperty autoStart = new SimpleBooleanProperty(ConfigManager.autoStart());
|
private final BooleanProperty autoStart = new SimpleBooleanProperty(ConfigManager.autoStart());
|
||||||
private final BooleanProperty silentStartup = new SimpleBooleanProperty(ConfigManager.silentStartup());
|
private final BooleanProperty silentStartup = new SimpleBooleanProperty(ConfigManager.silentStartup());
|
||||||
private final ObjectProperty<Theme> theme = new SimpleObjectProperty<>(ConfigManager.theme());
|
private final ObjectProperty<Theme> theme = new SimpleObjectProperty<>(ConfigManager.theme());
|
||||||
@ -96,24 +96,15 @@ public class SetupViewModel {
|
|||||||
|
|
||||||
private static ProxyCheckTask getProxyCheckTask(String checkUrl) {
|
private static ProxyCheckTask getProxyCheckTask(String checkUrl) {
|
||||||
var task = new ProxyCheckTask(checkUrl);
|
var task = new ProxyCheckTask(checkUrl);
|
||||||
final var progress = AlertUtil.progress();
|
task.onListen(new TaskListener(task) {
|
||||||
progress.title(Context.getLanguageBinding("proxy.test.title").get());
|
|
||||||
task.onListen(new BaseTask.Listener() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRunning() {
|
public void onSucceed() {
|
||||||
progress.onCancel(task::cancel).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSucceeded() {
|
|
||||||
Platform.runLater(progress::close);
|
|
||||||
AlertUtil.info(Context.getLanguageBinding("proxy.test.result.success").getValue()).show();
|
AlertUtil.info(Context.getLanguageBinding("proxy.test.result.success").getValue()).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailed(Throwable throwable) {
|
public void onFail(Throwable throwable) {
|
||||||
Platform.runLater(progress::close);
|
|
||||||
final var tmp = Context.getLanguageBinding("proxy.test.result.failed").getValue();
|
final var tmp = Context.getLanguageBinding("proxy.test.result.failed").getValue();
|
||||||
String throwableMessage = throwable.getMessage();
|
String throwableMessage = throwable.getMessage();
|
||||||
AlertUtil.error(tmp + (StringUtils.isEmpty(throwableMessage) ? "" : throwableMessage)).show();
|
AlertUtil.error(tmp + (StringUtils.isEmpty(throwableMessage) ? "" : throwableMessage)).show();
|
||||||
|
31
src/main/resources/css/admin-panel.scss
Normal file
31
src/main/resources/css/admin-panel.scss
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**************************************************
|
||||||
|
* Admin Password Panel
|
||||||
|
**************************************************/
|
||||||
|
|
||||||
|
#admin-panel {
|
||||||
|
-fx-background-color: -color-bg-default;
|
||||||
|
-fx-background-radius: 15;
|
||||||
|
-fx-border-radius: 15;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
.label {
|
||||||
|
-fx-font-size: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button, .button.ikonli-font-icon {
|
||||||
|
-fx-background-radius: 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-field {
|
||||||
|
-fx-spacing: 10;
|
||||||
|
|
||||||
|
.text-field {
|
||||||
|
-fx-pref-width: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
//-fx-padding: 5 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,24 +14,27 @@
|
|||||||
-fx-border-radius: 10;
|
-fx-border-radius: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-menu, #startButton, #passwordButton, #restartButton, #moreButton {
|
.control-menu, #moreButton {
|
||||||
-fx-font-size: 15;
|
-fx-font-size: 15;
|
||||||
-fx-background-radius: 15;
|
-fx-background-radius: 15;
|
||||||
-fx-padding: 10 40 10 40;
|
-fx-padding: 10 40;
|
||||||
-fx-border-radius: 15;
|
-fx-border-radius: 15;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#startButton {
|
#startButton {
|
||||||
|
-color-button-bg-hover: -color-button-bg;
|
||||||
|
-color-button-bg-focused: -color-button-bg;
|
||||||
-fx-border-color: -color-button-bg;
|
-fx-border-color: -color-button-bg;
|
||||||
|
-fx-border-color-hover: -color-button-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
#passwordButton {
|
#passwordButton {
|
||||||
-color-button-bg: -color-success-3;
|
-color-button-bg: -color-success-3;
|
||||||
-color-button-bg-hover: -color-button-bg;
|
-color-button-bg-hover: -color-button-bg;
|
||||||
-color-button-bg-focused: -color-button-bg;
|
-color-button-bg-focused: -color-success-3;
|
||||||
-color-button-bg-pressed: -color-button-bg;
|
-color-button-bg-pressed: -color-button-bg;
|
||||||
-fx-border-color: -color-button-bg;
|
-fx-border-color: -color-success-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#restartButton {
|
#restartButton {
|
||||||
@ -43,12 +46,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#moreButton {
|
#moreButton {
|
||||||
-color-button-bg-focused: transparent;
|
-fx-padding: 2 30;
|
||||||
-color-button-bg-pressed: transparent;
|
|
||||||
-color-button-bg-hover: -color-chart-6-alpha20;
|
-fx-background-color: transparent;
|
||||||
-color-button-border: -color-chart-6;
|
-fx-border-color: -color-chart-6-alpha70;
|
||||||
-fx-border-color: -color-chart-6;
|
|
||||||
-color-button-border-hover: -color-chart-6-alpha70;
|
&:hover {
|
||||||
|
-fx-background-color: -color-chart-6-alpha20;
|
||||||
|
-fx-border-color: -color-chart-6-alpha70;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu, .menu-item {
|
||||||
|
-fx-background-radius: 15;
|
||||||
|
-fx-border-radius: 15;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logArea {
|
.logArea {
|
||||||
|
@ -91,23 +91,22 @@
|
|||||||
#windowFooter {
|
#windowFooter {
|
||||||
.button {
|
.button {
|
||||||
-fx-font-size: 15;
|
-fx-font-size: 15;
|
||||||
-fx-background-color: transparent;
|
|
||||||
-fx-text-alignment: CENTER;
|
-fx-text-alignment: CENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
#document {
|
|
||||||
-fx-text-fill: -color-success-5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#github {
|
|
||||||
-fx-text-fill: -color-accent-5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sponsor {
|
|
||||||
-fx-text-fill: -color-danger-emphasis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ikonli-font-icon {
|
.ikonli-font-icon {
|
||||||
-fx-font-size: 15;
|
-fx-font-size: 15;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
* Modal Pane
|
||||||
|
**************************************************/
|
||||||
|
|
||||||
|
.modal-pane {
|
||||||
|
-fx-background-radius: 15;
|
||||||
|
|
||||||
|
.scrollable-content {
|
||||||
|
-fx-background-radius: 15;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,10 +1,12 @@
|
|||||||
/**************************************************
|
/**************************************************
|
||||||
* Root
|
* Root
|
||||||
**************************************************/
|
**************************************************/
|
||||||
|
.root {
|
||||||
|
-fx-font-size: 15;
|
||||||
|
-fx-font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
.root-pane {
|
.root-pane {
|
||||||
-fx-font-size: 15;
|
|
||||||
-fx-font-weight: normal;
|
|
||||||
|
|
||||||
-fx-background-radius: 15;
|
-fx-background-radius: 15;
|
||||||
-fx-border-radius: 15;
|
-fx-border-radius: 15;
|
||||||
|
@ -29,6 +29,6 @@
|
|||||||
<Label styleClass="shield-name" text="%about.app.version"/>
|
<Label styleClass="shield-name" text="%about.app.version"/>
|
||||||
<Label styleClass="shield-version" text="v${project.version}"/>
|
<Label styleClass="shield-version" text="v${project.version}"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Button fx:id="checkAppVersion" styleClass="flat" text="%about.app.update"/>
|
<Button fx:id="checkAppVersion" onAction="#checkGuiUpdate" styleClass="flat" text="%about.app.update"/>
|
||||||
<Button fx:id="checkAListVersion" styleClass="flat" onAction="#checkAListUpdate" text="%about.alist.update"/>
|
<Button fx:id="checkAListVersion" onAction="#checkAListUpdate" styleClass="flat" text="%about.alist.update"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
63
src/main/resources/fxml/admin-panel.fxml
Normal file
63
src/main/resources/fxml/admin-panel.fxml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import atlantafx.base.layout.InputGroup?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import org.kordamp.ikonli.javafx.*?>
|
||||||
|
<AnchorPane id="admin-panel" fx:id="adminPanel" maxHeight="250" maxWidth="520" prefHeight="250.0" prefWidth="520.0"
|
||||||
|
stylesheets="@../css/admin-panel.css" xmlns="http://javafx.com/javafx/11.0.14-internal"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1" fx:controller="cn.octopusyan.alistgui.controller.PasswordController">
|
||||||
|
|
||||||
|
<AnchorPane styleClass="header" prefWidth="520" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0"
|
||||||
|
AnchorPane.topAnchor="0">
|
||||||
|
<Label text="%admin.pwd.title" AnchorPane.leftAnchor="10" AnchorPane.topAnchor="10"/>
|
||||||
|
<Button onAction="#close" styleClass="flat" AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0">
|
||||||
|
<graphic>
|
||||||
|
<FontIcon iconLiteral="fa-remove"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</AnchorPane>
|
||||||
|
|
||||||
|
<VBox alignment="CENTER" spacing="10" AnchorPane.bottomAnchor="30" AnchorPane.leftAnchor="0"
|
||||||
|
AnchorPane.rightAnchor="0">
|
||||||
|
|
||||||
|
<Label style="-fx-background-radius: 10;-fx-background-color: -color-button-bg-hover;"
|
||||||
|
styleClass="button, flat, danger" text="%admin.pwd.toptip"/>
|
||||||
|
|
||||||
|
<HBox alignment="CENTER" styleClass="admin-field">
|
||||||
|
<Label text="%admin.pwd.user-field"/>
|
||||||
|
<InputGroup fx:id="userField" styleClass="admin-field-value">
|
||||||
|
<TextField fx:id="usernameField" text="admin" editable="false"/>
|
||||||
|
<Button fx:id="copyUsername" onAction="#copyUsername">
|
||||||
|
<graphic>
|
||||||
|
<FontIcon iconLiteral="fa-copy"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</InputGroup>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<HBox alignment="CENTER" styleClass="admin-field">
|
||||||
|
<Label text="%admin.pwd.pwd-field"/>
|
||||||
|
<InputGroup styleClass="admin-field-value">
|
||||||
|
<PasswordField fx:id="passwordField" editable="false"/>
|
||||||
|
<Button fx:id="refreshPassword" onAction="#savePassword" visible="false" managed="false">
|
||||||
|
<graphic>
|
||||||
|
<FontIcon iconLiteral="fa-refresh"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="savePassword" onAction="#savePassword" visible="false" managed="false">
|
||||||
|
<graphic>
|
||||||
|
<FontIcon iconLiteral="fa-save"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="copyPassword" onAction="#copyPassword">
|
||||||
|
<graphic>
|
||||||
|
<FontIcon iconLiteral="fa-copy"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</InputGroup>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
</AnchorPane>
|
@ -5,13 +5,14 @@
|
|||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<VBox fx:id="mainView" prefHeight="700" prefWidth="720" stylesheets="@../css/main-view.css"
|
<VBox fx:id="mainView" prefHeight="700" prefWidth="720" stylesheets="@../css/main-view.css"
|
||||||
xmlns="http://javafx.com/javafx/11.0.14-internal" xmlns:fx="http://javafx.com/fxml/1"
|
xmlns="http://javafx.com/javafx/11.0.14-internal" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
alignment="TOP_CENTER"
|
||||||
fx:controller="cn.octopusyan.alistgui.controller.MainController">
|
fx:controller="cn.octopusyan.alistgui.controller.MainController">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets left="10.0" right="10.0" top="10.0"/>
|
<Insets left="10.0" right="10.0" top="10.0"/>
|
||||||
</padding>
|
</padding>
|
||||||
<HBox alignment="TOP_CENTER" prefWidth="Infinity">
|
<HBox alignment="TOP_CENTER" prefWidth="Infinity">
|
||||||
<Label fx:id="homeLabel" alignment="CENTER" text="AList GUI"/>
|
<Label fx:id="homeLabel" alignment="CENTER" text="AList GUI"/>
|
||||||
<Button fx:id="statusLabel" styleClass="success" alignment="TOP_CENTER" text="%main.status.label-running">
|
<Button fx:id="statusLabel" styleClass="danger" alignment="TOP_CENTER" text="%main.status.label-stop">
|
||||||
<HBox.margin>
|
<HBox.margin>
|
||||||
<Insets left="-10.0" top="-5"/>
|
<Insets left="-10.0" top="-5"/>
|
||||||
</HBox.margin>
|
</HBox.margin>
|
||||||
@ -24,12 +25,19 @@
|
|||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" top="10.0"/>
|
<Insets bottom="10.0" top="10.0"/>
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
<Button fx:id="startButton" styleClass="large, control-menu, success" onAction="#start"
|
<Button fx:id="startButton" onAction="#start" styleClass="control-menu, success"
|
||||||
text="%main.control.start"/>
|
text="%main.control.start"/>
|
||||||
<Button fx:id="passwordButton" styleClass="large, control-menu, success" text="%main.control.password"/>
|
<Button fx:id="passwordButton" onAction="#adminPassword" styleClass="control-menu, success"
|
||||||
<Button fx:id="restartButton" styleClass="large, control-menu, success" onAction="#restart"
|
text="%main.control.password"/>
|
||||||
|
<Button fx:id="restartButton" onAction="#restart" styleClass="control-menu, success"
|
||||||
text="%main.control.restart"/>
|
text="%main.control.restart"/>
|
||||||
<Button fx:id="moreButton" styleClass="large, control-menu, button-outlined" text="%main.control.more"/>
|
<MenuButton fx:id="moreButton" styleClass="button-outlined, no-arrow" text="%main.control.more">
|
||||||
|
<items>
|
||||||
|
<MenuItem fx:id="browserButton" onAction="#openInBrowser" disable="true" text="%main.more.browser"/>
|
||||||
|
<MenuItem onAction="#openConfig" text="%main.more.open-config"/>
|
||||||
|
<MenuItem onAction="#openLogFolder" text="%main.more.open-log"/>
|
||||||
|
</items>
|
||||||
|
</MenuButton>
|
||||||
</HBox>
|
</HBox>
|
||||||
<ScrollPane fx:id="logAreaSp" fitToWidth="true" prefHeight="499.0" prefWidth="Infinity"
|
<ScrollPane fx:id="logAreaSp" fitToWidth="true" prefHeight="499.0" prefWidth="Infinity"
|
||||||
styleClass="logArea" VBox.vgrow="ALWAYS">
|
styleClass="logArea" VBox.vgrow="ALWAYS">
|
||||||
|
@ -2,55 +2,60 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
|
||||||
<?import org.kordamp.ikonli.javafx.*?>
|
<?import org.kordamp.ikonli.javafx.*?>
|
||||||
<VBox fx:id="rootPane" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
|
<StackPane fx:id="rootPane" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
|
||||||
alignment="TOP_CENTER" prefHeight="720.0" prefWidth="770.0" spacing="10.0"
|
prefHeight="720.0" prefWidth="770.0"
|
||||||
styleClass="root-pane" stylesheets="@../css/root-view.css"
|
styleClass="root-pane" stylesheets="@../css/root-view.css"
|
||||||
fx:controller="cn.octopusyan.alistgui.controller.RootController">
|
fx:controller="cn.octopusyan.alistgui.controller.RootController">
|
||||||
|
|
||||||
<HBox fx:id="windowHeader" alignment="CENTER_RIGHT" prefWidth="Infinity" spacing="10.0">
|
<VBox prefHeight="720.0" prefWidth="770.0" spacing="10.0">
|
||||||
<padding>
|
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
|
||||||
</padding>
|
|
||||||
<FontIcon fx:id="alwaysOnTopIcon" styleClass="icon-button"/>
|
|
||||||
<FontIcon fx:id="minimizeIcon" styleClass="icon-button"/>
|
|
||||||
<FontIcon fx:id="closeIcon" styleClass="icon-button"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<TabPane fx:id="tabPane" prefWidth="Infinity" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
|
<HBox fx:id="windowHeader" alignment="CENTER_RIGHT" prefWidth="Infinity" spacing="10.0">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets left="20.0" right="20.0"/>
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||||
</padding>
|
</padding>
|
||||||
<Tab text="%root.tab.main">
|
<FontIcon fx:id="alwaysOnTopIcon" styleClass="icon-button"/>
|
||||||
<graphic>
|
<FontIcon fx:id="minimizeIcon" styleClass="icon-button"/>
|
||||||
<FontIcon iconColor="white" iconLiteral="fa-th-large"/>
|
<FontIcon fx:id="closeIcon" styleClass="icon-button"/>
|
||||||
</graphic>
|
</HBox>
|
||||||
<!-- 引入主页 -->
|
|
||||||
<fx:include fx:id="mainController" source="main-view.fxml" prefWidth="Infinity" prefHeight="-Infinity"/>
|
<TabPane fx:id="tabPane" prefWidth="Infinity" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
|
||||||
</Tab>
|
<padding>
|
||||||
<Tab text="%root.tab.setup">
|
<Insets left="20.0" right="20.0"/>
|
||||||
<graphic>
|
</padding>
|
||||||
<FontIcon iconColor="white" iconLiteral="fa-cog"/>
|
<Tab text="%root.tab.main">
|
||||||
</graphic>
|
<graphic>
|
||||||
<!-- 引入设置页 -->
|
<FontIcon iconColor="white" iconLiteral="fa-th-large"/>
|
||||||
<fx:include fx:id="setupController" source="setup-view.fxml" prefWidth="Infinity" prefHeight="-Infinity"/>
|
</graphic>
|
||||||
</Tab>
|
<!-- 引入主页 -->
|
||||||
<Tab text="%root.tab.about">
|
<fx:include fx:id="mainController" source="main-view.fxml" prefWidth="Infinity" prefHeight="-Infinity"/>
|
||||||
<graphic>
|
</Tab>
|
||||||
<FontIcon iconColor="white" iconLiteral="fa-info-circle"/>
|
<Tab text="%root.tab.setup">
|
||||||
</graphic>
|
<graphic>
|
||||||
<!-- 引入关于页 -->
|
<FontIcon iconColor="white" iconLiteral="fa-cog"/>
|
||||||
<fx:include fx:id="aboutController" source="about-view.fxml" prefWidth="Infinity" prefHeight="-Infinity"/>
|
</graphic>
|
||||||
</Tab>
|
<!-- 引入设置页 -->
|
||||||
</TabPane>
|
<fx:include fx:id="setupController" source="setup-view.fxml" prefWidth="Infinity"
|
||||||
<HBox fx:id="windowFooter" alignment="CENTER" prefWidth="Infinity" spacing="25.0">
|
prefHeight="-Infinity"/>
|
||||||
<padding>
|
</Tab>
|
||||||
<Insets bottom="30.0"/>
|
<Tab text="%root.tab.about">
|
||||||
</padding>
|
<graphic>
|
||||||
<Button fx:id="document" text="%root.foot.doc"/>
|
<FontIcon iconColor="white" iconLiteral="fa-info-circle"/>
|
||||||
<Button fx:id="github" text="%root.foot.github"/>
|
</graphic>
|
||||||
<Button fx:id="sponsor" text="%root.foot.sponsor"/>
|
<!-- 引入关于页 -->
|
||||||
</HBox>
|
<fx:include fx:id="aboutController" source="about-view.fxml" prefWidth="Infinity"
|
||||||
</VBox>
|
prefHeight="-Infinity"/>
|
||||||
|
</Tab>
|
||||||
|
</TabPane>
|
||||||
|
<HBox fx:id="windowFooter" alignment="CENTER" prefWidth="Infinity" spacing="25.0">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="30.0"/>
|
||||||
|
</padding>
|
||||||
|
<Button fx:id="document" onAction="#openDocument" styleClass="success, flat" text="%root.foot.doc"/>
|
||||||
|
<Button fx:id="github" onAction="#openGithub" styleClass="accent, flat" text="%root.foot.github"/>
|
||||||
|
<Button fx:id="sponsor" styleClass="danger, flat" text="%root.foot.sponsor"
|
||||||
|
visible="false" managed="false"/>
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
|
@ -13,6 +13,7 @@ main.control.more=\u66F4\u591A
|
|||||||
main.status.label-running=\u8FD0\u884C\u4E2D
|
main.status.label-running=\u8FD0\u884C\u4E2D
|
||||||
main.status.label-stop=\u5DF2\u505C\u6B62
|
main.status.label-stop=\u5DF2\u505C\u6B62
|
||||||
main.more.browser=\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00
|
main.more.browser=\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00
|
||||||
|
main.more.open-config=\u6253\u5F00\u914D\u7F6E\u6587\u4EF6
|
||||||
main.more.open-log=\u6253\u5F00\u65E5\u5FD7\u6587\u4EF6\u5939
|
main.more.open-log=\u6253\u5F00\u65E5\u5FD7\u6587\u4EF6\u5939
|
||||||
alist.status.start.running=AList \u6B63\u5728\u8FD0\u884C\u3002\u3002\u3002
|
alist.status.start.running=AList \u6B63\u5728\u8FD0\u884C\u3002\u3002\u3002
|
||||||
alist.status.start=\u6B63\u5728\u542F\u52A8 AList
|
alist.status.start=\u6B63\u5728\u542F\u52A8 AList
|
||||||
@ -37,10 +38,16 @@ about.app.version=GUI \u7248\u672C
|
|||||||
about.alist.update=\u68C0\u67E5 AList \u7248\u672C
|
about.alist.update=\u68C0\u67E5 AList \u7248\u672C
|
||||||
about.app.update=\u68C0\u67E5 GUI \u7248\u672C
|
about.app.update=\u68C0\u67E5 GUI \u7248\u672C
|
||||||
setup.theme=\u4E3B\u9898
|
setup.theme=\u4E3B\u9898
|
||||||
update.current=\u5F53\u524D
|
update.current=\u5F53\u524D\u7248\u672C
|
||||||
update.remote=\u6700\u65B0
|
update.remote=\u6700\u65B0\u7248\u672C
|
||||||
update.upgrade.not=\u5DF2\u662F\u6700\u65B0\u7248\u672C
|
update.upgrade.not=\u5DF2\u662F\u6700\u65B0\u7248\u672C
|
||||||
update.upgrade.new=\u68C0\u67E5\u5230\u65B0\u7248\u672C
|
update.upgrade.new=\u68C0\u67E5\u5230\u65B0\u7248\u672C
|
||||||
|
msg.alist.download.notfile=\u6CA1\u68C0\u6D4B\u5230AList\u6587\u4EF6\uFF0C\u662F\u5426\u73B0\u5728\u4E0B\u8F7D\uFF1F
|
||||||
|
msg.alist.pwd.copy=\u590D\u5236\u6210\u529F
|
||||||
|
admin.pwd.title=\u7BA1\u7406\u5458\u5BC6\u7801
|
||||||
|
admin.pwd.toptip=\u65B0\u5BC6\u7801\u53EA\u4F1A\u663E\u793A\u4E00\u6B21
|
||||||
|
admin.pwd.user-field=\u7528\u6237\uFF1A
|
||||||
|
admin.pwd.pwd-field=\u5BC6\u7801\uFF1A
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ main.control.more=More
|
|||||||
main.status.label-running=Running
|
main.status.label-running=Running
|
||||||
main.status.label-stop=Stoped
|
main.status.label-stop=Stoped
|
||||||
main.more.browser=Open in browser
|
main.more.browser=Open in browser
|
||||||
|
main.more.open-config=Open configuration file
|
||||||
main.more.open-log=Open the log folder
|
main.more.open-log=Open the log folder
|
||||||
alist.status.start=Starting AList
|
alist.status.start=Starting AList
|
||||||
alist.status.start.running=AList is running...
|
alist.status.start.running=AList is running...
|
||||||
@ -37,9 +38,15 @@ about.app.version=GUI Version
|
|||||||
about.alist.update=Check AList Version
|
about.alist.update=Check AList Version
|
||||||
about.app.update=Check GUI Version
|
about.app.update=Check GUI Version
|
||||||
setup.theme=Theme
|
setup.theme=Theme
|
||||||
update.current=Current
|
update.current=Current Version
|
||||||
update.remote=Latest
|
update.remote=Latest Version
|
||||||
update.upgrade.not=It is already the latest version
|
update.upgrade.not=It is already the latest version
|
||||||
update.upgrade.new=Detected a new version
|
update.upgrade.new=Detected a new version
|
||||||
|
msg.alist.download.notfile=AList file not detected, download now?
|
||||||
|
msg.alist.pwd.copy=Copy successful
|
||||||
|
admin.pwd.title=Admin Password
|
||||||
|
admin.pwd.toptip=The new password will only be displayed once
|
||||||
|
admin.pwd.user-field=User:
|
||||||
|
admin.pwd.pwd-field=Password :
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ main.control.more=\u66F4\u591A
|
|||||||
main.status.label-running=\u8FD0\u884C\u4E2D
|
main.status.label-running=\u8FD0\u884C\u4E2D
|
||||||
main.status.label-stop=\u5DF2\u505C\u6B62
|
main.status.label-stop=\u5DF2\u505C\u6B62
|
||||||
main.more.browser=\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00
|
main.more.browser=\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00
|
||||||
|
main.more.open-config=\u6253\u5F00\u914D\u7F6E\u6587\u4EF6
|
||||||
main.more.open-log=\u6253\u5F00\u65E5\u5FD7\u6587\u4EF6\u5939
|
main.more.open-log=\u6253\u5F00\u65E5\u5FD7\u6587\u4EF6\u5939
|
||||||
alist.status.stop.stopped=AList \u5DF2\u505C\u6B62
|
alist.status.stop.stopped=AList \u5DF2\u505C\u6B62
|
||||||
alist.status.start=\u6B63\u5728\u542F\u52A8 AList
|
alist.status.start=\u6B63\u5728\u542F\u52A8 AList
|
||||||
@ -37,9 +38,15 @@ about.app.version=GUI \u7248\u672C
|
|||||||
about.alist.update=\u68C0\u67E5 AList \u7248\u672C
|
about.alist.update=\u68C0\u67E5 AList \u7248\u672C
|
||||||
about.app.update=\u68C0\u67E5 GUI \u7248\u672C
|
about.app.update=\u68C0\u67E5 GUI \u7248\u672C
|
||||||
setup.theme=\u4E3B\u9898
|
setup.theme=\u4E3B\u9898
|
||||||
update.current=\u5F53\u524D
|
update.current=\u5F53\u524D\u7248\u672C
|
||||||
update.remote=\u6700\u65B0
|
update.remote=\u6700\u65B0\u7248\u672C
|
||||||
update.upgrade.not=\u5DF2\u662F\u6700\u65B0\u7248\u672C
|
update.upgrade.not=\u5DF2\u662F\u6700\u65B0\u7248\u672C
|
||||||
update.upgrade.new=\u68C0\u67E5\u5230\u65B0\u7248\u672C
|
update.upgrade.new=\u68C0\u67E5\u5230\u65B0\u7248\u672C
|
||||||
|
msg.alist.download.notfile=\u6CA1\u68C0\u6D4B\u5230AList\u6587\u4EF6\uFF0C\u662F\u5426\u73B0\u5728\u4E0B\u8F7D\uFF1F
|
||||||
|
msg.alist.pwd.copy=\u590D\u5236\u6210\u529F
|
||||||
|
admin.pwd.title=\u7BA1\u7406\u5458\u5BC6\u7801
|
||||||
|
admin.pwd.toptip=\u65B0\u5BC6\u7801\u53EA\u4F1A\u663E\u793A\u4E00\u6B21
|
||||||
|
admin.pwd.user-field=\u7528\u6237\uFF1A
|
||||||
|
admin.pwd.pwd-field=\u5BC6\u7801\uFF1A
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user