feat: 主界面启动按钮、命令执行工具

This commit is contained in:
octopus_yan 2024-09-12 22:04:25 +08:00
parent cfd7075c8f
commit c93837a7a9
10 changed files with 217 additions and 53 deletions

View File

@ -6,6 +6,7 @@ import cn.octopusyan.alistgui.manager.ConfigManager;
import cn.octopusyan.alistgui.manager.http.HttpConfig; 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.alert.AlertUtil; import cn.octopusyan.alistgui.util.alert.AlertUtil;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.Scene; import javafx.scene.Scene;
@ -92,6 +93,8 @@ public class Application extends javafx.application.Application {
@Override @Override
public void stop() { public void stop() {
logger.info("application stop ..."); logger.info("application stop ...");
// 关闭所有命令
ProcessesUtil.destroyAll();
// 保存应用数据 // 保存应用数据
ConfigManager.save(); ConfigManager.save();
// 停止所有线程 // 停止所有线程

View File

@ -14,10 +14,12 @@ public class Constants {
public static final String APP_TITLE = PropertiesUtils.getInstance().getProperty("app.title"); public static final String APP_TITLE = PropertiesUtils.getInstance().getProperty("app.title");
public static final String APP_NAME = PropertiesUtils.getInstance().getProperty("app.name"); public static final String APP_NAME = PropertiesUtils.getInstance().getProperty("app.name");
public static final String APP_VERSION = PropertiesUtils.getInstance().getProperty("app.version"); public static final String APP_VERSION = PropertiesUtils.getInstance().getProperty("app.version");
public static final String DATA_DIR_PATH = Paths.get(".").toFile().getAbsolutePath(); public static final String DATA_DIR_PATH = Paths.get(".").toFile().getAbsolutePath();
public static final String BIN_DIR_PATH = STR."\{DATA_DIR_PATH}\{File.separator}bin"; public static final String BIN_DIR_PATH = STR."\{DATA_DIR_PATH}\{File.separator}bin";
public static final String UPGRADE_PATH = STR."\{BIN_DIR_PATH}\{File.separator}upgrade.yaml";
public static final String TMP_DIR_PATH = System.getProperty("java.io.tmpdir") + APP_NAME; public static final String TMP_DIR_PATH = System.getProperty("java.io.tmpdir") + APP_NAME;
public static final String ALIST_FILE = STR."\{BIN_DIR_PATH}\{File.separator}alist.exe";
public static final String CONFIG_DIR_PATH = STR."\{DATA_DIR_PATH}\{File.separator}config"; public static final String CONFIG_DIR_PATH = STR."\{DATA_DIR_PATH}\{File.separator}config";
public static final String GUI_CONFIG_PATH = STR."\{CONFIG_DIR_PATH}\{File.separator}gui.yaml"; public static final String GUI_CONFIG_PATH = STR."\{CONFIG_DIR_PATH}\{File.separator}gui.yaml";
public static final String BAK_FILE_PATH = STR."\{Constants.TMP_DIR_PATH}\{File.separator}bak"; public static final String BAK_FILE_PATH = STR."\{Constants.TMP_DIR_PATH}\{File.separator}bak";

View File

@ -1,8 +1,12 @@
package cn.octopusyan.alistgui.controller; package cn.octopusyan.alistgui.controller;
import cn.octopusyan.alistgui.base.BaseController; import cn.octopusyan.alistgui.base.BaseController;
import cn.octopusyan.alistgui.config.Context;
import cn.octopusyan.alistgui.manager.AListManager;
import cn.octopusyan.alistgui.manager.ConsoleLog; import cn.octopusyan.alistgui.manager.ConsoleLog;
import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button;
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;
@ -22,6 +26,10 @@ public class MainController extends BaseController<VBox> {
public VBox logArea; public VBox logArea;
@FXML @FXML
public ScrollPane logAreaSp; public ScrollPane logAreaSp;
@FXML
public Button statusLabel;
@FXML
public Button startButton;
@Override @Override
public VBox getRootPanel() { public VBox getRootPanel() {
@ -35,11 +43,42 @@ 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));
}
private void setStartButton(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(() -> {
startButton.getStyleClass().remove(removeStyle);
startButton.getStyleClass().add(addStyle);
startButton.textProperty().set(button);
statusLabel.getStyleClass().remove(addStyle);
statusLabel.getStyleClass().add(removeStyle);
statusLabel.textProperty().set(status);
});
}
@FXML
public void start() {
if (AListManager.isRunning()) {
AListManager.stop();
} else {
AListManager.start();
}
}
@FXML
public void restart() {
AListManager.restart();
} }
} }

View File

@ -0,0 +1,85 @@
package cn.octopusyan.alistgui.manager;
import cn.octopusyan.alistgui.config.Constants;
import cn.octopusyan.alistgui.config.Context;
import cn.octopusyan.alistgui.util.ProcessesUtil;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
/**
* AList 管理
*
* @author octopus_yan
*/
public class AListManager {
public static final String START_COMMAND = STR."\{Constants.ALIST_FILE} server";
private static final ProcessesUtil util;
private static final BooleanProperty running = new SimpleBooleanProperty(false);
static {
util = ProcessesUtil.init(Constants.BIN_DIR_PATH);
}
public static BooleanProperty runningProperty() {
return running;
}
public static boolean isRunning() {
return running.get();
}
public static void start() {
ConsoleLog.info(getText("alist.status.start"));
if (running.get()) {
ConsoleLog.warning(getText("alist.status.start.running"));
return;
}
running.set(true);
util.exec(START_COMMAND, new ProcessesUtil.OnExecuteListener() {
@Override
public void onExecute(String msg) {
if (ConsoleLog.isInit())
ConsoleLog.msg(msg);
}
@Override
public void onExecuteSuccess(int exitValue) {
running.set(false);
}
@Override
public void onExecuteError(Exception e) {
running.set(false);
}
});
}
public static void stop() {
ConsoleLog.info(getText("alist.status.stop"));
if (!running.get()) {
ConsoleLog.warning(getText("alist.status.stop.stopped"));
return;
}
util.destroy();
}
static ChangeListener<Boolean> changeListener;
public static void restart() {
stop();
changeListener = (_, _, run) -> {
if (run) return;
start();
if (changeListener != null) {
running.removeListener(changeListener);
}
};
running.addListener(changeListener);
}
private static String getText(String code) {
return Context.getLanguageBinding(code).get();
}
}

View File

@ -1,44 +1,66 @@
package cn.octopusyan.alistgui.util; package cn.octopusyan.alistgui.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.exec.*; import org.apache.commons.exec.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
/** /**
* 命令工具类 * 命令工具类
* *
* @author octopus_yan@foxmail.com * @author octopus_yan@foxmail.com
*/ */
@Slf4j
public class ProcessesUtil { public class ProcessesUtil {
private static final Logger logger = LoggerFactory.getLogger(ProcessesUtil.class);
private static final String NEW_LINE = System.lineSeparator(); private static final String NEW_LINE = System.lineSeparator();
private static final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
public static boolean exec(String command) { private final Executor executor = DefaultExecutor.builder().get();
try { private final ShutdownHookProcessDestroyer processDestroyer;
exec(command, msg -> {}); private DefaultExecuteResultHandler handler;
handler.waitFor(); private final PumpStreamHandler streamHandler;
} catch (Exception e) { private OnExecuteListener listener;
logger.error("", e); private CommandLine commandLine;
} private final LogOutputStream logout = new LogOutputStream() {
return 0 == handler.getExitValue();
}
public static void exec(String command, OnExecuteListener listener) {
LogOutputStream logout = new LogOutputStream() {
@Override @Override
protected void processLine(String line, int logLevel) { protected void processLine(String line, int logLevel) {
if (listener != null) listener.onExecute(line + NEW_LINE); if (listener != null)
listener.onExecute(line + NEW_LINE);
} }
}; };
CommandLine commandLine = CommandLine.parse(command); private static final Set<ProcessesUtil> set = new HashSet<>();
DefaultExecutor executor = DefaultExecutor.builder().get();
executor.setStreamHandler(new PumpStreamHandler(logout, logout)); /**
DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler() { * Prevent construction.
*/
private ProcessesUtil(String workingDirectory) {
streamHandler = new PumpStreamHandler(logout, logout);
executor.setStreamHandler(streamHandler);
executor.setWorkingDirectory(new File(workingDirectory));
executor.setExitValue(1);
processDestroyer = new ShutdownHookProcessDestroyer();
executor.setProcessDestroyer(processDestroyer);
}
public static ProcessesUtil init(String workingDirectory) {
ProcessesUtil util = new ProcessesUtil(workingDirectory);
set.add(util);
return util;
}
public boolean exec(String command) throws IOException {
commandLine = CommandLine.parse(command);
int execute = executor.execute(commandLine);
return 0 == execute;
}
public void exec(String command, OnExecuteListener listener) {
this.listener = listener;
commandLine = CommandLine.parse(command);
handler = new DefaultExecuteResultHandler() {
@Override @Override
public void onProcessComplete(int exitValue) { public void onProcessComplete(int exitValue) {
if (listener != null) { if (listener != null) {
@ -60,15 +82,22 @@ public class ProcessesUtil {
} }
} }
public interface OnExecuteListener { public void destroy() {
void onExecute(String msg); if (processDestroyer.isEmpty()) return;
default void onExecuteSuccess(int exitValue){} processDestroyer.run();
default void onExecuteError(Exception e){}
} }
/** public static void destroyAll() {
* Prevent construction. set.forEach(ProcessesUtil::destroy);
*/ }
private ProcessesUtil() {
public interface OnExecuteListener {
void onExecute(String msg);
default void onExecuteSuccess(int exitValue) {
}
default void onExecuteError(Exception e) {
}
} }
} }

View File

@ -12,10 +12,6 @@
-fx-background-radius: 10; -fx-background-radius: 10;
-fx-text-alignment: CENTER; -fx-text-alignment: CENTER;
-fx-border-radius: 10; -fx-border-radius: 10;
-color-button-bg: -color-success-3;
-color-button-border: -color-button-bg;
-color-button-border-hover: -color-button-bg;
-color-button-bg-hover: -color-button-bg;
} }
.control-menu, #startButton, #passwordButton, #restartButton, #moreButton { .control-menu, #startButton, #passwordButton, #restartButton, #moreButton {
@ -27,12 +23,6 @@
} }
#startButton { #startButton {
-color-button-bg: -color-danger-4;
-color-button-bg-hover: -color-button-bg;
-color-button-bg-focused: -color-button-bg;
-color-button-bg-pressed: -color-button-bg;
-color-button-border: -color-button-bg;
-color-button-border-hover: -color-button-bg;
-fx-border-color: -color-button-bg; -fx-border-color: -color-button-bg;
} }
@ -41,8 +31,6 @@
-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-button-bg;
-color-button-bg-pressed: -color-button-bg; -color-button-bg-pressed: -color-button-bg;
-color-button-border: -color-button-bg;
-color-button-border-hover: -color-button-bg;
-fx-border-color: -color-button-bg; -fx-border-color: -color-button-bg;
} }
@ -51,15 +39,16 @@
-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-button-bg;
-color-button-bg-pressed: -color-button-bg; -color-button-bg-pressed: -color-button-bg;
-color-button-border: -color-button-bg;
-color-button-border-hover: -color-button-bg;
-fx-border-color: -color-button-bg; -fx-border-color: -color-button-bg;
} }
#moreButton { #moreButton {
-fx-background-color: transparent; -color-button-bg-focused: transparent;
-fx-text-fill: -color-chart-6; -color-button-bg-pressed: transparent;
-color-button-bg-hover: -color-chart-6-alpha20;
-color-button-border: -color-chart-6;
-fx-border-color: -color-chart-6; -fx-border-color: -color-chart-6;
-color-button-border-hover: -color-chart-6-alpha70;
} }
.logArea { .logArea {

View File

@ -24,10 +24,12 @@
<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="control-menu, danger" text="%main.control.start"/> <Button fx:id="startButton" styleClass="large, control-menu, success" onAction="#start"
<Button fx:id="passwordButton" styleClass="control-menu, success" text="%main.control.password"/> text="%main.control.start"/>
<Button fx:id="restartButton" styleClass="control-menu, success" text="%main.control.restart"/> <Button fx:id="passwordButton" styleClass="large, control-menu, success" text="%main.control.password"/>
<Button fx:id="moreButton" styleClass="control-menu" text="%main.control.more"/> <Button fx:id="restartButton" styleClass="large, control-menu, success" onAction="#restart"
text="%main.control.restart"/>
<Button fx:id="moreButton" styleClass="large, control-menu, button-outlined" text="%main.control.more"/>
</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">

View File

@ -6,6 +6,7 @@ root.foot.doc=\u6587\u6863
root.foot.github=GitHub root.foot.github=GitHub
root.foot.sponsor=\u8D5E\u52A9 AList root.foot.sponsor=\u8D5E\u52A9 AList
main.control.start=\u5F00\u59CB main.control.start=\u5F00\u59CB
main.control.stop=\u505C\u6B62
main.control.password=\u5BC6\u7801 main.control.password=\u5BC6\u7801
main.control.restart=\u91CD\u542F main.control.restart=\u91CD\u542F
main.control.more=\u66F4\u591A main.control.more=\u66F4\u591A
@ -13,6 +14,10 @@ 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-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=\u6B63\u5728\u542F\u52A8 AList
alist.status.stop=\u6B63\u5728\u505C\u6B62AList
alist.status.stop.stopped=AList \u5DF2\u505C\u6B62
setup.proxy=HTTP\u4EE3\u7406 setup.proxy=HTTP\u4EE3\u7406
setup.auto-start.label=\u5F00\u673A\u81EA\u542F setup.auto-start.label=\u5F00\u673A\u81EA\u542F
setup.silent-startup.label=\u9759\u9ED8\u542F\u52A8 setup.silent-startup.label=\u9759\u9ED8\u542F\u52A8

View File

@ -6,6 +6,7 @@ root.foot.doc=Document
root.foot.github=GitHub root.foot.github=GitHub
root.foot.sponsor=Sponsor AList root.foot.sponsor=Sponsor AList
main.control.start=Start main.control.start=Start
main.control.stop=Stop
main.control.password=Password main.control.password=Password
main.control.restart=Restart main.control.restart=Restart
main.control.more=More main.control.more=More
@ -13,6 +14,10 @@ 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-log=Open the log folder main.more.open-log=Open the log folder
alist.status.start=Starting AList
alist.status.start.running=AList is running...
alist.status.stop=Stopping AList
alist.status.stop.stopped=AList has stopped
setup.proxy=HTTP PROXY setup.proxy=HTTP PROXY
setup.auto-start.label=Auto start with PC setup.auto-start.label=Auto start with PC
setup.silent-startup.label=Silent startup setup.silent-startup.label=Silent startup

View File

@ -6,6 +6,7 @@ root.foot.doc=\u6587\u6863
root.foot.github=GitHub root.foot.github=GitHub
root.foot.sponsor=\u8D5E\u52A9 AList root.foot.sponsor=\u8D5E\u52A9 AList
main.control.start=\u5F00\u59CB main.control.start=\u5F00\u59CB
main.control.stop=\u505C\u6B62
main.control.password=\u5BC6\u7801 main.control.password=\u5BC6\u7801
main.control.restart=\u91CD\u542F main.control.restart=\u91CD\u542F
main.control.more=\u66F4\u591A main.control.more=\u66F4\u591A
@ -13,6 +14,10 @@ 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-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.start=\u6B63\u5728\u542F\u52A8 AList
alist.status.stop=\u6B63\u5728\u505C\u6B62AList
alist.status.start.running=AList \u6B63\u5728\u8FD0\u884C\u3002\u3002\u3002
setup.proxy=HTTP\u4EE3\u7406 setup.proxy=HTTP\u4EE3\u7406
setup.auto-start.label=\u5F00\u673A\u81EA\u542F setup.auto-start.label=\u5F00\u673A\u81EA\u542F
setup.silent-startup.label=\u9759\u9ED8\u542F\u52A8 setup.silent-startup.label=\u9759\u9ED8\u542F\u52A8