├── LICENSE ├── README.md ├── Sec-MS-GEC-ProxyServer ├── config.json ├── index.html ├── pom.xml └── src │ └── main │ └── java │ └── mai_onsyn │ └── gecProxy │ ├── Constants.java │ ├── Getter.java │ ├── Main.java │ └── Server.java ├── lib ├── assembly.xml ├── javafxblur.dll ├── javafxblur.jar └── srt-library.jar ├── pom.xml └── src ├── main ├── header │ └── com_kieferlam_javafxblur_NativeBlur.h ├── java │ ├── com │ │ └── kieferlam │ │ │ └── javafxblur │ │ │ ├── Blur.java │ │ │ └── NativeBlur.java │ └── mai_onsyn │ │ ├── AnimeFX │ │ ├── Frame │ │ │ ├── Layout │ │ │ │ ├── AutoPane.java │ │ │ │ ├── NamePopup.java │ │ │ │ └── PopupMenu.java │ │ │ ├── Module │ │ │ │ ├── Assistant │ │ │ │ │ ├── Cell.java │ │ │ │ │ ├── DataCell.java │ │ │ │ │ └── Item.java │ │ │ │ ├── DiffusionButton.java │ │ │ │ ├── FXLogger.java │ │ │ │ ├── SmoothChoiceBox.java │ │ │ │ ├── SmoothProgressBar.java │ │ │ │ ├── SmoothSlider.java │ │ │ │ ├── SmoothSwitch.java │ │ │ │ ├── SmoothTextArea.java │ │ │ │ ├── SmoothTextField.java │ │ │ │ └── SmoothTreeView.java │ │ │ ├── Styles │ │ │ │ ├── ButtonStyle.java │ │ │ │ ├── CellStyle.java │ │ │ │ ├── DefaultButtonStyle.java │ │ │ │ ├── DefaultCellStyle.java │ │ │ │ ├── DefaultNamePopupStyle.java │ │ │ │ └── NamePopupStyle.java │ │ │ └── Utils │ │ │ │ ├── DiffusionButtonGroup.java │ │ │ │ └── Toolkit.java │ │ └── Main.java │ │ ├── AnimeFX2 │ │ ├── AutoUpdatable.java │ │ ├── LanguageManager.java │ │ ├── LanguageSwitchable.java │ │ ├── Module │ │ │ ├── AXBackGround.java │ │ │ ├── AXBase.java │ │ │ ├── AXButton.java │ │ │ ├── AXChoiceBox.java │ │ │ ├── AXLogger.java │ │ │ ├── AXProgressBar.java │ │ │ ├── AXSlider.java │ │ │ ├── AXSwitch.java │ │ │ ├── AXTextArea.java │ │ │ ├── AXTextField.java │ │ │ └── AXTreeView.java │ │ ├── ResourceManager.java │ │ ├── Styles │ │ │ ├── AXBackGroundStyle.java │ │ │ ├── AXBaseStyle.java │ │ │ ├── AXButtonStyle.java │ │ │ ├── AXChoiceBoxStyle.java │ │ │ ├── AXContextPaneStyle.java │ │ │ ├── AXLangLabelStyle.java │ │ │ ├── AXProgressBarStyle.java │ │ │ ├── AXSliderStyle.java │ │ │ ├── AXSwitchStyle.java │ │ │ ├── AXTextAreaStyle.java │ │ │ ├── AXTextFieldStyle.java │ │ │ ├── AXTextInputPopupStyle.java │ │ │ ├── AXTreeItemStyle.java │ │ │ ├── AXTreeViewStyle.java │ │ │ ├── DefaultAXBackGroundStyle.java │ │ │ ├── DefaultAXBaseStyle.java │ │ │ ├── DefaultAXButtonGroupStyle.java │ │ │ ├── DefaultAXButtonStyle.java │ │ │ ├── DefaultAXChoiceBoxStyle.java │ │ │ ├── DefaultAXContextPaneStyle.java │ │ │ ├── DefaultAXLangLabelStyle.java │ │ │ ├── DefaultAXProgressBarStyle.java │ │ │ ├── DefaultAXSliderStyle.java │ │ │ ├── DefaultAXSwitchStyle.java │ │ │ ├── DefaultAXTextAreaStyle.java │ │ │ ├── DefaultAXTextFieldStyle.java │ │ │ ├── DefaultAXTextInputPopupStyle.java │ │ │ ├── DefaultAXTreeItemStyle.java │ │ │ └── DefaultAXTreeViewStyle.java │ │ ├── ThemeManager.java │ │ ├── Utls │ │ │ ├── AXButtonGroup.java │ │ │ ├── AXDataTreeItem.java │ │ │ ├── AXDatableButtonGroup.java │ │ │ ├── AXFloatTextField.java │ │ │ ├── AXIntegerTextField.java │ │ │ ├── AXLangLabel.java │ │ │ ├── AXTreeItem.java │ │ │ └── Toolkit.java │ │ └── layout │ │ │ ├── AXContextPane.java │ │ │ ├── AXScrollPane.java │ │ │ ├── AXTextInputPopup.java │ │ │ ├── AutoPane.java │ │ │ ├── HDoubleSplitPane.java │ │ │ └── VDoubleSplitPane.java │ │ ├── VeloVoice │ │ ├── App │ │ │ ├── AppConfig.java │ │ │ ├── ConfigListener.java │ │ │ ├── Constants.java │ │ │ ├── Runtime.java │ │ │ └── Theme.java │ │ ├── ConsoleApp.java │ │ ├── FrameApp.java │ │ ├── FrameFactory.java │ │ ├── Main.java │ │ ├── NetWork │ │ │ ├── Crawler │ │ │ │ ├── HttpFactory.java │ │ │ │ ├── NovelSite.java │ │ │ │ └── Websites │ │ │ │ │ ├── Kakuyomu.java │ │ │ │ │ ├── LiNovel.java │ │ │ │ │ └── WenKu8.java │ │ │ ├── TTS │ │ │ │ ├── ChatTTSClient.java │ │ │ │ ├── EdgeTTSClient.java │ │ │ │ ├── FixedEdgeTTSClient.java │ │ │ │ └── TTSClient.java │ │ │ ├── TTSExecutor.java │ │ │ └── Voice.java │ │ ├── Text │ │ │ ├── TTS.java │ │ │ ├── TextFactory.java │ │ │ └── TextLoader.java │ │ └── Utils │ │ │ ├── AudioPlayer.java │ │ │ ├── Chapter.java │ │ │ ├── Structure.java │ │ │ ├── Util.java │ │ │ └── Volume.java │ │ └── VeloVoice2 │ │ ├── App │ │ ├── Config.java │ │ ├── ConfigManager.java │ │ ├── Constants.java │ │ ├── Resource.java │ │ └── Runtime.java │ │ ├── Audio │ │ ├── AfterEffects.java │ │ ├── AudioPlayer.java │ │ └── AudioSaver.java │ │ ├── FrameApp.java │ │ ├── FrameFactory │ │ ├── LocalTXTHeaderEditor2.java │ │ ├── LocalTXTHeadersEditor.java │ │ ├── LogFactory.java │ │ └── MainFactory.java │ │ ├── Main.java │ │ ├── NetWork │ │ ├── Item │ │ │ ├── LocalTXT.java │ │ │ └── Source.java │ │ ├── TTS │ │ │ ├── EdgeTTSVoice.java │ │ │ ├── FixedEdgeTTSClient.java │ │ │ └── TTSClient.java │ │ └── TTSPool.java │ │ └── Text │ │ ├── Sentence.java │ │ ├── TTS.java │ │ └── TextUtil.java └── resources │ ├── edgeTTS.json │ ├── lang │ ├── en_us.json │ ├── lang_info.json │ └── zh_cn.json │ ├── style.css │ ├── styles │ ├── scroll-pane.css │ ├── slider.css │ ├── text-area.css │ └── text-field.css │ └── textures │ ├── bg.png │ ├── close_dark.png │ ├── close_light.png │ ├── file_dark.png │ ├── file_light.png │ ├── folder_dark.png │ ├── folder_light.png │ ├── icon.png │ ├── icons │ ├── app │ │ └── horn.png │ ├── config.png │ ├── context_menu │ │ ├── clear.png │ │ ├── copy.png │ │ ├── cut.png │ │ ├── delete.png │ │ ├── down.png │ │ ├── expand.png │ │ ├── file_new.png │ │ ├── folder_new.png │ │ ├── paste.png │ │ ├── paste_append.png │ │ ├── rename.png │ │ ├── select_all.png │ │ ├── undo.png │ │ └── up.png │ ├── file.png │ ├── folder.png │ ├── fork.png │ └── triangle.png │ ├── maximize_dark.png │ ├── maximize_light.png │ ├── minimize_dark.png │ ├── minimize_light.png │ ├── root_dark.png │ ├── root_light.png │ ├── setting_dark.png │ └── setting_light.png └── test └── java ├── FrameLauncher.java ├── FrameTest.java ├── Frames └── TestButtonStyle.java ├── Test01.java ├── Test02.java ├── Test03.java └── Test04.java /README.md: -------------------------------------------------------------------------------- 1 | # This software is developed for Chinese users! 2 | 3 | ## 简介 4 | 5 | 这是一个用于将txt格式小说按章节目录形式拆解为文件目录结构,或从网络上爬取小说存储为文件目录结构,并通过Edge TTS(Edge浏览器的大声朗读功能)将章节文本转为mp3语音文件。 6 | - 7 | - 8 | 9 | ## 特点 10 | 11 | - **多线程TTS文本转语音**:64线程下40万字的文本转换仅需5分钟。(由于微软搞事情 我在最新版把最大线程限制到了8 但速度还是够用的) 12 | - **一键加载**:指定小说源,自动构建文本结构树。 13 | - **泛用性高**:可手动操作的文本结构树,不仅是小说朗读,还可轻松用于其他文字转语音的相关需求。 14 | 15 | ### api服务器部属 16 | 直接看专栏 17 | - 18 | 19 | ## 使用 20 | 21 | 程序使用JDK22开发(使用了未命名变量),因此至少需要JDK22运行。 22 | 23 | 1. 运行jar文件(首次启动会生成一个`settings.json`存储软件配置)。 24 | 2. 在左侧选择一种加载模式,然后在下方的输入框输入小说源(本地模式需要的是txt文件所在目录路径,会读取目录下所有`.txt`后缀的文件),点击加载,即可在"ROOT"下看到小说的结构。 25 | - 注意:加载模式要选对,否则可能会无反应或者加载出空目录! 26 | - 当前支持的小说来源: 27 | - **本地(文件)**:直接加载txt文件,不进行解析。 28 | - **本地(全集)**:需要整个系列的小说txt文件,格式需要与轻小说文库的全本下载一致。 29 | - **本地(分卷)**:需要一本书的体量的txt文件,格式需要与轻小说文库的分卷下载一致。 30 | - **轻小说文库**():需要小说主页或目录页的地址。 31 | - **轻之文库** ():需要小说主页或目录页的地址。 32 | 4. 点击文本图标的项目,即可在中间的文本区域查看或修改其内容。 33 | 5. 可在右侧的操作面板修改TTS相关配置。 34 | 35 | 若一切准备就绪,点击"开始"即可开始TTS转换,下方会有进度提示。 36 | 37 | ## 注意事项 38 | 39 | - 线程不要设置过多,更多的线程不仅不会更快,反而会增加重连频率,降低转换速度。64线程基本就达到饱和了。 40 | - 若是遇到Bug,请尽可能提供反馈,这是我独立开发的软件,缺乏测试,Bug肯定满天飞。 41 | - 使用**轻之文库**的在线加载时,重试次数建议拉满,这个网站访问非常慢。 42 | - 请结合网络小说的名称仔细考虑是否开启**添加额外章节/卷名**。 43 | 44 | ## 设置详解 45 | 46 | ### 图形 47 | 48 | - **主题** - 切换亮色与暗色模式,亮色模式下调整背景亮度是越大越亮,暗色模式则是越大越暗 —— 重启生效 49 | - **主题色** - 影响整个软件的颜色 —— 重启生效 50 | - **Windows UI** - 启用透明与模糊的窗口,(不稳定,但还算好看) —— 重启生效 51 | - **Blur Mode** - 背景的模糊样式 —— 立即生效(有bug,具体看release) —— 立即生效 52 | - **背景图片** - 设置背景图片,可输入本地图片或网络图片,网络图片地址尽量复制粘贴,不然每次输入文字都会很卡 —— 立即生效 53 | - **背景模糊度** 与 **背景亮/暗度** - 背景图片的设置 —— 立即生效 54 | 55 | ### 网络 56 | 57 | - **连接线程数** - 转换时建立多少个TTS线程 —— 立即生效 58 | - **重试次数** - 在TTS连接出现问题时重试多少次,若是超出这个次数将会放弃某个句子的转换(或直接报错 - 一般不会)—— 立即生效 59 | - **超时时间** - 一个TTS请求的最大等待时间,超时会触发重连 —— 立即生效 60 | 61 | ### 文本 62 | 63 | - **切片最大长度** - 文本切片的大小,也就是一次向TTS发送的文本最大字符数量,越大越快,但也越容易出问题 —— 立即生效 64 | - **切片首选符号** - 文本切片的判断符号,优先会使用换行符切割文本,若是切片大小还是大于**切片最大长度**,就会按这些符号优先从左到右查找尝试切割文本,若都没有,将会强制切割(强制断句)。注意:不要设置逗号“,” 因为这样会导致生成的语音衔接极其不自然! —— 立即生效 65 | - **章节名添加卷名** - 仅针对从网络加载的小说生效,在每个章节的名字前添加卷名,如果你使用MP3,这将会很有用。本地小说不支持是因为本地txt的章节名自带卷名 —— 立即生效 66 | - **文件名添加序号** - 若是一个目录下的文件数量大于1,就添加序号,将其保持与文本结构树的顺序一致。因为小说的名字在大多数排序方式下都是混乱的,所以不管是用什么设备听,这个选项都很有必要 —— 立即生效 67 | - **添加额外章节名** - 为从网络加载的小说的章节名前额外添加形如**第1章**,**第2章**这样的名称 —— 立即生效 68 | - **添加额外章节名** - 为从网络加载的小说的卷名前额外添加形如**第1卷**,**第2卷**这样的名称 —— 立即生效 69 | 70 | ### 音频 71 | - **试听文本** - 试听时使用的文本 —— 立即生效 72 | - **分段保存音频** - 按**分段时长**设置的时间拆分一个音频文件 —— 立即生效 73 | - **分段添加名称** - 为每个分段的音频(不包括第一个分段)添加第一个分段音频中的第一句话(单个File的第一个文本切片,一般为软件中文本文件的第一行,转换小说时可以理解为该章节的标题) —— 立即生效 74 | - **分段时长** - 每个音频分段的最大时长(单位:分钟) 75 | 76 | ## 下载 77 | 78 | 都来这了,Releases页面不正是个很好的地方吗?\ 79 | 不过蓝奏盘也是推荐的: 密码:14ix\ 80 | 如果你心情很好或者有钞能力也可以选择去这里下载: 81 | - 注:Test-v0.2及之后的版本需要运行此软件的java的bin目录下有项目lib文件夹里的javafxblur.dll,否则Windows 10 April 2018 Update (1803, build 17134)或更高的版本可能会出错,要是嫌麻烦还请直接下带jre运行库的zip压缩包。 82 | -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "EdgePath": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", 3 | "ServerPort": 44165, 4 | "MITMProxyPort": 22165, 5 | "FlushPeriod": 10000 6 | } -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 自动循环播放语音 7 | 8 | 9 | 10 |
11 |

天王盖地虎,

12 |

宝塔镇河妖。

13 |

三国无双,

14 |

一代天骄,

15 |

一代宗师。

16 |

我爱你中国

17 |

亲爱的母亲

18 |
19 | 20 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | Sec-MS-GEC-ProxyServer 9 | 0.1 10 | 11 | 12 | 18 13 | 18 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | com.alibaba 21 | fastjson 22 | 1.2.83 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-assembly-plugin 32 | 33 | 34 | 35 | 36 | mai_onsyn.gecProxy.Main 37 | 38 | 39 | 40 | 41 | jar-with-dependencies 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/src/main/java/mai_onsyn/gecProxy/Constants.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.gecProxy; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.util.Objects; 9 | 10 | public class Constants { 11 | 12 | public static int mitmproxyPort; 13 | public static int serverPort; 14 | public static int flushPeriod; 15 | public static String edgePath; 16 | public static String htmlPath = System.getProperty("user.dir") + "\\index.html"; 17 | 18 | static { 19 | try { 20 | JSONObject json = JSONObject.parseObject(Files.readString(new File(System.getProperty("user.dir") + "\\config.json").toPath())); 21 | 22 | mitmproxyPort = json.getInteger("MITMProxyPort"); 23 | serverPort = json.getInteger("ServerPort"); 24 | edgePath = json.getString("EdgePath"); 25 | flushPeriod = json.getInteger("FlushPeriod"); 26 | 27 | } catch (IOException e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/src/main/java/mai_onsyn/gecProxy/Getter.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.gecProxy; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | 7 | public class Getter { 8 | 9 | private final Thread mitmproxyThread; 10 | private final Thread edgeSwitch; 11 | 12 | private String Sec_MS_GEC = ""; 13 | private String Sec_MS_GEC_Version = ""; 14 | 15 | public Getter() { 16 | mitmproxyThread = new Thread(this::launchMitmproxy, "MITM-Thread"); 17 | edgeSwitch = new Thread(() -> { 18 | try { 19 | launchEdge(); 20 | while (!Thread.currentThread().isInterrupted()) { 21 | Thread.sleep(Constants.flushPeriod); 22 | closeEdge(); 23 | Thread.sleep(500); 24 | launchEdge(); 25 | } 26 | closeEdge(); 27 | } catch (InterruptedException ignored) {} 28 | }, "EdgeSwitch-Thread"); 29 | } 30 | 31 | public String get() { 32 | return String.format(""" 33 | { 34 | "Sec-MS-GEC": "%s", 35 | "Sec-MS-GEC-Version": "%s" 36 | } 37 | """, Sec_MS_GEC, Sec_MS_GEC_Version); 38 | } 39 | 40 | public void stop() { 41 | mitmproxyThread.interrupt(); 42 | edgeSwitch.interrupt(); 43 | } 44 | 45 | public void start() { 46 | mitmproxyThread.start(); 47 | edgeSwitch.start(); 48 | } 49 | 50 | public void launchEdge() { 51 | ProcessBuilder processBuilder = new ProcessBuilder(Constants.edgePath, Constants.htmlPath); 52 | 53 | try { 54 | processBuilder.start(); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | 60 | private void closeEdge() { 61 | ProcessBuilder processBuilder = new ProcessBuilder("taskkill", "-im", "msedge.exe"); 62 | 63 | try { 64 | processBuilder.start(); 65 | } catch (IOException e) { 66 | throw new RuntimeException(e); 67 | } 68 | } 69 | 70 | private void launchMitmproxy() { 71 | 72 | 73 | ProcessBuilder processBuilder = new ProcessBuilder("mitmdump", "--listen-port", String.valueOf(Constants.mitmproxyPort)); 74 | Process mitmproxyProcess = null; 75 | 76 | try { 77 | processBuilder.redirectErrorStream(true); 78 | mitmproxyProcess = processBuilder.start(); 79 | 80 | BufferedReader reader = new BufferedReader(new InputStreamReader(mitmproxyProcess.getInputStream())); 81 | String line; 82 | 83 | while (!Thread.currentThread().isInterrupted()) { 84 | if ((line = reader.readLine()) == null) continue; 85 | 86 | if (line.contains("Sec-MS-GEC")) { 87 | 88 | int index1 = line.indexOf("Sec-MS-GEC="); 89 | int index2 = line.indexOf("&Sec-MS-GEC-Version="); 90 | int index3 = line.indexOf("&ConnectionId="); 91 | 92 | Sec_MS_GEC = line.substring(index1 + 11, index2); 93 | Sec_MS_GEC_Version = line.substring(index2 + 20, index3); 94 | } 95 | } 96 | 97 | 98 | } catch (IOException e) { 99 | throw new RuntimeException(e); 100 | } finally { 101 | assert mitmproxyProcess != null; 102 | mitmproxyProcess.destroy(); 103 | closeEdge(); 104 | System.out.println("mitmproxy stopped."); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/src/main/java/mai_onsyn/gecProxy/Main.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.gecProxy; 2 | 3 | import java.util.Objects; 4 | import java.util.Scanner; 5 | 6 | public class Main { 7 | 8 | 9 | public static void main(String[] args) { 10 | 11 | Getter getter = new Getter(); 12 | Server server = new Server(getter, Constants.serverPort); 13 | getter.start(); 14 | 15 | Thread serverThread = new Thread(server::start, "Server-Thread"); 16 | 17 | Thread cmdThread = new Thread(() -> { 18 | Scanner scanner = new Scanner(System.in); 19 | System.out.println("Type \"exit\" to close the server"); 20 | 21 | while (true) { 22 | String command = scanner.next(); 23 | if (Objects.equals(command, "exit")) { 24 | serverThread.interrupt(); 25 | server.stop(); 26 | System.out.println("closing mitmproxy..."); 27 | getter.stop(); 28 | break; 29 | } 30 | } 31 | }, "cmd-Thread"); 32 | 33 | serverThread.start(); 34 | cmdThread.start(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Sec-MS-GEC-ProxyServer/src/main/java/mai_onsyn/gecProxy/Server.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.gecProxy; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import com.sun.net.httpserver.HttpHandler; 5 | import com.sun.net.httpserver.HttpServer; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.net.InetSocketAddress; 10 | 11 | public class Server { 12 | private final Getter responseBody; 13 | private final HttpServer server; 14 | 15 | public Server(Getter responseBody, int port) { 16 | this.responseBody = responseBody; 17 | try { 18 | server = HttpServer.create(new InetSocketAddress(port), 0); 19 | } catch (IOException e) { 20 | throw new RuntimeException(e); 21 | } 22 | configureServer(); 23 | } 24 | 25 | // 配置服务器 26 | private void configureServer() { 27 | server.createContext("/api", exchange -> { 28 | if ("GET".equals(exchange.getRequestMethod())) { 29 | String response = responseBody.get(); 30 | 31 | exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=UTF-8"); 32 | exchange.sendResponseHeaders(200, response.getBytes().length); 33 | 34 | try (OutputStream os = exchange.getResponseBody()) { 35 | os.write(response.getBytes()); 36 | } 37 | } else { 38 | exchange.sendResponseHeaders(405, -1); // 405 Method Not Allowed 39 | } 40 | }); 41 | } 42 | 43 | // 启动服务器 44 | public void start() { 45 | server.start(); 46 | System.out.println("Server started. Access it at http://localhost:" + server.getAddress().getPort() + "/api"); 47 | } 48 | 49 | // 停止服务器 50 | public void stop() { 51 | server.stop(0); 52 | System.out.println("Server stopped."); 53 | } 54 | } -------------------------------------------------------------------------------- /lib/assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | release 5 | 6 | jar 7 | 8 | false 9 | 10 | 11 | / 12 | true 13 | true 14 | runtime 15 | 16 | 17 | / 18 | true 19 | true 20 | system 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/javafxblur.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/lib/javafxblur.dll -------------------------------------------------------------------------------- /lib/javafxblur.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/lib/javafxblur.jar -------------------------------------------------------------------------------- /lib/srt-library.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/lib/srt-library.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | mai_onsyn.VeloVoice 8 | VeloVoice 9 | 1.0-dev-1 10 | 11 | 12 | 22 13 | 22 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | org.java-websocket 21 | Java-WebSocket 22 | 1.5.6 23 | 24 | 25 | 26 | com.alibaba 27 | fastjson 28 | 1.2.83 29 | 30 | 31 | 32 | com.googlecode.juniversalchardet 33 | juniversalchardet 34 | 1.0.3 35 | 36 | 37 | 38 | org.jsoup 39 | jsoup 40 | 1.15.3 41 | 42 | 43 | 44 | org.openjfx 45 | javafx-fxml 46 | 22.0.1 47 | 48 | 49 | 50 | javazoom 51 | jlayer 52 | 1.0.1 53 | 54 | 55 | 56 | 57 | com.positiondev.epublib 58 | epublib-core 59 | 3.1 60 | 61 | 62 | 63 | com.jonathanedgecombe 64 | srt-library 65 | 1.0 66 | system 67 | ${project.basedir}/lib/srt-library.jar 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-assembly-plugin 79 | 80 | 81 | 82 | 83 | mai_onsyn.VeloVoice2.Main 84 | 85 | 86 | 87 | jar-with-dependencies 88 | 89 | 90 | 91 | ${project.basedir}/lib/assembly.xml 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/header/com_kieferlam_javafxblur_NativeBlur.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_kieferlam_javafxblur_NativeBlur */ 4 | 5 | #ifndef _Included_com_kieferlam_javafxblur_NativeBlur 6 | #define _Included_com_kieferlam_javafxblur_NativeBlur 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_kieferlam_javafxblur_NativeBlur 12 | * Method: _extApplyBlur 13 | * Signature: (Ljava/lang/String;I)V 14 | */ 15 | JNIEXPORT void JNICALL Java_com_kieferlam_javafxblur_NativeBlur__1extApplyBlur 16 | (JNIEnv *, jobject, jstring, jint); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /src/main/java/com/kieferlam/javafxblur/Blur.java: -------------------------------------------------------------------------------- 1 | package com.kieferlam.javafxblur; 2 | 3 | import javafx.stage.Stage; 4 | 5 | /** 6 | * Singleton handler enum class Blur. 7 | * This class provides global methods to load and apply blur effects to a JavaFX stage. 8 | */ 9 | public enum Blur { 10 | NONE(0), BLUR_BEHIND(3), ACRYLIC(4); 11 | private final int accentState; 12 | 13 | Blur(int accentState) { 14 | this.accentState = accentState; 15 | } 16 | 17 | private static final String BLUR_TARGET_PREFIX = "_JFX"; 18 | private static final NativeBlur _extBlur = new NativeBlur(); 19 | 20 | /** 21 | * Loads the required blur library. 22 | * This should be called at the very start of your main function. 23 | * The "javafxblur" library file should be added to your library path. 24 | */ 25 | public static void loadBlurLibrary(){ 26 | System.loadLibrary("javafxblur"); 27 | } 28 | 29 | private static void _extApplyBlur(String target, int accentState){ 30 | _extBlur._extApplyBlur(target, accentState); 31 | } 32 | 33 | /** 34 | * Calls the external (native) function to apply the blur effect to a JavaFX stage. 35 | * The JavaFX stage must be visible before this function is called. 36 | * If the stage is ever hidden (destroyed, not minimised), this function must be called again once visible. 37 | * @param stage 38 | */ 39 | public static void applyBlur(Stage stage, Blur blur){ 40 | if (!stage.isShowing()) System.err.println("Warning: blur effect was called on a hidden stage!"); 41 | String stageTitle = stage.getTitle(); 42 | String targetTitle = BLUR_TARGET_PREFIX + (System.currentTimeMillis() % 1000); 43 | stage.setTitle(targetTitle); 44 | _extApplyBlur(targetTitle, blur.accentState); 45 | stage.setTitle(stageTitle); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/kieferlam/javafxblur/NativeBlur.java: -------------------------------------------------------------------------------- 1 | package com.kieferlam.javafxblur; 2 | 3 | public class NativeBlur { 4 | 5 | protected NativeBlur(){} 6 | 7 | protected native void _extApplyBlur(String target, int accentState); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Layout/NamePopup.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Layout; 2 | 3 | import javafx.beans.value.ChangeListener; 4 | import javafx.event.EventHandler; 5 | import javafx.geometry.Pos; 6 | import javafx.scene.control.Label; 7 | import javafx.scene.control.TextField; 8 | import javafx.scene.input.KeyCode; 9 | import javafx.scene.layout.StackPane; 10 | import javafx.scene.layout.VBox; 11 | import javafx.scene.paint.Color; 12 | import javafx.scene.shape.Rectangle; 13 | import javafx.scene.text.Font; 14 | import javafx.stage.Popup; 15 | import javafx.stage.Stage; 16 | import mai_onsyn.AnimeFX.Frame.Module.SmoothTextField; 17 | import mai_onsyn.AnimeFX.Frame.Styles.DefaultNamePopupStyle; 18 | import mai_onsyn.AnimeFX.Frame.Styles.NamePopupStyle; 19 | 20 | public class NamePopup extends Popup { 21 | private static Stage stage; 22 | public static void setStage(Stage s) { 23 | stage = s; 24 | } 25 | 26 | private final Rectangle borderClip; 27 | 28 | private NamePopupStyle popupStyle = new DefaultNamePopupStyle(); 29 | private String description; 30 | private double width, height; 31 | 32 | public NamePopup borderShape(double v1) { 33 | borderClip.setArcWidth(v1); 34 | borderClip.setArcHeight(v1); 35 | return this; 36 | } 37 | public NamePopup popupStyle(NamePopupStyle popupStyle) { 38 | this.popupStyle = popupStyle; 39 | return this; 40 | } 41 | private SmoothTextField textField; 42 | 43 | public NamePopup init() { 44 | 45 | textField = popupStyle.createTextField(); 46 | 47 | Label label = popupStyle.createDescription(description); 48 | 49 | textField.setMaxWidth(width * 0.8); 50 | // 设置垂直居中排列 51 | VBox box = new VBox(); 52 | box.setAlignment(Pos.CENTER); 53 | box.setSpacing(10); 54 | box.getChildren().addAll(label, textField); 55 | 56 | // 设置背景面板 57 | Rectangle background = popupStyle.createBackground(width, height); 58 | //background.setFill(Color.LIGHTGRAY); 59 | 60 | StackPane stackPane = new StackPane(); 61 | stackPane.setPrefSize(width, height); 62 | stackPane.getChildren().addAll(background, box); 63 | stackPane.setClip(borderClip); 64 | stackPane.setOnMouseClicked(_ -> stackPane.requestFocus()); 65 | 66 | // 设置文本框的事件监听器,当按下Enter时关闭弹窗 67 | textField.setOnKeyPressed(event -> { 68 | if (event.getCode() == KeyCode.ENTER && !textField.getTextField().getText().isEmpty()) { 69 | if (onTextAvailable != null) { 70 | onTextAvailable.changed(null, null, textField.getTextField().getText()); 71 | } 72 | this.hide(); 73 | } 74 | }); 75 | 76 | // 将StackPane添加到Popup中 77 | getContent().add(stackPane); 78 | 79 | stage.focusedProperty().addListener((o, ov, nv) -> { 80 | if (!nv) { 81 | super.hide(); 82 | } 83 | }); 84 | 85 | stage.xProperty().addListener((o, ov, nv) -> { 86 | if (super.isShowing()) { 87 | super.hide(); 88 | } 89 | }); 90 | 91 | stage.yProperty().addListener((o, ov, nv) -> { 92 | if (super.isShowing()) { 93 | super.hide(); 94 | } 95 | }); 96 | 97 | stage.getScene().addEventFilter(javafx.scene.input.MouseEvent.MOUSE_CLICKED, e -> { 98 | if (!super.getContent().getFirst().getBoundsInLocal().contains(e.getSceneX(), e.getSceneY())) { 99 | super.hide(); // 点击在Popup之外,隐藏Popup 100 | } 101 | }); 102 | 103 | return this; 104 | } 105 | 106 | private ChangeListener onTextAvailable; 107 | 108 | public NamePopup(String description, double width, double height) { 109 | this.width = width; 110 | this.height = height; 111 | this.description = description; 112 | borderClip = new Rectangle(width, height); 113 | } 114 | 115 | public void showOnCenter() { 116 | if (stage != null) { 117 | double centerX = stage.getX() + stage.getWidth() / 2; 118 | double centerY = stage.getY() + stage.getHeight() / 2; 119 | // 将Popup的左上角移到计算的中心位置,因此需要减去Popup的一半宽高 120 | show(stage, centerX - width / 2, centerY - height / 2); 121 | } 122 | } 123 | 124 | public void setOnTextAvailable(ChangeListener listener) { 125 | this.onTextAvailable = listener; 126 | } 127 | 128 | public TextField getTextField() { 129 | return textField.getTextField(); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Module/Assistant/DataCell.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Module.Assistant; 2 | 3 | import javafx.scene.image.Image; 4 | import javafx.scene.paint.Color; 5 | import javafx.scene.text.Font; 6 | 7 | public class DataCell extends Cell { 8 | 9 | private T data; 10 | 11 | public DataCell(double depth, double offset, Image image, String text) { 12 | super(depth, offset, image, text); 13 | } 14 | 15 | public DataCell setData(T data) { 16 | this.data = data; 17 | return this; 18 | } 19 | 20 | public T getData() { 21 | return data; 22 | } 23 | 24 | 25 | 26 | 27 | 28 | public DataCell borderColor(Color color) { 29 | super.borderColor(color); 30 | return this; 31 | } 32 | public DataCell borderShape(double v1) { 33 | super.borderShape(v1); 34 | return this; 35 | } 36 | public DataCell font(Font font) { 37 | super.font(font); 38 | return this; 39 | } 40 | public DataCell width(double width) { 41 | super.width(width); 42 | return this; 43 | } 44 | public DataCell height(double height) { 45 | super.height(height); 46 | return this; 47 | } 48 | public DataCell bgColor(Color bgColor) { 49 | super.bgColor(bgColor); 50 | return this; 51 | } 52 | public DataCell animeDuration(int animeDuration) { 53 | super.animeDuration(animeDuration); 54 | return this; 55 | } 56 | public DataCell textColor(Color textColor) { 57 | super.textColor(textColor); 58 | return this; 59 | } 60 | public DataCell ftImageXOffset(double ftImageXOffset) { 61 | super.ftImageXOffset(ftImageXOffset); 62 | return this; 63 | } 64 | public DataCell ftImageYOffset(double ftImageYOffset) { 65 | super.ftImageYOffset(ftImageYOffset); 66 | return this; 67 | } 68 | public DataCell textXOffset(double textXOffset) { 69 | super.textXOffset(textXOffset); 70 | return this; 71 | } 72 | public DataCell textYOffset(double textYOffset) { 73 | super.textYOffset(textYOffset); 74 | return this; 75 | } 76 | public DataCell align(int align) { 77 | super.align(align); 78 | return this; 79 | } 80 | public DataCell bgImage(Image bgImage) { 81 | super.bgImage(bgImage); 82 | return this; 83 | } 84 | public DataCell ftImage(Image ftImage) { 85 | super.ftImage(ftImage); 86 | return this; 87 | } 88 | public DataCell ftImageRatio(double v) { 89 | super.ftImageRatio(v); 90 | return this; 91 | } 92 | public DataCell name(String description) { 93 | super.name(description); 94 | return this; 95 | } 96 | public DataCell borderRadius(double r) { 97 | super.borderRadius(r); 98 | return this; 99 | } 100 | public DataCell textFocusColor(Color textFocusColor) { 101 | super.textFocusColor(textFocusColor); 102 | return this; 103 | } 104 | public DataCell bgFocusColor(Color backGroundFocusColor) { 105 | super.bgFocusColor(backGroundFocusColor); 106 | return this; 107 | } 108 | public DataCell fillColor(Color fillColor) { 109 | super.fillColor(fillColor); 110 | return this; 111 | } 112 | public DataCell init() { 113 | super.init(); 114 | return this; 115 | } 116 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Module/Assistant/Item.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Module.Assistant; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Item { 7 | private final T data; 8 | private final List> children; 9 | 10 | public Item(T data) { 11 | this.data = data; 12 | this.children = new ArrayList<>(); 13 | } 14 | 15 | public void add(Item child) { 16 | children.add(child); 17 | } 18 | 19 | 20 | @SafeVarargs 21 | public final void addAll(Item... child) { 22 | children.addAll(List.of(child)); 23 | } 24 | 25 | public T getData() { 26 | return data; 27 | } 28 | 29 | public List> getChildren() { 30 | return children; 31 | } 32 | 33 | public Item lookup(T key) { 34 | if (this.data.equals(key)) return this; 35 | 36 | for (Item child : children) { 37 | Item result = child.lookup(key); 38 | if (result != null) { 39 | return result; 40 | } 41 | } 42 | 43 | return null; 44 | } 45 | 46 | public static void delete(Item root, Item children) { 47 | for (Item child : root.getChildren()) { 48 | if (child == children) { 49 | root.getChildren().remove(child); 50 | return; 51 | } 52 | else delete(child, children); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Module/FXLogger.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Module; 2 | 3 | import javafx.application.Platform; 4 | import javafx.scene.control.TextArea; 5 | import javafx.scene.paint.Color; 6 | import javafx.scene.text.Font; 7 | import mai_onsyn.AnimeFX.Frame.Styles.ButtonStyle; 8 | 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | public class FXLogger extends SmoothTextArea { 13 | public FXLogger borderRadius(double r) { 14 | super.borderRadius(r); 15 | return this; 16 | } 17 | public FXLogger borderColor(Color color) { 18 | super.borderColor(color); 19 | return this; 20 | } 21 | public FXLogger borderShape(double v1) { 22 | super.borderShape(v1); 23 | return this; 24 | } 25 | public FXLogger font(Font font) { 26 | super.font(font); 27 | return this; 28 | } 29 | public FXLogger width(double width) { 30 | super.width(width); 31 | return this; 32 | } 33 | public FXLogger height(double height) { 34 | super.height(height); 35 | return this; 36 | } 37 | public FXLogger bgColor(Color bgColor) { 38 | super.bgColor(bgColor); 39 | return this; 40 | } 41 | public FXLogger animeDuration(int animeDuration) { 42 | super.animeDuration(animeDuration); 43 | return this; 44 | } 45 | public FXLogger textHighlightBGColor(Color textHighlightBGColor) { 46 | super.textHighlightBGColor(textHighlightBGColor); 47 | return this; 48 | } 49 | public FXLogger textHighlightColor(Color textHighlightColor) { 50 | super.textHighlightColor(textHighlightColor); 51 | return this; 52 | } 53 | public FXLogger textColor(Color textColor) { 54 | super.textColor(textColor); 55 | return this; 56 | } 57 | public FXLogger init() { 58 | super.init(); 59 | return this; 60 | } 61 | public FXLogger buttonStyle(ButtonStyle style) { 62 | super.buttonStyle(style); 63 | return this; 64 | } 65 | 66 | public FXLogger logLevel(int level) { 67 | this.level = level; 68 | return this; 69 | } 70 | 71 | private final TextArea logMenu; 72 | private final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 73 | 74 | 75 | /** 76 | * 0 - error 77 | * 1 - warn 78 | * 2 - info 79 | * 3 - debug 80 | */ 81 | private int level = 3; 82 | 83 | public FXLogger() { 84 | super(); 85 | logMenu = super.getTextArea(); 86 | logMenu.setEditable(false); 87 | //logMenu.setOnMouseEntered(event -> logMenu.setCursor(Cursor.DEFAULT)); 88 | } 89 | 90 | private boolean firstLine = true; 91 | private void log(String s) { 92 | if (firstLine) { 93 | Platform.runLater(() -> logMenu.appendText("[" + format.format(new Date()) + "] " + s)); 94 | firstLine = false; 95 | } 96 | else Platform.runLater(() -> logMenu.appendText("\n[" + format.format(new Date()) + "] " + s)); 97 | } 98 | 99 | public void debug(String s) { 100 | if (level >= 3) log(s); 101 | } 102 | 103 | public void info(String s) { 104 | if (level >= 2) log(s); 105 | } 106 | 107 | public void warn(String s) { 108 | if (level >= 1) log(s); 109 | } 110 | 111 | public void error(String s) { 112 | log(s); 113 | } 114 | 115 | public void prompt(String s) { 116 | log(s); 117 | } 118 | 119 | public void cancel() { 120 | String text = logMenu.getText(); 121 | int lastIndex = text.lastIndexOf('\n'); 122 | if (lastIndex >= 0) { 123 | logMenu.setText(text.substring(0, lastIndex + 1)); 124 | } else { 125 | logMenu.clear(); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Styles/ButtonStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Styles; 2 | 3 | import mai_onsyn.AnimeFX.Frame.Module.DiffusionButton; 4 | 5 | public interface ButtonStyle { 6 | 7 | DiffusionButton createButton(String name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Styles/CellStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Styles; 2 | 3 | import mai_onsyn.AnimeFX.Frame.Module.Assistant.Cell; 4 | import mai_onsyn.AnimeFX.Frame.Module.SmoothTreeView; 5 | 6 | public interface CellStyle { 7 | Cell createFileCell(String name, SmoothTreeView treeView); 8 | Cell createFolderCell(String name, SmoothTreeView treeView); 9 | Cell createRootCell(String name, SmoothTreeView treeView); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Styles/DefaultButtonStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Styles; 2 | 3 | import mai_onsyn.AnimeFX.Frame.Module.DiffusionButton; 4 | 5 | public class DefaultButtonStyle implements ButtonStyle{ 6 | @Override 7 | public DiffusionButton createButton(String name) { 8 | return new DiffusionButton() 9 | .name(name) 10 | .height(30) 11 | .init(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Styles/DefaultCellStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Styles; 2 | 3 | import javafx.scene.image.Image; 4 | import javafx.scene.paint.Color; 5 | import mai_onsyn.AnimeFX.Frame.Module.Assistant.Cell; 6 | import mai_onsyn.AnimeFX.Frame.Module.SmoothTreeView; 7 | 8 | public class DefaultCellStyle implements CellStyle { 9 | @Override 10 | public Cell createFileCell(String name, SmoothTreeView treeView) { 11 | Cell cell = new Cell(20, 15, new Image("textures/file_light.png"), name) 12 | .bgColor(Color.TRANSPARENT) 13 | .borderShape(20) 14 | .init(); 15 | cell.setType(true); 16 | return cell; 17 | } 18 | 19 | @Override 20 | public Cell createFolderCell(String name, SmoothTreeView treeView) { 21 | return new Cell(20, 15, new Image("textures/folder_light.png"), name) 22 | .bgColor(Color.TRANSPARENT) 23 | .borderShape(20) 24 | .init(); 25 | } 26 | 27 | @Override 28 | public Cell createRootCell(String name, SmoothTreeView treeView) { 29 | return new Cell(20, 15, new Image("textures/root_light.png"), name) 30 | .bgColor(Color.TRANSPARENT) 31 | .borderShape(20) 32 | .init(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Styles/DefaultNamePopupStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Styles; 2 | 3 | import javafx.scene.control.Label; 4 | import javafx.scene.paint.Color; 5 | import javafx.scene.shape.Rectangle; 6 | import javafx.scene.text.Font; 7 | import mai_onsyn.AnimeFX.Frame.Module.SmoothTextField; 8 | 9 | public class DefaultNamePopupStyle implements NamePopupStyle{ 10 | @Override 11 | public SmoothTextField createTextField() { 12 | return new SmoothTextField() 13 | .borderRadius(1) 14 | .subLineColor(Color.GREEN) 15 | .font(new Font(15)) 16 | .borderShape(30) 17 | .init(); 18 | } 19 | 20 | @Override 21 | public Label createDescription(String description) { 22 | return new Label(description); 23 | } 24 | 25 | @Override 26 | public Rectangle createBackground(double width, double height) { 27 | Rectangle rectangle = new Rectangle(width, height); 28 | rectangle.setFill(Color.LIGHTGRAY); 29 | return rectangle; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Styles/NamePopupStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Styles; 2 | 3 | import javafx.scene.control.Label; 4 | import javafx.scene.shape.Rectangle; 5 | import mai_onsyn.AnimeFX.Frame.Layout.NamePopup; 6 | import mai_onsyn.AnimeFX.Frame.Module.SmoothTextField; 7 | 8 | public interface NamePopupStyle { 9 | 10 | SmoothTextField createTextField(); 11 | 12 | Label createDescription(String description); 13 | 14 | Rectangle createBackground(double width, double height); 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX/Frame/Utils/DiffusionButtonGroup.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX.Frame.Utils; 2 | 3 | import javafx.scene.input.MouseEvent; 4 | import javafx.scene.paint.Color; 5 | import mai_onsyn.AnimeFX.Frame.Module.DiffusionButton; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class DiffusionButtonGroup { 11 | 12 | private final List group = new ArrayList<>(); 13 | 14 | private DiffusionButton switchedNow; 15 | private DiffusionButton switchedLast; 16 | 17 | private Color switchedColor = new Color(0.1, 0.2, 1, 0.5); 18 | private Color switchedFocusColor = new Color(0.8, 0.4, 0.8, 0.5); 19 | private Color switchedTextColor = new Color(0.5, 1, 0.5, 0.5); 20 | private Color switchedTextFocusColor = new Color(1, 0.9, 0.9, 0.5); 21 | private Color freeColor; 22 | private Color freeFocusColor; 23 | private Color freeTextColor; 24 | private Color freeTextFocusColor; 25 | 26 | public DiffusionButtonGroup(DiffusionButton... buttons) { 27 | group.addAll(List.of(buttons)); 28 | if (buttons.length != 0) { 29 | freeColor = buttons[0].getBgColor(); 30 | freeFocusColor = buttons[0].getBgFocusColor(); 31 | freeTextColor = buttons[0].getTextColor(); 32 | freeTextFocusColor = buttons[0].getTextFocusColor(); 33 | } 34 | 35 | for (DiffusionButton button : buttons) { 36 | addEventListener(button); 37 | } 38 | } 39 | 40 | public void setSwitchedColor(Color switchedColor) { 41 | this.switchedColor = switchedColor; 42 | } 43 | public void setSwitchedFocusColor(Color switchedFocusColor) { 44 | this.switchedFocusColor = switchedFocusColor; 45 | } 46 | public void setSwitchedTextColor(Color switchedTextColor) { 47 | this.switchedTextColor = switchedTextColor; 48 | } 49 | public void setSwitchedTextFocusColor(Color switchedTextFocusColor) { 50 | this.switchedTextFocusColor = switchedTextFocusColor; 51 | } 52 | 53 | public DiffusionButton getSwitching() { 54 | return switchedNow; 55 | } 56 | 57 | public DiffusionButton getLastSwitching() { 58 | return switchedLast; 59 | } 60 | 61 | public List getAllButtons() { 62 | return group; 63 | } 64 | 65 | public void addAll(DiffusionButton... buttons) { 66 | if (buttons.length != 0) { 67 | freeColor = buttons[0].getBgColor(); 68 | freeFocusColor = buttons[0].getBgFocusColor(); 69 | freeTextColor = buttons[0].getTextColor(); 70 | freeTextFocusColor = buttons[0].getTextFocusColor(); 71 | } 72 | 73 | for (DiffusionButton button : buttons) { 74 | addEventListener(button); 75 | } 76 | group.addAll(List.of(buttons)); 77 | } 78 | 79 | public void add(DiffusionButton button) { 80 | freeColor = button.getBgColor(); 81 | freeFocusColor = button.getBgFocusColor(); 82 | freeTextColor = button.getTextColor(); 83 | freeTextFocusColor = button.getTextFocusColor(); 84 | 85 | addEventListener(button); 86 | 87 | group.add(button); 88 | } 89 | 90 | public void remove(DiffusionButton button) { 91 | group.remove(button); 92 | } 93 | 94 | private void addEventListener(DiffusionButton button) { 95 | button.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> { 96 | if (switchedNow == button) return; 97 | 98 | switchedLast = switchedNow; 99 | switchedNow = button; 100 | 101 | if (switchedLast != null) { 102 | switchedLast.setBgColor(freeColor); 103 | switchedLast.setBgFocusColor(freeFocusColor, false); 104 | switchedLast.setTextColor(freeTextColor, true); 105 | switchedLast.setTextFocusColor(freeTextFocusColor, false); 106 | } 107 | 108 | switchedNow.setBgColor(switchedColor); 109 | switchedNow.setBgFocusColor(switchedFocusColor, true); 110 | switchedNow.setTextColor(switchedTextColor, false); 111 | switchedNow.setTextFocusColor(switchedTextFocusColor, true); 112 | }); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/AutoUpdatable.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2; 2 | 3 | public interface AutoUpdatable { 4 | void update(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/LanguageSwitchable.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2; 2 | 3 | import java.util.Map; 4 | 5 | public interface LanguageSwitchable { 6 | 7 | void switchLanguage(String str); 8 | 9 | Map getLanguageElements(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Module/AXBase.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Module; 2 | 3 | import javafx.animation.KeyFrame; 4 | import javafx.animation.KeyValue; 5 | import javafx.animation.Timeline; 6 | import javafx.application.Platform; 7 | import javafx.scene.input.MouseEvent; 8 | import javafx.scene.paint.Color; 9 | import javafx.scene.shape.Rectangle; 10 | import javafx.util.Duration; 11 | import mai_onsyn.AnimeFX2.AutoUpdatable; 12 | import mai_onsyn.AnimeFX2.Styles.AXBaseStyle; 13 | import mai_onsyn.AnimeFX2.Styles.DefaultAXBaseStyle; 14 | import mai_onsyn.AnimeFX2.layout.AutoPane; 15 | import mai_onsyn.AnimeFX2.Utls.Toolkit; 16 | 17 | public class AXBase extends AutoPane implements AutoUpdatable { 18 | private AXBaseStyle style = new DefaultAXBaseStyle(); 19 | 20 | private final Rectangle border = new Rectangle(100, 50, style.getBGColor()); 21 | private final Rectangle shadow = new Rectangle(100, 50, Toolkit.adjustOpacity(style.getHoverShadow(), 0)); 22 | 23 | private Timeline shadowTimeline = new Timeline(); 24 | protected boolean isPressed = false; 25 | 26 | public AXBase() { 27 | 28 | border.setArcWidth(style.getBorderArcSize()); 29 | border.setArcHeight(style.getBorderArcSize()); 30 | 31 | border.setStroke(style.getBorderColor()); 32 | border.setStrokeWidth(style.getBorderRadius()); 33 | 34 | Rectangle borderClip = new Rectangle(); 35 | borderClip.widthProperty().bindBidirectional(border.widthProperty()); 36 | borderClip.heightProperty().bindBidirectional(border.heightProperty()); 37 | borderClip.arcWidthProperty().bindBidirectional(border.arcWidthProperty()); 38 | borderClip.arcHeightProperty().bindBidirectional(border.arcHeightProperty()); 39 | super.setClip(borderClip); 40 | shadow.widthProperty().bindBidirectional(border.widthProperty()); 41 | shadow.heightProperty().bindBidirectional(border.heightProperty()); 42 | shadow.arcWidthProperty().bindBidirectional(border.arcWidthProperty()); 43 | shadow.arcHeightProperty().bindBidirectional(border.arcHeightProperty()); 44 | 45 | border.widthProperty().bind(super.widthProperty()); 46 | border.heightProperty().bind(super.heightProperty()); 47 | 48 | super.addEventHandler(MouseEvent.MOUSE_ENTERED, _ -> { 49 | fireShadowTimeline(isPressed ? style.getPressedShadow() : style.getHoverShadow()); 50 | }); 51 | 52 | super.addEventHandler(MouseEvent.MOUSE_EXITED, _ -> { 53 | fireShadowTimeline(isPressed ? style.getPressedShadow() : Toolkit.adjustOpacity(style.getHoverShadow(), 0)); 54 | }); 55 | 56 | super.addEventHandler(MouseEvent.MOUSE_PRESSED, _ -> { 57 | isPressed = true; 58 | fireShadowTimeline(style.getPressedShadow()); 59 | }); 60 | 61 | super.addEventHandler(MouseEvent.MOUSE_RELEASED, _ -> { 62 | isPressed = false; 63 | fireShadowTimeline(isHover() ? style.getHoverShadow() : Toolkit.adjustOpacity(style.getHoverShadow(), 0)); 64 | }); 65 | 66 | super.getChildren().addAll(border, shadow); 67 | } 68 | 69 | private void fireShadowTimeline(Color target) { 70 | shadowTimeline.stop(); 71 | shadowTimeline = new Timeline(new KeyFrame(Duration.millis(200 * style.getAnimeRate()), 72 | new KeyValue(shadow.fillProperty(), target) 73 | )); 74 | shadowTimeline.play(); 75 | } 76 | 77 | @Override 78 | public void update() { 79 | Timeline timeline = new Timeline(new KeyFrame(Duration.millis(200 * style.getAnimeRate()), 80 | new KeyValue(border.fillProperty(), style.getBGColor()), 81 | new KeyValue(border.strokeProperty(), style.getBorderColor()), 82 | new KeyValue(border.strokeWidthProperty(), style.getBorderRadius()), 83 | new KeyValue(border.arcWidthProperty(), style.getBorderArcSize()), 84 | new KeyValue(border.arcHeightProperty(), style.getBorderArcSize()), 85 | new KeyValue(shadow.fillProperty(), isHover() ? style.getHoverShadow() : isPressed ? style.getPressedShadow() : Toolkit.adjustOpacity(style.getHoverShadow(), 0)) 86 | )); 87 | timeline.play(); 88 | 89 | Platform.runLater(super::flush); 90 | } 91 | 92 | public void setTheme(AXBaseStyle style) { 93 | this.style = style; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Module/AXLogger.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Module; 2 | 3 | import javafx.application.Platform; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | public class AXLogger extends AXTextArea { 9 | /** 10 | * 0 - error 11 | * 1 - warn 12 | * 2 - info 13 | * 3 - debug 14 | */ 15 | private int level = 3; 16 | 17 | private final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 18 | 19 | public AXLogger() { 20 | super(); 21 | super.setEditable(false); 22 | } 23 | 24 | private boolean firstLine = true; 25 | private void log(String s) { 26 | String log = String.format("[%s] %s", format.format(new Date()), s); 27 | if (firstLine) { 28 | Platform.runLater(() -> super.appendText(log)); 29 | firstLine = false; 30 | } 31 | else Platform.runLater(() -> super.appendText("\n" + log)); 32 | System.out.println(log); 33 | } 34 | 35 | public void debug(String s) { 36 | if (level >= 3) log(s); 37 | } 38 | 39 | public void info(String s) { 40 | if (level >= 2) log(s); 41 | } 42 | 43 | public void warn(String s) { 44 | if (level >= 1) log(s); 45 | } 46 | 47 | public void error(String s) { 48 | log(s); 49 | } 50 | 51 | public void prompt(String s) { 52 | log(s); 53 | } 54 | 55 | public void debug(String s, Object... args) { 56 | if (level >= 2) log(String.format(s, args)); 57 | } 58 | 59 | public void warn(String s, Object... args) { 60 | if (level >= 1) log(String.format(s, args)); 61 | } 62 | 63 | public void error(String s, Object... args) { 64 | log(String.format(s, args)); 65 | } 66 | 67 | public void info(String s, Object... args) { 68 | log(String.format(s, args)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Module/AXProgressBar.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Module; 2 | 3 | import javafx.animation.KeyFrame; 4 | import javafx.animation.KeyValue; 5 | import javafx.animation.Timeline; 6 | import javafx.application.Platform; 7 | import javafx.beans.property.SimpleDoubleProperty; 8 | import javafx.scene.shape.Rectangle; 9 | import javafx.util.Duration; 10 | import mai_onsyn.AnimeFX2.Styles.AXProgressBarStyle; 11 | import mai_onsyn.AnimeFX2.Styles.DefaultAXProgressBarStyle; 12 | 13 | public class AXProgressBar extends AXBase { 14 | 15 | private AXProgressBarStyle style = new DefaultAXProgressBarStyle(); 16 | private final Rectangle progressBar = new Rectangle(); 17 | 18 | private final SimpleDoubleProperty progress = new SimpleDoubleProperty(0); 19 | 20 | private final SimpleDoubleProperty insets = new SimpleDoubleProperty(0); 21 | private Timeline progressTimeline = new Timeline(); 22 | private boolean setLocker = false; 23 | 24 | public AXProgressBar() { 25 | super(); 26 | super.setTheme(style); 27 | insets.set(style.getBorderInsets()); 28 | 29 | progressBar.layoutXProperty().bind(insets); 30 | progressBar.layoutYProperty().bind(insets); 31 | progressBar.heightProperty().bind(super.heightProperty().subtract(insets.multiply(2))); 32 | 33 | progressBar.setFill(style.getInnerBGColor()); 34 | progressBar.setArcWidth(style.getInnerBorderArcSize()); 35 | progressBar.setArcHeight(style.getInnerBorderArcSize()); 36 | progressBar.setStrokeWidth(style.getInnerBorderRadius()); 37 | progressBar.setStroke(style.getInnerBorderColor()); 38 | 39 | progress.addListener((o, ov, nv) -> { 40 | if (!setLocker) { 41 | setLocker = true; 42 | this.setProgress(nv.doubleValue()); 43 | setLocker = false; 44 | } 45 | }); 46 | 47 | super.widthProperty().addListener((o, ov, nv) -> Platform.runLater(() -> { 48 | if (!setLocker) { 49 | setLocker = true; 50 | this.setProgress(progress.get(), 1); 51 | setLocker = false; 52 | } 53 | })); 54 | 55 | super.getChildren().add(progressBar); 56 | } 57 | 58 | 59 | public void setProgress(double p) { 60 | setProgress(p, 200 * style.getAnimeRate()); 61 | } 62 | 63 | public void setProgress(double p, double duration) { 64 | p = Math.max(0, Math.min(1, p)); 65 | double width = (super.getLayoutBounds().getWidth() - insets.get() * 2) * p; 66 | 67 | progressTimeline.stop(); 68 | progressTimeline = new Timeline(new KeyFrame(Duration.millis(duration), 69 | new KeyValue(progressBar.widthProperty(), width)//, 70 | //new KeyValue(progress, p) 71 | )); 72 | progressTimeline.play(); 73 | setLocker = true; 74 | progress.set(p); 75 | setLocker = false; 76 | } 77 | 78 | public SimpleDoubleProperty progressProperty() { 79 | return progress; 80 | } 81 | 82 | public void setTheme(AXProgressBarStyle style) { 83 | this.style = style; 84 | super.setTheme(style); 85 | } 86 | 87 | @Override 88 | public void update() { 89 | Timeline timeline = new Timeline(new KeyFrame(Duration.millis(200 * style.getAnimeRate()), 90 | new KeyValue(insets, style.getBorderInsets()), 91 | new KeyValue(progressBar.strokeWidthProperty(), style.getInnerBorderRadius()), 92 | new KeyValue(progressBar.arcWidthProperty(), style.getInnerBorderRadius()), 93 | new KeyValue(progressBar.arcHeightProperty(), style.getInnerBorderRadius()), 94 | new KeyValue(progressBar.strokeProperty(), style.getInnerBorderColor()), 95 | new KeyValue(progressBar.fillProperty(), style.getInnerBGColor()) 96 | )); 97 | timeline.play(); 98 | 99 | 100 | super.update(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/ResourceManager.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2; 2 | 3 | import javafx.scene.image.WritableImage; 4 | import mai_onsyn.AnimeFX2.Utls.Toolkit; 5 | 6 | import java.io.IOException; 7 | 8 | public class ResourceManager { 9 | public static final WritableImage copy; 10 | public static final WritableImage paste; 11 | public static final WritableImage pasteAppend; 12 | public static final WritableImage cut; 13 | public static final WritableImage selectAll; 14 | public static final WritableImage clear; 15 | public static final WritableImage undo; 16 | public static final WritableImage expand; 17 | public static final WritableImage rename; 18 | public static final WritableImage up; 19 | public static final WritableImage down; 20 | public static final WritableImage delete; 21 | 22 | public static final WritableImage triangle; 23 | public static final WritableImage fork; 24 | public static final WritableImage file; 25 | public static final WritableImage fileNew; 26 | public static final WritableImage folder; 27 | public static final WritableImage folderNew; 28 | 29 | static { 30 | try { 31 | copy = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/copy.png").getPixelReader(), 512, 512); 32 | paste = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/paste.png").getPixelReader(), 512, 512); 33 | pasteAppend = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/paste_append.png").getPixelReader(), 512, 512); 34 | cut = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/cut.png").getPixelReader(), 512, 512); 35 | selectAll = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/select_all.png").getPixelReader(), 512, 512); 36 | clear = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/clear.png").getPixelReader(), 512, 512); 37 | delete = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/delete.png").getPixelReader(), 512, 512); 38 | undo = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/undo.png").getPixelReader(), 512, 512); 39 | expand = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/expand.png").getPixelReader(), 512, 512); 40 | folderNew = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/folder_new.png").getPixelReader(), 512, 512); 41 | fileNew = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/file_new.png").getPixelReader(), 512, 512); 42 | rename = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/rename.png").getPixelReader(), 512, 512); 43 | up = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/up.png").getPixelReader(), 512, 512); 44 | down = new WritableImage(Toolkit.loadImage("textures/icons/context_menu/down.png").getPixelReader(), 512, 512); 45 | 46 | triangle = new WritableImage(Toolkit.loadImage("textures/icons/triangle.png").getPixelReader(), 384, 384); 47 | folder = new WritableImage(Toolkit.loadImage("textures/icons/folder.png").getPixelReader(), 512, 512); 48 | file = new WritableImage(Toolkit.loadImage("textures/icons/file.png").getPixelReader(), 512, 512); 49 | fork = new WritableImage(Toolkit.loadImage("textures/icons/fork.png").getPixelReader(), 512, 512); 50 | 51 | } catch (IOException e) { 52 | throw new RuntimeException(e); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXBackGroundStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public interface AXBackGroundStyle { 6 | 7 | Color getBGShadow(); 8 | double getAnimeRate(); 9 | double getBGOpacity(); 10 | double getBGBlurStrength(); 11 | 12 | void setBGShadow(Color color); 13 | void setBGBlurStrength(double strength); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXBaseStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public interface AXBaseStyle { 6 | Color getBGColor(); 7 | Color getHoverShadow(); 8 | Color getPressedShadow(); 9 | Color getBorderColor(); 10 | double getAnimeRate(); 11 | double getBorderRadius(); 12 | double getBorderArcSize(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXButtonStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public interface AXButtonStyle extends AXBaseStyle { 7 | Color getTextColor(); 8 | Color getTextHoverColor(); 9 | Color getFillColor(); 10 | Font getTextFont(); 11 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXChoiceBoxStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.image.Image; 4 | 5 | public interface AXChoiceBoxStyle extends AXBaseStyle { 6 | 7 | double getSignalRelateX(); 8 | double getSignalScale(); 9 | RelativePosition getSignalRelate(); 10 | Image getSignalImage(); 11 | 12 | 13 | enum RelativePosition { 14 | LEFT, RIGHT 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXContextPaneStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | public interface AXContextPaneStyle extends AXBaseStyle { 4 | double getItemHeight(); 5 | double getBGInsets(); 6 | double getPaneWidth(); 7 | double getMaxHeight(); 8 | 9 | AXButtonStyle getItemStyle(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXLangLabelStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public interface AXLangLabelStyle { 6 | Color getFill(); 7 | double getAnimeRate(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXProgressBarStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public interface AXProgressBarStyle extends AXBaseStyle { 6 | 7 | Color getInnerBGColor(); 8 | Color getInnerBorderColor(); 9 | double getInnerBorderRadius(); 10 | double getInnerBorderArcSize(); 11 | double getBorderInsets(); 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXSliderStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | public interface AXSliderStyle { 4 | AXProgressBarStyle getTrackStyle(); 5 | AXBaseStyle getThumbStyle(); 6 | double getThumbWidth(); 7 | double getThumbHeight(); 8 | double getTrackHeight(); 9 | 10 | double getHoveredScale(); 11 | double getClickedScale(); 12 | double getPressedScale(); 13 | 14 | double getAnimeRate(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXSwitchStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | public interface AXSwitchStyle{ 4 | 5 | AXBaseStyle getThumbStyle(); 6 | AXBaseStyle getSwitchedThumbStyle(); 7 | AXBaseStyle getTrackStyle(); 8 | AXBaseStyle getSwitchedTrackStyle(); 9 | 10 | double getThumbInsetsScale(); 11 | double getHoveredScale(); 12 | double getClickedScale(); 13 | double getPressedScale(); 14 | 15 | double getAnimeRate(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXTextAreaStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public interface AXTextAreaStyle extends AXBaseStyle { 7 | 8 | AXContextPaneStyle getContextMenuStyle(); 9 | 10 | Color getTextColor(); 11 | Color getTextSelectedColor(); 12 | Color getTextSelectedBGColor(); 13 | Font getTextFont(); 14 | double getAreaInsets(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXTextFieldStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public interface AXTextFieldStyle extends AXBaseStyle{ 7 | 8 | AXContextPaneStyle getContextPaneStyle(); 9 | 10 | Color getLineColor(); 11 | Color getTextColor(); 12 | Color getTextSelectedColor(); 13 | Color getTextSelectedBGColor(); 14 | Font getTextFont(); 15 | double getAreaInsets(); 16 | double getLineWeight(); 17 | double getLineInsets(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXTextInputPopupStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public interface AXTextInputPopupStyle extends AXBaseStyle{ 7 | AXTextFieldStyle getTextFieldStyle(); 8 | 9 | Font getTextFont(); 10 | Color getTextColor(); 11 | double getWidth(); 12 | double getHeight(); 13 | 14 | double getTextFieldHeight(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXTreeItemStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.image.Image; 4 | 5 | public interface AXTreeItemStyle extends AXButtonStyle{ 6 | 7 | Image getIcon(); 8 | double getIconInsets(); 9 | double getTextLeftInsets(); 10 | double getTextRightInsets(); 11 | double getChildrenInsets(); 12 | double getItemHeight(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/AXTreeViewStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | public interface AXTreeViewStyle extends AXBaseStyle { 4 | AXTreeItemStyle getRootItemStyle(); 5 | AXTreeItemStyle getFolderItemStyle(); 6 | AXTreeItemStyle getFileItemStyle(); 7 | 8 | AXContextPaneStyle getContextMenuStyle(); 9 | 10 | double getContentInsets(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXBackGroundStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public class DefaultAXBackGroundStyle implements AXBackGroundStyle{ 6 | 7 | private Color bgShadow = Color.rgb(0, 0, 0, 0.5); 8 | private double animeRate = 1.0; 9 | private double bgOpacity = 1.0; 10 | private double bgBlur = 10.0; 11 | 12 | @Override 13 | public Color getBGShadow() { 14 | return bgShadow; 15 | } 16 | 17 | @Override 18 | public double getAnimeRate() { 19 | return animeRate; 20 | } 21 | 22 | @Override 23 | public double getBGOpacity() { 24 | return bgOpacity; 25 | } 26 | 27 | @Override 28 | public double getBGBlurStrength() { 29 | return bgBlur; 30 | } 31 | 32 | @Override 33 | public void setBGShadow(Color color) { 34 | this.bgShadow = color; 35 | } 36 | 37 | @Override 38 | public void setBGBlurStrength(double strength) { 39 | this.bgBlur = strength; 40 | } 41 | 42 | public void setBgShadow(Color bgShadow) { 43 | this.bgShadow = bgShadow; 44 | } 45 | 46 | public void setAnimeRate(double animeRate) { 47 | this.animeRate = animeRate; 48 | } 49 | 50 | public void setBgOpacity(double bgOpacity) { 51 | this.bgOpacity = bgOpacity; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXBaseStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public class DefaultAXBaseStyle implements AXBaseStyle{ 6 | 7 | 8 | private Color bgColor = Color.rgb(192, 192, 192, 0.5); 9 | private Color hoverShadow = Color.rgb(128, 128, 128, 0.5); 10 | private Color pressedShadow = Color.rgb(64, 64, 64, 0.5); 11 | private Color borderColor = Color.CORNFLOWERBLUE; 12 | private double animeRate = 1.0; 13 | private double borderRadius = 1.0; 14 | private double borderArcSize = 10.0; 15 | 16 | @Override 17 | public Color getBGColor() { 18 | return bgColor; 19 | } 20 | 21 | @Override 22 | public Color getHoverShadow() { 23 | return hoverShadow; 24 | } 25 | 26 | @Override 27 | public Color getPressedShadow() { 28 | return pressedShadow; 29 | } 30 | 31 | @Override 32 | public Color getBorderColor() { 33 | return borderColor; 34 | } 35 | 36 | @Override 37 | public double getAnimeRate() { 38 | return animeRate; 39 | } 40 | 41 | @Override 42 | public double getBorderRadius() { 43 | return borderRadius; 44 | } 45 | 46 | @Override 47 | public double getBorderArcSize() { 48 | return borderArcSize; 49 | } 50 | 51 | 52 | 53 | public void setBGColor(Color color) { 54 | this.bgColor = color; 55 | } 56 | 57 | public void setHoverShadow(Color color) { 58 | this.hoverShadow = color; 59 | } 60 | 61 | public void setPressedShadow(Color color) { 62 | this.pressedShadow = color; 63 | } 64 | 65 | public void setBorderColor(Color color) { 66 | this.borderColor = color; 67 | } 68 | 69 | public void setAnimeRate(double rate) { 70 | this.animeRate = rate; 71 | } 72 | 73 | public void setBorderRadius(double radius) { 74 | this.borderRadius = radius; 75 | } 76 | 77 | public void setBorderArcSize(double arcSize) { 78 | this.borderArcSize = arcSize; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXButtonGroupStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | 4 | import javafx.scene.paint.Color; 5 | import javafx.scene.text.Font; 6 | 7 | public class DefaultAXButtonGroupStyle extends DefaultAXButtonStyle { 8 | 9 | 10 | private Color bgColor = Color.rgb(25, 69, 94, 0.5); 11 | private Color hoverShadow = Color.rgb(128, 128, 128, 0.5); 12 | private Color pressedShadow = Color.rgb(64, 64, 64, 0.5); 13 | private Color borderColor = Color.CORNFLOWERBLUE; 14 | private double animeRate = 1.0; 15 | private double borderRadius = 1.0; 16 | private double borderArcSize = 10.0; 17 | 18 | 19 | private Color textColor = Color.WHITE; 20 | private Color textHoverColor = Color.LIGHTBLUE; 21 | private Color fillColor = Color.rgb(255, 128, 128, 0.5); 22 | private Font textFont = new Font(12); 23 | 24 | 25 | 26 | @Override 27 | public Color getBGColor() { 28 | return bgColor; 29 | } 30 | 31 | @Override 32 | public Color getHoverShadow() { 33 | return hoverShadow; 34 | } 35 | 36 | @Override 37 | public Color getPressedShadow() { 38 | return pressedShadow; 39 | } 40 | 41 | @Override 42 | public Color getBorderColor() { 43 | return borderColor; 44 | } 45 | 46 | @Override 47 | public double getAnimeRate() { 48 | return animeRate; 49 | } 50 | 51 | @Override 52 | public double getBorderRadius() { 53 | return borderRadius; 54 | } 55 | 56 | @Override 57 | public double getBorderArcSize() { 58 | return borderArcSize; 59 | } 60 | 61 | 62 | 63 | @Override 64 | public Color getTextColor() { 65 | return textColor; 66 | } 67 | 68 | @Override 69 | public Color getTextHoverColor() { 70 | return textHoverColor; 71 | } 72 | 73 | @Override 74 | public Color getFillColor() { 75 | return fillColor; 76 | } 77 | 78 | @Override 79 | public Font getTextFont() { 80 | return textFont; 81 | } 82 | 83 | 84 | 85 | 86 | 87 | public void setBGColor(Color color) { 88 | this.bgColor = color; 89 | } 90 | 91 | public void setHoverShadow(Color color) { 92 | this.hoverShadow = color; 93 | } 94 | 95 | public void setPressedShadow(Color color) { 96 | this.pressedShadow = color; 97 | } 98 | 99 | public void setBorderColor(Color color) { 100 | this.borderColor = color; 101 | } 102 | 103 | public void setAnimeRate(double rate) { 104 | this.animeRate = rate; 105 | } 106 | 107 | public void setBorderRadius(double radius) { 108 | this.borderRadius = radius; 109 | } 110 | 111 | public void setBorderArcSize(double arcSize) { 112 | this.borderArcSize = arcSize; 113 | } 114 | 115 | 116 | 117 | public void setTextColor(Color color) { 118 | this.textColor = color; 119 | } 120 | 121 | public void setTextHoverColor(Color color) { 122 | this.textHoverColor = color; 123 | } 124 | 125 | public void setTextFont(Font font) { 126 | this.textFont = font; 127 | } 128 | 129 | public void setFillColor(Color color) { 130 | this.fillColor = color; 131 | } 132 | 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXButtonStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public class DefaultAXButtonStyle extends DefaultAXBaseStyle implements AXButtonStyle { 7 | 8 | private Color textColor = Color.BLACK; 9 | private Color textHoverColor = Color.RED; 10 | private Color fillColor = Color.rgb(128, 128, 255, 0.5); 11 | private Font textFont = new Font(12); 12 | 13 | @Override 14 | public Color getTextColor() { 15 | return textColor; 16 | } 17 | 18 | @Override 19 | public Color getTextHoverColor() { 20 | return textHoverColor; 21 | } 22 | 23 | @Override 24 | public Color getFillColor() { 25 | return fillColor; 26 | } 27 | 28 | @Override 29 | public Font getTextFont() { 30 | return textFont; 31 | } 32 | 33 | 34 | public void setTextColor(Color color) { 35 | this.textColor = color; 36 | } 37 | 38 | public void setTextHoverColor(Color color) { 39 | this.textHoverColor = color; 40 | } 41 | 42 | public void setTextFont(Font font) { 43 | this.textFont = font; 44 | } 45 | 46 | public void setFillColor(Color color) { 47 | this.fillColor = color; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXChoiceBoxStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.image.Image; 4 | import mai_onsyn.AnimeFX2.ResourceManager; 5 | 6 | public class DefaultAXChoiceBoxStyle extends DefaultAXBaseStyle implements AXChoiceBoxStyle { 7 | 8 | private double signalRelateX = 5; 9 | private double signalScale = 20; 10 | private RelativePosition signalRelate = RelativePosition.RIGHT; 11 | private Image signalImage = ResourceManager.triangle; 12 | 13 | 14 | @Override 15 | public double getSignalRelateX() { 16 | return signalRelateX; 17 | } 18 | 19 | @Override 20 | public double getSignalScale() { 21 | return signalScale; 22 | } 23 | 24 | @Override 25 | public RelativePosition getSignalRelate() { 26 | return signalRelate; 27 | } 28 | 29 | @Override 30 | public Image getSignalImage() { 31 | return signalImage; 32 | } 33 | 34 | 35 | public void setSignalRelateX(double signalRelateX) { 36 | this.signalRelateX = signalRelateX; 37 | } 38 | 39 | public void setSignalScale(double signalScale) { 40 | this.signalScale = signalScale; 41 | } 42 | 43 | public void setSignalRelate(RelativePosition signalRelate) { 44 | this.signalRelate = signalRelate; 45 | } 46 | 47 | public void setSignalImage(Image signalImage) { 48 | this.signalImage = signalImage; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXContextPaneStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public class DefaultAXContextPaneStyle extends DefaultAXBaseStyle implements AXContextPaneStyle { 6 | 7 | private Color hoverShadow = Color.TRANSPARENT; 8 | private Color pressedShadow = Color.TRANSPARENT; 9 | private double itemArcSize = 10.0; 10 | private double itemHeight = 25.0; 11 | private double bgInsets = 5.0; 12 | private double paneWidth = 192.0; 13 | private double maxHeight = 500.0; 14 | 15 | @Override 16 | public Color getHoverShadow() { 17 | return hoverShadow; 18 | } 19 | 20 | @Override 21 | public Color getPressedShadow() { 22 | return pressedShadow; 23 | } 24 | 25 | @Override 26 | public double getItemHeight() { 27 | return itemHeight; 28 | } 29 | 30 | @Override 31 | public double getBGInsets() { 32 | return bgInsets; 33 | } 34 | 35 | @Override 36 | public double getPaneWidth() { 37 | return paneWidth; 38 | } 39 | 40 | @Override 41 | public double getMaxHeight() { 42 | return maxHeight; 43 | } 44 | 45 | @Override 46 | public void setHoverShadow(Color hoverShadow) { 47 | this.hoverShadow = hoverShadow; 48 | } 49 | 50 | @Override 51 | public void setPressedShadow(Color pressedShadow) { 52 | this.pressedShadow = pressedShadow; 53 | } 54 | 55 | public void setItemArcSize(double itemArcSize) { 56 | this.itemArcSize = itemArcSize; 57 | } 58 | 59 | public void setItemHeight(double itemHeight) { 60 | this.itemHeight = itemHeight; 61 | } 62 | 63 | public void setBgInsets(double bgInsets) { 64 | this.bgInsets = bgInsets; 65 | } 66 | 67 | public void setPaneWidth(double paneWidth) { 68 | this.paneWidth = paneWidth; 69 | } 70 | 71 | public void setMaxHeight(double maxHeight) { 72 | this.maxHeight = maxHeight; 73 | } 74 | 75 | @Override 76 | public ItemStyle getItemStyle() { 77 | return new ItemStyle(); 78 | } 79 | 80 | public class ItemStyle extends DefaultAXButtonStyle { 81 | @Override 82 | public Color getBGColor() { 83 | return Color.TRANSPARENT; 84 | } 85 | 86 | public Color getBorderColor() { 87 | return Color.TRANSPARENT; 88 | } 89 | 90 | public double getBorderArcSize() { 91 | return itemArcSize; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXLangLabelStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public class DefaultAXLangLabelStyle implements AXLangLabelStyle { 6 | 7 | private Color color = Color.BLACK; 8 | private double animeRate = 1; 9 | 10 | 11 | @Override 12 | public Color getFill() { 13 | return color; 14 | } 15 | 16 | @Override 17 | public double getAnimeRate() { 18 | return animeRate; 19 | } 20 | 21 | public void setColor(Color color) { 22 | this.color = color; 23 | } 24 | 25 | public void setAnimeRate(double animeRate) { 26 | this.animeRate = animeRate; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXProgressBarStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public class DefaultAXProgressBarStyle extends DefaultAXBaseStyle implements AXProgressBarStyle { 6 | 7 | 8 | private Color innerBGColor = Color.rgb(25, 69, 94, 0.5); 9 | private Color innerBorderColor = Color.TRANSPARENT; 10 | private double innerBorderRadius = 0; 11 | private double innerBorderArcSize = 10.0; 12 | private double borderInsets = 3; 13 | 14 | 15 | 16 | @Override 17 | public Color getInnerBGColor() { 18 | return innerBGColor; 19 | } 20 | 21 | @Override 22 | public Color getInnerBorderColor() { 23 | return innerBorderColor; 24 | } 25 | 26 | @Override 27 | public double getInnerBorderRadius() { 28 | return innerBorderRadius; 29 | } 30 | 31 | @Override 32 | public double getInnerBorderArcSize() { 33 | return innerBorderArcSize; 34 | } 35 | 36 | @Override 37 | public double getBorderInsets() { 38 | return borderInsets; 39 | } 40 | 41 | 42 | public void setInnerBGColor(Color innerBGColor) { 43 | this.innerBGColor = innerBGColor; 44 | } 45 | 46 | public void setInnerBorderColor(Color innerBorderColor) { 47 | this.innerBorderColor = innerBorderColor; 48 | } 49 | 50 | public void setInnerBorderRadius(double innerBorderRadius) { 51 | this.innerBorderRadius = innerBorderRadius; 52 | } 53 | 54 | public void setInnerBorderArcSize(double innerBorderArcSize) { 55 | this.innerBorderArcSize = innerBorderArcSize; 56 | } 57 | 58 | public void setBorderInsets(double borderInsets) { 59 | this.borderInsets = borderInsets; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXSliderStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | 5 | public class DefaultAXSliderStyle implements AXSliderStyle { 6 | 7 | private double thumbWidth = 30; 8 | private double thumbHeight = 30; 9 | private double trackHeight = 7.5; 10 | private double hoveredScale = 1.4; 11 | private double clickedScale = 0.9; 12 | private double pressedScale = 1.2; 13 | private double animeRate = 1.0; 14 | 15 | 16 | @Override 17 | public AXProgressBarStyle getTrackStyle() { 18 | DefaultAXProgressBarStyle style = new DefaultAXProgressBarStyle(); 19 | style.setBorderColor(Color.TRANSPARENT); 20 | style.setBorderInsets(1); 21 | style.setAnimeRate(animeRate); 22 | return style; 23 | } 24 | 25 | @Override 26 | public AXBaseStyle getThumbStyle() { 27 | DefaultAXBaseStyle style = new DefaultAXBaseStyle(); 28 | style.setBorderArcSize(thumbWidth); 29 | style.setBGColor(Color.WHITE); 30 | return style; 31 | } 32 | 33 | @Override 34 | public double getThumbWidth() { 35 | return thumbWidth; 36 | } 37 | 38 | @Override 39 | public double getThumbHeight() { 40 | return thumbHeight; 41 | } 42 | 43 | @Override 44 | public double getTrackHeight() { 45 | return trackHeight; 46 | } 47 | 48 | @Override 49 | public double getHoveredScale() { 50 | return hoveredScale; 51 | } 52 | 53 | @Override 54 | public double getClickedScale() { 55 | return clickedScale; 56 | } 57 | 58 | @Override 59 | public double getPressedScale() { 60 | return pressedScale; 61 | } 62 | 63 | @Override 64 | public double getAnimeRate() { 65 | return animeRate; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXSwitchStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import mai_onsyn.AnimeFX2.Utls.Toolkit; 5 | 6 | public class DefaultAXSwitchStyle implements AXSwitchStyle{ 7 | 8 | private double thumbInsetsScale = 0.1; 9 | private double hoveredScale = 1.1; 10 | private double clickedScale = 0.8; 11 | private double pressedScale = 0.9; 12 | private double borderArcSize = 20; 13 | 14 | private double animeRate = 1.0; 15 | 16 | 17 | @Override 18 | public AXBaseStyle getThumbStyle() { 19 | DefaultAXBaseStyle style = new DefaultAXBaseStyle(); 20 | style.setBorderArcSize(borderArcSize); 21 | style.setBGColor(Color.rgb(25, 69, 94, 0.8)); 22 | return style; 23 | } 24 | 25 | @Override 26 | public AXBaseStyle getSwitchedThumbStyle() { 27 | DefaultAXBaseStyle style = new DefaultAXBaseStyle(); 28 | style.setBorderArcSize(borderArcSize); 29 | style.setBGColor(Color.rgb(255, 255, 255, 0.8)); 30 | return style; 31 | } 32 | 33 | @Override 34 | public AXBaseStyle getTrackStyle() { 35 | DefaultAXBaseStyle style = new DefaultAXBaseStyle(); 36 | style.setBorderArcSize(borderArcSize); 37 | return style; 38 | } 39 | 40 | @Override 41 | public AXBaseStyle getSwitchedTrackStyle() { 42 | DefaultAXBaseStyle style = new DefaultAXBaseStyle(); 43 | style.setBorderArcSize(borderArcSize); 44 | style.setBGColor(Color.rgb(25, 69, 94, 0.5)); 45 | return style; 46 | } 47 | 48 | @Override 49 | public double getThumbInsetsScale() { 50 | return thumbInsetsScale; 51 | } 52 | 53 | @Override 54 | public double getHoveredScale() { 55 | return hoveredScale; 56 | } 57 | 58 | @Override 59 | public double getClickedScale() { 60 | return clickedScale; 61 | } 62 | 63 | @Override 64 | public double getPressedScale() { 65 | return pressedScale; 66 | } 67 | 68 | @Override 69 | public double getAnimeRate() { 70 | return animeRate; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXTextAreaStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public class DefaultAXTextAreaStyle extends DefaultAXBaseStyle implements AXTextAreaStyle { 7 | 8 | private Color textColor = Color.BLACK; 9 | private Color textSelectedColor = Color.HOTPINK; 10 | private Color textSelectedBGColor = Color.LIGHTBLUE; 11 | private Font textFont = new Font(12); 12 | private double areaInsets = 5.0; 13 | 14 | @Override 15 | public AXContextPaneStyle getContextMenuStyle() { 16 | return new DefaultAXContextPaneStyle(); 17 | } 18 | 19 | @Override 20 | public Color getTextColor() { 21 | return textColor; 22 | } 23 | 24 | @Override 25 | public Color getTextSelectedColor() { 26 | return textSelectedColor; 27 | } 28 | 29 | @Override 30 | public Color getTextSelectedBGColor() { 31 | return textSelectedBGColor; 32 | } 33 | 34 | @Override 35 | public Font getTextFont() { 36 | return textFont; 37 | } 38 | 39 | @Override 40 | public double getAreaInsets() { 41 | return areaInsets; 42 | } 43 | 44 | public void setTextColor(Color textColor) { 45 | this.textColor = textColor; 46 | } 47 | 48 | public void setTextSelectedColor(Color textSelectedColor) { 49 | this.textSelectedColor = textSelectedColor; 50 | } 51 | 52 | public void setTextSelectedBGColor(Color textSelectedBGColor) { 53 | this.textSelectedBGColor = textSelectedBGColor; 54 | } 55 | 56 | public void setAreaInsets(double areaInsets) { 57 | this.areaInsets = areaInsets; 58 | } 59 | 60 | public void setTextFont(Font textFont) { 61 | this.textFont = textFont; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXTextFieldStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public class DefaultAXTextFieldStyle extends DefaultAXBaseStyle implements AXTextFieldStyle { 7 | 8 | private Color lineColor = Color.GREEN; 9 | private Color textColor = Color.BLACK; 10 | private Color textSelectedColor = Color.HOTPINK; 11 | private Color textSelectedBGColor = Color.LIGHTBLUE; 12 | private Font textFont = new Font(12); 13 | private double areaInsets = 5.0; 14 | private double lineWeight = 1.0; 15 | private double lineInsets = 2.5; 16 | 17 | @Override 18 | public AXContextPaneStyle getContextPaneStyle() { 19 | return new DefaultAXContextPaneStyle(); 20 | } 21 | 22 | @Override 23 | public Color getLineColor() { 24 | return lineColor; 25 | } 26 | 27 | @Override 28 | public Color getTextColor() { 29 | return textColor; 30 | } 31 | 32 | @Override 33 | public Color getTextSelectedColor() { 34 | return textSelectedColor; 35 | } 36 | 37 | @Override 38 | public Color getTextSelectedBGColor() { 39 | return textSelectedBGColor; 40 | } 41 | 42 | @Override 43 | public Font getTextFont() { 44 | return textFont; 45 | } 46 | 47 | @Override 48 | public double getAreaInsets() { 49 | return areaInsets; 50 | } 51 | 52 | @Override 53 | public double getLineWeight() { 54 | return lineWeight; 55 | } 56 | 57 | @Override 58 | public double getLineInsets() { 59 | return lineInsets; 60 | } 61 | 62 | public void setTextColor(Color textColor) { 63 | this.textColor = textColor; 64 | } 65 | 66 | public void setTextSelectedColor(Color textSelectedColor) { 67 | this.textSelectedColor = textSelectedColor; 68 | } 69 | 70 | public void setTextSelectedBGColor(Color textSelectedBGColor) { 71 | this.textSelectedBGColor = textSelectedBGColor; 72 | } 73 | 74 | public void setAreaInsets(double areaInsets) { 75 | this.areaInsets = areaInsets; 76 | } 77 | 78 | public void setTextFont(Font textFont) { 79 | this.textFont = textFont; 80 | } 81 | 82 | public void setLineColor(Color lineColor) { 83 | this.lineColor = lineColor; 84 | } 85 | 86 | public void setLineWeight(double lineWeight) { 87 | this.lineWeight = lineWeight; 88 | } 89 | 90 | public void setLineInsets(double lineInsets) { 91 | this.lineInsets = lineInsets; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXTextInputPopupStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | 6 | public class DefaultAXTextInputPopupStyle extends DefaultAXBaseStyle implements AXTextInputPopupStyle { 7 | @Override 8 | public AXTextFieldStyle getTextFieldStyle() { 9 | return null; 10 | } 11 | 12 | @Override 13 | public Font getTextFont() { 14 | return new Font(13); 15 | } 16 | 17 | @Override 18 | public Color getTextColor() { 19 | return Color.BLACK; 20 | } 21 | 22 | @Override 23 | public double getWidth() { 24 | return 350; 25 | } 26 | 27 | @Override 28 | public double getHeight() { 29 | return 150; 30 | } 31 | 32 | @Override 33 | public double getTextFieldHeight() { 34 | return 25; 35 | } 36 | 37 | @Override 38 | public Color getHoverShadow() { 39 | return Color.TRANSPARENT; 40 | } 41 | 42 | @Override 43 | public Color getPressedShadow() { 44 | return Color.TRANSPARENT; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXTreeItemStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.image.Image; 4 | import javafx.scene.image.WritableImage; 5 | 6 | public class DefaultAXTreeItemStyle extends DefaultAXButtonStyle implements AXTreeItemStyle { 7 | 8 | private Image icon = new WritableImage(1, 1); 9 | private double iconInsets = 3; 10 | private double textLeftInsets = 0; 11 | private double textRightInsets = 10; 12 | private double childrenInsets = 30; 13 | private double itemHeight = 25; 14 | 15 | @Override 16 | public Image getIcon() { 17 | return icon; 18 | } 19 | 20 | @Override 21 | public double getIconInsets() { 22 | return iconInsets; 23 | } 24 | 25 | @Override 26 | public double getTextLeftInsets() { 27 | return textLeftInsets; 28 | } 29 | 30 | @Override 31 | public double getTextRightInsets() { 32 | return textRightInsets; 33 | } 34 | 35 | @Override 36 | public double getChildrenInsets() { 37 | return childrenInsets; 38 | } 39 | 40 | @Override 41 | public double getItemHeight() { 42 | return itemHeight; 43 | } 44 | 45 | 46 | public void setIcon(Image icon) { 47 | this.icon = icon; 48 | } 49 | 50 | public void setIconInsets(double iconInsets) { 51 | this.iconInsets = iconInsets; 52 | } 53 | 54 | public void setChildrenInsets(double childrenInsets) { 55 | this.childrenInsets = childrenInsets; 56 | } 57 | 58 | public void setItemHeight(double itemHeight) { 59 | this.itemHeight = itemHeight; 60 | } 61 | 62 | public void setTextLeftInsets(double textLeftInsets) { 63 | this.textLeftInsets = textLeftInsets; 64 | } 65 | 66 | public void setTextRightInsets(double textRightInsets) { 67 | this.textRightInsets = textRightInsets; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Styles/DefaultAXTreeViewStyle.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Styles; 2 | 3 | import javafx.scene.paint.Color; 4 | import mai_onsyn.AnimeFX2.ResourceManager; 5 | 6 | public class DefaultAXTreeViewStyle extends DefaultAXBaseStyle implements AXTreeViewStyle { 7 | @Override 8 | public AXTreeItemStyle getRootItemStyle() { 9 | DefaultAXTreeItemStyle style = new DefaultAXTreeItemStyle(); 10 | style.setIcon(ResourceManager.fork); 11 | style.setBorderRadius(0); 12 | style.setBorderColor(Color.TRANSPARENT); 13 | style.setBGColor(Color.TRANSPARENT); 14 | return style; 15 | } 16 | 17 | @Override 18 | public AXTreeItemStyle getFolderItemStyle() {DefaultAXTreeItemStyle style = new DefaultAXTreeItemStyle(); 19 | style.setIcon(ResourceManager.folder); 20 | style.setBorderRadius(0); 21 | style.setBorderColor(Color.TRANSPARENT); 22 | style.setBGColor(Color.TRANSPARENT); 23 | return style; 24 | } 25 | 26 | @Override 27 | public AXTreeItemStyle getFileItemStyle() {DefaultAXTreeItemStyle style = new DefaultAXTreeItemStyle(); 28 | style.setIcon(ResourceManager.file); 29 | style.setBorderRadius(0); 30 | style.setBorderColor(Color.TRANSPARENT); 31 | style.setBGColor(Color.TRANSPARENT); 32 | return style; 33 | } 34 | 35 | @Override 36 | public AXContextPaneStyle getContextMenuStyle() { 37 | return new DefaultAXContextPaneStyle(); 38 | } 39 | 40 | @Override 41 | public double getContentInsets() { 42 | return 10; 43 | } 44 | 45 | @Override 46 | public Color getHoverShadow() { 47 | return Color.TRANSPARENT; 48 | } 49 | 50 | @Override 51 | public Color getPressedShadow() { 52 | return Color.TRANSPARENT; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/ThemeManager.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class ThemeManager { 7 | 8 | private final List nodes; 9 | 10 | public ThemeManager() { 11 | this.nodes = new ArrayList<>(); 12 | } 13 | 14 | public void register(AutoUpdatable node) { 15 | nodes.add(node); 16 | } 17 | 18 | public void register(AutoUpdatable... node) { 19 | nodes.addAll(List.of(node)); 20 | } 21 | 22 | public void flushAll() { 23 | for (AutoUpdatable autoUpdatable : nodes) { 24 | autoUpdatable.update(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Utls/AXButtonGroup.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Utls; 2 | 3 | import javafx.beans.value.ChangeListener; 4 | import javafx.event.EventHandler; 5 | import javafx.scene.input.MouseEvent; 6 | import mai_onsyn.AnimeFX2.AutoUpdatable; 7 | import mai_onsyn.AnimeFX2.Module.AXButton; 8 | import mai_onsyn.AnimeFX2.Styles.AXButtonStyle; 9 | import mai_onsyn.AnimeFX2.Styles.DefaultAXButtonGroupStyle; 10 | import mai_onsyn.AnimeFX2.Styles.DefaultAXButtonStyle; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public class AXButtonGroup implements AutoUpdatable { 18 | 19 | protected AXButtonStyle style = new DefaultAXButtonStyle(); 20 | protected AXButtonStyle selectedStyle = new DefaultAXButtonGroupStyle(); 21 | 22 | protected final List buttons = new ArrayList<>(); 23 | protected final Map> buttonHandlers = new HashMap<>(); 24 | 25 | protected AXButton selectedButton; 26 | protected AXButton lastSelectedButton; 27 | protected List> onSelectedChanged = new ArrayList<>(); 28 | 29 | public AXButtonGroup(AXButton... buttons) { 30 | register(buttons); 31 | 32 | } 33 | 34 | public void register(AXButton... e) { 35 | for (AXButton button : e) { 36 | 37 | buttons.add(button); 38 | 39 | EventHandler eventEventHandler = _ -> selectButton(button); 40 | button.addEventHandler(MouseEvent.MOUSE_CLICKED, eventEventHandler); 41 | 42 | buttonHandlers.put(button, eventEventHandler); 43 | } 44 | } 45 | 46 | public void remove(AXButton button) { 47 | if (buttons.contains(button)) { 48 | buttons.remove(button); 49 | button.removeEventHandler(MouseEvent.MOUSE_CLICKED, buttonHandlers.get(button)); 50 | } 51 | if (button == selectedButton) { 52 | selectedButton = null; 53 | onSelectedChanged.forEach(e -> e.changed(null, lastSelectedButton, null)); 54 | } 55 | if (button == lastSelectedButton) { 56 | lastSelectedButton = null; 57 | onSelectedChanged.forEach(e -> e.changed(null, null, selectedButton)); 58 | } 59 | } 60 | 61 | public void selectButton(AXButton button) { 62 | if (selectedButton != button) { 63 | lastSelectedButton = selectedButton; 64 | selectedButton = button; 65 | 66 | if (onSelectedChanged != null) { 67 | onSelectedChanged.forEach(e -> e.changed(null, lastSelectedButton, selectedButton)); 68 | } 69 | 70 | if (lastSelectedButton != null) { 71 | lastSelectedButton.setTheme(style); 72 | lastSelectedButton.update(); 73 | } 74 | if (selectedButton != null) { 75 | selectedButton.setTheme(selectedStyle); 76 | selectedButton.update(); 77 | } 78 | } 79 | } 80 | 81 | public void setFreeStyle(AXButtonStyle style) { 82 | this.style = style; 83 | } 84 | 85 | public void setSelectedStyle(AXButtonStyle style) { 86 | this.selectedStyle = style; 87 | } 88 | 89 | public void addOnSelectChangedListener(ChangeListener onSelectedChanged) { 90 | this.onSelectedChanged.add(onSelectedChanged); 91 | } 92 | 93 | public AXButton getSelectedButton() { 94 | return selectedButton; 95 | } 96 | 97 | public List getButtonList() { 98 | return buttons; 99 | } 100 | 101 | @Override 102 | public void update() { 103 | for (AXButton button : buttons) { 104 | button.update(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Utls/AXDataTreeItem.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Utls; 2 | 3 | public class AXDataTreeItem extends AXTreeItem { 4 | 5 | private T data; 6 | private final AXTreeviewCopyRule rule; 7 | 8 | public AXDataTreeItem(String name, T data, AXTreeviewCopyRule rule) { 9 | super(name); 10 | this.rule = rule; 11 | setData(data); 12 | } 13 | 14 | public T getData() { 15 | return data; 16 | } 17 | 18 | public T getCopiedData() { 19 | return rule.copy(data); 20 | } 21 | 22 | public void setData(T data) { 23 | this.data = data; 24 | } 25 | 26 | public interface AXTreeviewCopyRule { 27 | T copy(T src); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Utls/AXDatableButtonGroup.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Utls; 2 | 3 | import javafx.beans.value.ChangeListener; 4 | import javafx.scene.input.MouseEvent; 5 | import javafx.util.Pair; 6 | import mai_onsyn.AnimeFX2.Module.AXButton; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class AXDatableButtonGroup extends AXButtonGroup{ 12 | 13 | private final Map buttonMap = new HashMap<>(); 14 | private ChangeListener> onSelectedChanged; 15 | 16 | public AXDatableButtonGroup(AXButton button, T data) { 17 | super(button); 18 | 19 | buttonMap.put(button, data); 20 | } 21 | 22 | public AXDatableButtonGroup() { 23 | super(); 24 | } 25 | 26 | 27 | 28 | public void register(AXButton button, T data) { 29 | super.register(button); 30 | 31 | buttonMap.put(button, data); 32 | } 33 | 34 | public T getData(AXButton button) { 35 | return buttonMap.get(button); 36 | } 37 | 38 | @Override 39 | public void remove(AXButton button) { 40 | if (buttons.contains(button)) { 41 | buttons.remove(button); 42 | button.removeEventHandler(MouseEvent.MOUSE_CLICKED, buttonHandlers.get(button)); 43 | } 44 | if (button == selectedButton) { 45 | selectedButton = null; 46 | onSelectedChanged.changed(null, new Pair<>(lastSelectedButton, getData(lastSelectedButton)), null); 47 | } 48 | if (button == lastSelectedButton) { 49 | lastSelectedButton = null; 50 | onSelectedChanged.changed(null, null, new Pair<>(selectedButton, getData(selectedButton))); 51 | } 52 | 53 | buttonMap.remove(button); 54 | } 55 | 56 | @Override 57 | public void selectButton(AXButton button) { 58 | if (selectedButton != button) { 59 | lastSelectedButton = selectedButton; 60 | selectedButton = button; 61 | 62 | if (super.onSelectedChanged != null) { 63 | super.onSelectedChanged.forEach(e -> e.changed(null, lastSelectedButton, selectedButton)); 64 | } 65 | if (onSelectedChanged != null) { 66 | onSelectedChanged.changed(null, new Pair<>(lastSelectedButton, buttonMap.get(lastSelectedButton)), new Pair<>(selectedButton, buttonMap.get(selectedButton))); 67 | } 68 | 69 | if (lastSelectedButton != null) { 70 | lastSelectedButton.setTheme(style); 71 | lastSelectedButton.update(); 72 | } 73 | if (selectedButton != null) { 74 | selectedButton.setTheme(selectedStyle); 75 | selectedButton.update(); 76 | } 77 | } 78 | } 79 | 80 | public void setOnSelectedChangedDatable(ChangeListener> onSelectedChanged) { 81 | this.onSelectedChanged = onSelectedChanged; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Utls/AXFloatTextField.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Utls; 2 | 3 | import javafx.beans.property.SimpleDoubleProperty; 4 | import javafx.scene.control.TextField; 5 | import mai_onsyn.AnimeFX2.Module.AXTextField; 6 | 7 | public class AXFloatTextField extends AXTextField { 8 | private final SimpleDoubleProperty valueProperty; 9 | 10 | private int textState = 1; 11 | private boolean textChangeLock = false; 12 | 13 | public AXFloatTextField(double min, double max, double value) { 14 | super(); 15 | 16 | if (value < min || value > max) throw new IllegalArgumentException("value: " + value + " must be between min and max: " + min + " - " + max); 17 | 18 | valueProperty = new SimpleDoubleProperty(value); 19 | TextField textField = super.textField(); 20 | valueProperty.addListener((e, ov, nv) -> { 21 | if (!textChangeLock) { 22 | textChangeLock = true; 23 | 24 | textField.setText(String.format("%.2f", nv.doubleValue())); 25 | 26 | textChangeLock = false; 27 | } 28 | }); 29 | 30 | textField.textProperty().addListener((e, ov, nv) -> { 31 | if (!textChangeLock) { 32 | textChangeLock = true; 33 | 34 | textState = checkFloat(nv); 35 | switch (textState) { 36 | case 0 -> { 37 | double newValue = Math.min(Math.max(Double.parseDouble(nv), min), max); 38 | if (valueProperty.get() != newValue) { 39 | valueProperty.set(newValue); 40 | } 41 | } 42 | case 1, 3, 4, 5 -> {} 43 | case 2 -> { 44 | textField.setText(ov); 45 | Toolkit.defaultToolkit.beep(); 46 | } 47 | } 48 | 49 | textChangeLock = false; 50 | } 51 | }); 52 | textField.focusedProperty().addListener((e, ov, nv) -> { 53 | if (!nv) { 54 | if ( 55 | textState == 1 || 56 | textState == 3 || 57 | textState == 4 || 58 | textState == 5 || 59 | Double.parseDouble(textField.getText()) > max || 60 | Double.parseDouble(textField.getText()) < min 61 | ) textField.setText(String.valueOf(valueProperty.get())); 62 | } 63 | }); 64 | 65 | } 66 | 67 | public SimpleDoubleProperty valueProperty() { 68 | return valueProperty; 69 | } 70 | 71 | 72 | /** 73 | * 0 - readable number; 74 | * 1 - empty; 75 | * 2 - not a number; 76 | * 3 - only a minus sign; 77 | * 4 - start with number and end with dot; 78 | * 5 - a number is sandwiched between a minus sign and a dot; 79 | * 6 - a number is sandwiched between a minus and a dot sign, and end with a number; 80 | */ 81 | private static int checkFloat(String s) { 82 | if (s == null || s.isEmpty()) { 83 | return 1; 84 | } 85 | 86 | // 判断是否只有负号 87 | if (s.equals("-")) { 88 | return 3; 89 | } 90 | 91 | // 判断是否是负数的浮动数(例如 -12.34) 92 | if (s.matches("-?\\d+\\.\\d+$")) { 93 | return 0; 94 | } 95 | 96 | // 判断是否是正数的浮动数(例如 12.34) 97 | if (s.matches("\\d+\\.\\d+$")) { 98 | return 0; 99 | } 100 | 101 | // 判断是否是整数(正整数或负整数) 102 | if (s.matches("-?\\d+$")) { 103 | return 0; 104 | } 105 | 106 | // 判断是否是负号后接数字和点 107 | if (s.matches("-\\d+\\.$")) { 108 | return 5; 109 | } 110 | 111 | // 判断是否是正号后接数字和点 112 | if (s.matches("\\d+\\.$")) { 113 | return 4; 114 | } 115 | 116 | // 判断无效输入 117 | return 2; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Utls/AXIntegerTextField.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Utls; 2 | 3 | import javafx.beans.property.SimpleIntegerProperty; 4 | import javafx.scene.control.TextField; 5 | import mai_onsyn.AnimeFX2.Module.AXTextField; 6 | 7 | public class AXIntegerTextField extends AXTextField { 8 | 9 | private final SimpleIntegerProperty valueProperty; 10 | 11 | private int textState = 1; 12 | private boolean textChangeLock = false; 13 | 14 | public AXIntegerTextField(int min, int max, int value) { 15 | super(); 16 | 17 | if (value < min || value > max) throw new IllegalArgumentException("value must be between min and max"); 18 | 19 | valueProperty = new SimpleIntegerProperty(value); 20 | TextField textField = super.textField(); 21 | valueProperty.addListener((e, ov, nv) -> { 22 | textChangeLock = true; 23 | 24 | textField.setText(String.valueOf(nv.intValue())); 25 | 26 | textChangeLock = false; 27 | }); 28 | 29 | textField.textProperty().addListener((e, ov, nv) -> { 30 | if (!textChangeLock) { 31 | textChangeLock = true; 32 | 33 | textState = checkInteger(nv); 34 | switch (textState) { 35 | case 0 -> { 36 | int newValue = Math.min(Math.max(Integer.parseInt(nv), min), max); 37 | if (valueProperty.get() != newValue) { 38 | valueProperty.set(newValue); 39 | } 40 | } 41 | case 1, 3 -> {} 42 | case 2 -> { 43 | textField.setText(ov); 44 | Toolkit.defaultToolkit.beep(); 45 | } 46 | } 47 | 48 | textChangeLock = false; 49 | } 50 | }); 51 | textField.focusedProperty().addListener((e, ov, nv) -> { 52 | if (!nv) { 53 | if ( 54 | textState == 1 || 55 | textState == 3 || 56 | Integer.parseInt(textField.getText()) > max || 57 | Integer.parseInt(textField.getText()) < min 58 | ) textField.setText(String.valueOf(valueProperty.get())); 59 | } 60 | }); 61 | 62 | } 63 | 64 | public SimpleIntegerProperty valueProperty() { 65 | return valueProperty; 66 | } 67 | 68 | 69 | 70 | /** 71 | * 0 - readable number; 72 | * 1 - empty; 73 | * 2 - not a number; 74 | * 3 - only a minus sign; 75 | */ 76 | private static int checkInteger(String s) { 77 | if (s == null || s.isEmpty()) { 78 | return 1; 79 | } 80 | 81 | // 判断是否只包含负号 82 | if (s.equals("-")) { 83 | return 3; 84 | } 85 | 86 | // 判断是否是负数且后面跟着数字 或是纯数字 87 | if (s.matches("-\\d+") || s.matches("\\d+")) { 88 | return 0; 89 | } 90 | 91 | // 如果不符合以上任何条件,则为无效输入 92 | return 2; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/Utls/AXLangLabel.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.Utls; 2 | 3 | import javafx.animation.KeyFrame; 4 | import javafx.animation.KeyValue; 5 | import javafx.animation.Timeline; 6 | import javafx.geometry.Pos; 7 | import javafx.scene.control.Label; 8 | import javafx.util.Duration; 9 | import mai_onsyn.AnimeFX2.AutoUpdatable; 10 | import mai_onsyn.AnimeFX2.LanguageSwitchable; 11 | import mai_onsyn.AnimeFX2.Styles.DefaultAXLangLabelStyle; 12 | import mai_onsyn.AnimeFX2.Styles.AXLangLabelStyle; 13 | 14 | import java.util.Map; 15 | 16 | public class AXLangLabel extends Label implements LanguageSwitchable, AutoUpdatable { 17 | 18 | private AXLangLabelStyle style = new DefaultAXLangLabelStyle(); 19 | private Timeline timeline = new Timeline(); 20 | 21 | public AXLangLabel(String text) { 22 | super(text); 23 | super.setWrapText(true); 24 | super.setAlignment(Pos.CENTER_LEFT); 25 | } 26 | 27 | public AXLangLabel() { 28 | super(); 29 | super.setWrapText(true); 30 | super.setAlignment(Pos.CENTER_LEFT); 31 | } 32 | 33 | @Override 34 | public void switchLanguage(String str) { 35 | setText(str); 36 | } 37 | 38 | @Override 39 | public Map getLanguageElements() { 40 | return Map.of(); 41 | } 42 | 43 | @Override 44 | public void update() { 45 | timeline.stop(); 46 | timeline = new Timeline(new KeyFrame(Duration.millis(200 * style.getAnimeRate()), 47 | new KeyValue(this.textFillProperty(), style.getFill()) 48 | )); 49 | timeline.play(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/layout/AXScrollPane.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.layout; 2 | 3 | import javafx.scene.Node; 4 | import javafx.scene.control.ScrollPane; 5 | import mai_onsyn.AnimeFX2.Utls.Toolkit; 6 | 7 | public class AXScrollPane extends ScrollPane { 8 | 9 | public AXScrollPane() { 10 | super(); 11 | super.getStylesheets().add("style.css"); 12 | Toolkit.addSmoothScrolling(this); 13 | } 14 | 15 | public AXScrollPane(Node content) { 16 | super(content); 17 | super.getStylesheets().add("style.css"); 18 | Toolkit.addSmoothScrolling(this); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/layout/AXTextInputPopup.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.layout; 2 | 3 | import javafx.beans.value.ChangeListener; 4 | import javafx.geometry.Point2D; 5 | import javafx.scene.control.Label; 6 | import javafx.scene.input.KeyCode; 7 | import javafx.stage.Popup; 8 | import javafx.stage.Window; 9 | import mai_onsyn.AnimeFX2.AutoUpdatable; 10 | import mai_onsyn.AnimeFX2.Module.AXBase; 11 | import mai_onsyn.AnimeFX2.Module.AXTextField; 12 | import mai_onsyn.AnimeFX2.Styles.AXTextInputPopupStyle; 13 | import mai_onsyn.AnimeFX2.Styles.DefaultAXTextInputPopupStyle; 14 | 15 | public class AXTextInputPopup extends Popup implements AutoUpdatable { 16 | 17 | private AXTextInputPopupStyle style = new DefaultAXTextInputPopupStyle(); 18 | 19 | private final AXBase pane = new AXBase(); 20 | private final Label label = new Label(); 21 | private final AXTextField textField = new AXTextField(); 22 | 23 | private ChangeListener onTextAvailable; 24 | 25 | public AXTextInputPopup() { 26 | pane.setTheme(style); 27 | textField.setTheme(style); 28 | 29 | pane.getChildren().addAll(label, textField); 30 | 31 | pane.setPosition(label, AutoPane.AlignmentMode.CENTER, AutoPane.LocateMode.RELATIVE, 0.5, 0.3); 32 | pane.setPosition(textField, AutoPane.AlignmentMode.CENTER, AutoPane.LocateMode.RELATIVE, 0.5, 0.6); 33 | 34 | //Draggable 35 | { 36 | final double[] offset = new double[2]; 37 | pane.setOnMouseDragged(event -> { 38 | super.setX(event.getScreenX() - offset[0]); 39 | super.setY(event.getScreenY() - offset[1]); 40 | }); 41 | pane.setOnMousePressed(event -> { 42 | pane.requestFocus(); 43 | offset[0] = event.getSceneX(); 44 | offset[1] = event.getSceneY(); 45 | }); 46 | } 47 | 48 | textField.setOnKeyPressed(event -> { 49 | if (event.getCode() == KeyCode.ENTER && !textField.getText().isEmpty()) { 50 | if (onTextAvailable != null) { 51 | onTextAvailable.changed(null, null, textField.getText()); 52 | } 53 | this.hide(); 54 | } 55 | }); 56 | 57 | super.getContent().add(pane); 58 | super.setAutoHide(true); 59 | 60 | update(); 61 | } 62 | 63 | public void setTheme(AXTextInputPopupStyle style) { 64 | this.style = style; 65 | } 66 | 67 | public void show(String label, Window parent) { 68 | this.label.setText(label); 69 | super.show(parent); 70 | pane.flush(); 71 | } 72 | 73 | public void show(String label, String text, Window parent) { 74 | this.show(label, parent); 75 | textField.setText(text); 76 | textField.textField().selectAll(); 77 | } 78 | 79 | public void showOnCenter(String label, Window parent) { 80 | Point2D c = getPopupCorner(style.getWidth(), style.getHeight(), parent); 81 | super.show(parent, c.getX(), c.getY()); 82 | 83 | this.label.setText(label); 84 | textField.textField().selectAll(); 85 | pane.flush(); 86 | } 87 | 88 | public void showOnCenter(String label, String text, Window parent) { 89 | showOnCenter(label, parent); 90 | 91 | textField.setText(text); 92 | } 93 | 94 | public void setOnTextAvailable(ChangeListener listener) { 95 | this.onTextAvailable = listener; 96 | } 97 | 98 | @Override 99 | public void update() { 100 | label.setTextFill(style.getTextColor()); 101 | label.setFont(style.getTextFont()); 102 | 103 | textField.setMaxSize(style.getWidth() * 0.8, style.getTextFieldHeight()); 104 | textField.setMinSize(style.getWidth() * 0.8, style.getTextFieldHeight()); 105 | 106 | pane.setMaxSize(style.getWidth(), style.getHeight()); 107 | pane.setMinSize(style.getWidth(), style.getHeight()); 108 | 109 | pane.update(); 110 | textField.update(); 111 | } 112 | 113 | public void clear() { 114 | textField.clear(); 115 | } 116 | 117 | public AXTextInputPopupStyle style() { 118 | return style; 119 | } 120 | 121 | private static Point2D getPopupCorner(double popupWidth, double popupHeight, Window window) { 122 | // 获取窗口的中心坐标 123 | double centerX = window.getX() + window.getWidth() / 2; 124 | double centerY = window.getY() + window.getHeight() / 2; 125 | 126 | // 计算Popup左上角的坐标 127 | double popupX = centerX - popupWidth / 2; 128 | double popupY = centerY - popupHeight / 2; 129 | 130 | // 返回左上角的Point2D 131 | return new Point2D(popupX, popupY); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/layout/HDoubleSplitPane.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.layout; 2 | 3 | import javafx.beans.property.SimpleDoubleProperty; 4 | import javafx.scene.Cursor; 5 | import javafx.scene.layout.Region; 6 | 7 | public class HDoubleSplitPane extends AutoPane { 8 | 9 | private final AutoPane right = new AutoPane(); 10 | private final AutoPane left = new AutoPane(); 11 | private final Region divider = new Region(); 12 | 13 | private final SimpleDoubleProperty valueProperty; 14 | 15 | 16 | public HDoubleSplitPane(double dividerWidth, double value, double lInsets, double rInsets) { 17 | 18 | if (value < 0.0 || value > 1.0) { 19 | throw new IllegalArgumentException("value must be between 0.0 and 1.0"); 20 | } 21 | valueProperty = new SimpleDoubleProperty(value); 22 | 23 | AutoPane leftBox = new AutoPane(); 24 | AutoPane rightBox = new AutoPane(); 25 | 26 | leftBox.getChildren().add(left); 27 | rightBox.getChildren().add(right); 28 | 29 | leftBox.setPosition(left, true, false, true, true, 0, dividerWidth / 2, 0, 0); 30 | rightBox.setPosition(right, false, true, true, true, dividerWidth / 2, 0, 0, 0); 31 | 32 | super.getChildren().addAll(leftBox, rightBox, divider); 33 | super.setPosition(leftBox, true, 0, 1 - value, 0, 0); 34 | super.setPosition(rightBox, true, value, 0, 0, 0); 35 | 36 | //divider.setStyle("-fx-background-color: #0020ff40;"); 37 | 38 | divider.setPrefWidth(dividerWidth); 39 | divider.prefHeightProperty().bind(super.heightProperty()); 40 | divider.layoutXProperty().bind(super.widthProperty().multiply(valueProperty).subtract(dividerWidth / 2)); 41 | 42 | divider.setCursor(Cursor.H_RESIZE); 43 | 44 | 45 | divider.setOnMousePressed(event -> { 46 | divider.setUserData(event.getScreenX()); 47 | }); 48 | divider.setOnMouseDragged(event -> { 49 | double deltaX = event.getScreenX() - (double) divider.getUserData(); 50 | 51 | divider.setUserData(event.getScreenX()); 52 | 53 | double trackStartX = super.localToScreen(super.getBoundsInLocal()).getMinX(); 54 | double trackEndX = super.localToScreen(super.getBoundsInLocal()).getMaxX(); 55 | double trackWidth = super.getWidth(); 56 | 57 | if (event.getScreenX() < trackStartX + lInsets) { 58 | valueProperty.set(lInsets / trackWidth); 59 | } else if (event.getScreenX() > trackEndX - rInsets) { 60 | valueProperty.set(1 - rInsets / trackWidth); 61 | } else { 62 | double deltaProgress = deltaX / trackWidth; 63 | 64 | double newProgress = Math.max(0, Math.min(valueProperty.get() + deltaProgress, 1)); 65 | valueProperty.set(newProgress); 66 | } 67 | }); 68 | 69 | 70 | valueProperty.addListener((o, ov, nv) -> { 71 | super.setPosition(leftBox, true, 0, 1 - nv.doubleValue(), 0, 0); 72 | super.setPosition(rightBox, true, nv.doubleValue(), 0, 0, 0); 73 | }); 74 | } 75 | 76 | 77 | public AutoPane getRight() { 78 | return right; 79 | } 80 | 81 | public AutoPane getLeft() { 82 | return left; 83 | } 84 | 85 | public SimpleDoubleProperty valueProperty() { 86 | return valueProperty; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/AnimeFX2/layout/VDoubleSplitPane.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.AnimeFX2.layout; 2 | 3 | import javafx.beans.property.SimpleDoubleProperty; 4 | import javafx.scene.Cursor; 5 | import javafx.scene.layout.Region; 6 | 7 | public class VDoubleSplitPane extends AutoPane { 8 | 9 | private final AutoPane top = new AutoPane(); 10 | private final AutoPane bottom = new AutoPane(); 11 | private final Region divider = new Region(); 12 | 13 | private final SimpleDoubleProperty valueProperty; 14 | 15 | public VDoubleSplitPane(double dividerHeight, double value, double tInsets, double bInsets) { 16 | 17 | if (value < 0.0 || value > 1.0) { 18 | throw new IllegalArgumentException("value must be between 0.0 and 1.0"); 19 | } 20 | valueProperty = new SimpleDoubleProperty(value); 21 | 22 | AutoPane topBox = new AutoPane(); 23 | AutoPane bottomBox = new AutoPane(); 24 | 25 | topBox.getChildren().add(top); 26 | bottomBox.getChildren().add(bottom); 27 | 28 | topBox.setPosition(top, true, true, true, false, 0, 0, 0, dividerHeight / 2); 29 | bottomBox.setPosition(bottom, true, true, false, true, 0, 0, dividerHeight / 2, 0); 30 | 31 | super.getChildren().addAll(topBox, bottomBox, divider); 32 | super.setPosition(topBox, true, 0, 0, 0, 1 - value); 33 | super.setPosition(bottomBox, true, 0, 0, value, 0); 34 | 35 | //divider.setStyle("-fx-background-color: #0020ff40;"); 36 | 37 | divider.setPrefHeight(dividerHeight); 38 | divider.prefWidthProperty().bind(super.widthProperty()); 39 | divider.layoutYProperty().bind(super.heightProperty().multiply(valueProperty).subtract(dividerHeight / 2)); 40 | 41 | divider.setCursor(Cursor.V_RESIZE); 42 | 43 | divider.setOnMousePressed(event -> { 44 | divider.setUserData(event.getScreenY()); 45 | }); 46 | divider.setOnMouseDragged(event -> { 47 | double deltaY = event.getScreenY() - (double) divider.getUserData(); 48 | 49 | divider.setUserData(event.getScreenY()); 50 | 51 | double trackStartY = super.localToScreen(super.getBoundsInLocal()).getMinY(); 52 | double trackEndY = super.localToScreen(super.getBoundsInLocal()).getMaxY(); 53 | double trackHeight = super.getHeight(); 54 | 55 | if (event.getScreenY() < trackStartY + tInsets) { 56 | valueProperty.set(tInsets / trackHeight); 57 | } else if (event.getScreenY() > trackEndY - bInsets) { 58 | valueProperty.set(1 - bInsets / trackHeight); 59 | } else { 60 | double deltaProgress = deltaY / trackHeight; 61 | 62 | double newProgress = Math.max(0, Math.min(valueProperty.get() + deltaProgress, 1)); 63 | valueProperty.set(newProgress); 64 | } 65 | }); 66 | 67 | valueProperty.addListener((o, ov, nv) -> { 68 | super.setPosition(topBox, true, 0, 0, 0, 1 - nv.doubleValue()); 69 | super.setPosition(bottomBox, true, 0, 0, nv.doubleValue(), 0); 70 | }); 71 | } 72 | 73 | public AutoPane getTop() { 74 | return top; 75 | } 76 | 77 | public AutoPane getBottom() { 78 | return bottom; 79 | } 80 | 81 | public SimpleDoubleProperty valueProperty() { 82 | return valueProperty; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/App/AppConfig.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.App; 2 | 3 | import mai_onsyn.VeloVoice.Utils.Util; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class AppConfig { 10 | public enum LoadType { 11 | LOCAL_DIRECTLY(0), 12 | LOCAL_FULL(1), 13 | LOCAL_VOLUMED(2), 14 | LOCAL_EPUB(3), 15 | WEN_KU8(4), 16 | LI_NOVEL(5), 17 | KAKUYOMU(6); 18 | 19 | LoadType(int i) {} 20 | 21 | public static LoadType valueOf(int value) { 22 | for (LoadType a : LoadType.values()) { 23 | if (a.ordinal() == value) { 24 | return a; 25 | } 26 | } 27 | throw new IllegalArgumentException("No enum constant with value " + value); 28 | } 29 | } 30 | 31 | public static boolean pausing = false; 32 | public static boolean isWindowSupport = Util.isWindowSupport() && Theme.enableWinUI; 33 | 34 | public static LoadType loadType = LoadType.LOCAL_DIRECTLY; 35 | public static String voiceModel = "zh-CN-XiaoxiaoNeural"; 36 | public static double configWindowChangeWidth = 820; 37 | 38 | public static int logLevel = 0; 39 | 40 | public static int connectThread = 2; 41 | public static int retryCount = 5; 42 | public static int timeoutSeconds = 10; 43 | public static String Sec_MS_GEC_api = "http://127.0.0.1:44165/api"; 44 | public static boolean builtin_Sec_MS_GEC_generation = true; 45 | 46 | public static int textPieceSize = 256; 47 | public static List textSplitSymbols = new ArrayList<>(Arrays.asList('。', '?', '!', '…', ',', ' ')); 48 | public static boolean isAppendVolumeName = true; //为网络小说的每个章节的章节名字前加上该章节所属卷的卷名 49 | public static boolean isAppendOrdinal = true; //生成音频文件时是否为文件以及文件夹添加序号 50 | public static boolean isAppendExtraChapterName = false; //为网络章节添加额外的章(前面添加"第%d章") 51 | public static boolean isAppendExtraVolumeName = false; //为网络章节添加额外的卷名(前面添加"第%d卷") 52 | 53 | public static String previewText = "全名制作人们大家好,我是练习时长2.5年的个人练习生0d00,喜欢唱、跳、ciallo、0b0000001011010001"; 54 | public static boolean splitChapter = false; //分割生成的音频 55 | public static double maxAudioDuration = 20; //每个音频文件的最大时长 56 | public static boolean isAppendNameForSplitChapter = true; //分割的每个音频文件的开头都添加该章节的名称 57 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/App/Constants.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.App; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | import java.io.IOException; 6 | import java.net.URI; 7 | import java.net.http.HttpClient; 8 | import java.net.http.HttpRequest; 9 | import java.net.http.HttpResponse; 10 | import java.nio.charset.StandardCharsets; 11 | import java.security.MessageDigest; 12 | import java.security.NoSuchAlgorithmException; 13 | 14 | public class Constants { 15 | 16 | public static String URL; 17 | 18 | public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55"; 19 | public static final String ORIGIN = "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold"; 20 | public static final String VOICES_LIST_URL = "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4"; 21 | 22 | static { 23 | UpdateSec_MS_GEC(); 24 | } 25 | 26 | 27 | 28 | private static final long COOLDOWN_PERIOD_MS = 30 * 1000; 29 | private static volatile long lastUpdateTimestamp = 0; 30 | 31 | 32 | public static void UpdateSec_MS_GEC() { 33 | 34 | String SEC_MS_GEC; 35 | String SEC_MS_GEC_Version; 36 | 37 | if (!AppConfig.builtin_Sec_MS_GEC_generation) { 38 | long currentTime = System.currentTimeMillis(); 39 | if (currentTime - lastUpdateTimestamp < COOLDOWN_PERIOD_MS) { 40 | System.out.println("UpdateSec_MS_GEC() called too soon. Skipping..."); 41 | return; 42 | } 43 | synchronized (Constants.class) { 44 | if (currentTime - lastUpdateTimestamp < COOLDOWN_PERIOD_MS) { 45 | return; 46 | } 47 | lastUpdateTimestamp = currentTime; 48 | } 49 | 50 | HttpRequest request = HttpRequest.newBuilder() 51 | .uri(URI.create(AppConfig.Sec_MS_GEC_api)) 52 | .GET() 53 | .build(); 54 | HttpClient httpClient = HttpClient.newBuilder().build(); 55 | 56 | try { 57 | HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); 58 | 59 | System.out.println(response.body()); 60 | JSONObject responseJson = JSONObject.parseObject(response.body()); 61 | 62 | SEC_MS_GEC = responseJson.getString("Sec-MS-GEC"); 63 | SEC_MS_GEC_Version = responseJson.getString("Sec-MS-GEC-Version"); 64 | 65 | 66 | 67 | //System.out.println("Adjusted SEC_MS_GEC to : " + SEC_MS_GEC); 68 | } catch (IOException | InterruptedException e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | else { 73 | SEC_MS_GEC = genSecMsGec(); 74 | SEC_MS_GEC_Version = "1-131.0.2903.112"; 75 | } 76 | 77 | URL = String.format("wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4&Sec-MS-GEC=%s&Sec-MS-GEC-Version=%s", SEC_MS_GEC, SEC_MS_GEC_Version); 78 | } 79 | 80 | private static String genSecMsGec() { 81 | long currentTime = System.currentTimeMillis(); 82 | long ticks = (long) ((currentTime / 1000.0) + 11644473600L) * 10000000L; 83 | long roundedTicks = ticks - (ticks % 3000000000L); 84 | String strToHash = roundedTicks + "6A5AA1D4EAFF4E9FB37E23D68491D6F4"; 85 | 86 | try { 87 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 88 | byte[] hash = sha256.digest(strToHash.getBytes(StandardCharsets.UTF_8)); 89 | 90 | StringBuilder hexString = new StringBuilder(); 91 | for (byte b : hash) { 92 | String hex = Integer.toHexString(0xff & b); 93 | if (hex.length() == 1) { 94 | hexString.append('0'); 95 | } 96 | hexString.append(hex); 97 | } 98 | 99 | return hexString.toString().toUpperCase(); 100 | } catch (NoSuchAlgorithmException e) { 101 | throw new RuntimeException(e); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/App/Runtime.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.App; 2 | 3 | import javafx.beans.property.SimpleIntegerProperty; 4 | import mai_onsyn.AnimeFX.Frame.Layout.AutoPane; 5 | import mai_onsyn.AnimeFX.Frame.Module.FXLogger; 6 | 7 | public class Runtime { 8 | 9 | public static boolean systemSupportButLibraryNotExist = false; 10 | public static boolean consoleMode = false; 11 | 12 | public static boolean runningState = false; 13 | public static Thread TaskMainThread; 14 | 15 | public static FXLogger logger; 16 | 17 | public static final SimpleIntegerProperty totalProgress = new SimpleIntegerProperty(0); 18 | public static volatile SimpleIntegerProperty currentProgress = new SimpleIntegerProperty(0); 19 | public static AutoPane progressPane = new AutoPane(); 20 | public static String currentFile; 21 | public static int totalCount; 22 | public static int currentCount; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/App/Theme.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.App; 2 | 3 | import com.kieferlam.javafxblur.Blur; 4 | import javafx.scene.image.Image; 5 | import javafx.scene.paint.Color; 6 | import javafx.scene.text.Font; 7 | import mai_onsyn.AnimeFX.Frame.Utils.Toolkit; 8 | import mai_onsyn.VeloVoice.Utils.Util; 9 | 10 | import java.util.List; 11 | 12 | public class Theme { 13 | public static List ALTERNATE_THEMES = List.of( 14 | Color.web("#dc143c", 1), 15 | Color.web("#ff1493", 1), 16 | Color.web("#ff00ff", 1), 17 | Color.web("#8b008b", 1), 18 | Color.web("#9400d3", 1), 19 | Color.web("#4b0082", 1), 20 | Color.web("#7b68ee", 1), 21 | Color.web("#000080", 1), 22 | Color.web("#6495ed", 1), 23 | Color.web("#4682b4", 1), 24 | Color.web("#00bfff", 1), 25 | Color.web("#00ced1", 1), 26 | Color.web("#008b8b", 1), 27 | Color.web("#00fa9a", 1), 28 | Color.web("#3cb371", 1), 29 | Color.web("#556b2f", 1), 30 | Color.web("#bdb76b", 1), 31 | Color.web("#daa520", 1), 32 | Color.web("#cd853f", 1), 33 | Color.web("#a0522d", 1), 34 | Color.web("#fa8072", 1), 35 | Color.web("#b22222", 1) 36 | ); 37 | 38 | public static boolean LIGHT_THEME = ConfigListener.light_theme_cache; 39 | public static double BACKGROUND_AMBIGUITY = 25; 40 | public static double BACKGROUND_BRIGHTNESS = 0.75; 41 | public static String BACKGROUND_IMAGE_URI = "textures/bg.png"; 42 | 43 | public static boolean enableWinUI = false; 44 | public static Blur blurMode = Blur.ACRYLIC; 45 | 46 | public static Image BACKGROUND_IMAGE; 47 | public static final Font FONT_NORMAL = new Font(14); 48 | public static final Font FONT_SMALLER = new Font(12.25); 49 | 50 | public static final Color DARK_THEME_COLOR = new Color(0.1, 0.1, 0.1, 0.75); 51 | public static final Color LIGHT_THEME_COLOR = new Color(0.97, 0.97, 0.97, 0.75); 52 | 53 | public static final int BUTTON_ANIME_DURATION = 300; 54 | 55 | 56 | public static Color THEME_COLOR = ConfigListener.theme_color_cache; 57 | public static final Color MODULE_BG_COLOR; 58 | public static final Color MODULE_BG_TRANSPERTANT_COLOR; 59 | 60 | public static final Color BUTTON_FOCUS_COLOR; 61 | public static final Color BUTTON_PRESSED_COLOR; 62 | public static final Color TEXT_COLOR; 63 | public static final Color BUTTON_TEXT_FOCUS_COLOR; 64 | 65 | public static final Color SLIDER_BG_COLOR; 66 | public static final Color TRANSPERTANT_THEME_COLOR; 67 | public static final Color SLIDER_THUMB_COLOR; 68 | public static final Color SLIDER_THUMB_HOVER_COLOR; 69 | public static final Color SLIDER_THUMB_PRESSED_COLOR; 70 | 71 | public static final Color TREE_SWITCHED_BACKGROUND_COLOR; 72 | public static final Color TREE_SWITCHED_BACKGROUND_FOCUS_COLOR; 73 | public static final Color TREE_SWITCHED_TEXT_COLOR; 74 | public static final Color TREE_SWITCHED_TEXT_FOCUS_COLOR; 75 | 76 | static { 77 | MODULE_BG_COLOR = LIGHT_THEME ? Toolkit.adjustOpacity(LIGHT_THEME_COLOR, 0.1) : Toolkit.adjustOpacity(DARK_THEME_COLOR, 0.1); 78 | MODULE_BG_TRANSPERTANT_COLOR = Toolkit.adjustOpacity(MODULE_BG_COLOR, 0); 79 | 80 | BUTTON_FOCUS_COLOR = new Color(0.6, 0.6, 0.6, 0.5); 81 | BUTTON_PRESSED_COLOR = Toolkit.adjustBrightness(BUTTON_FOCUS_COLOR, LIGHT_THEME ? 0.4 : 0.8); 82 | TEXT_COLOR = LIGHT_THEME ? new Color(0, 0, 0, 1) : new Color(1, 1, 1, 1); 83 | BUTTON_TEXT_FOCUS_COLOR = THEME_COLOR; 84 | 85 | SLIDER_BG_COLOR = new Color(1, 1, 1, 0.3); 86 | TRANSPERTANT_THEME_COLOR = new Color(THEME_COLOR.getRed(), THEME_COLOR.getGreen(), THEME_COLOR.getBlue(), 0.3); 87 | SLIDER_THUMB_COLOR = new Color(1, 1, 1, 1); 88 | SLIDER_THUMB_HOVER_COLOR = new Color(0.9, 0.9, 0.9, 1); 89 | SLIDER_THUMB_PRESSED_COLOR = new Color(0.8, 0.8, 0.8, 1); 90 | 91 | TREE_SWITCHED_BACKGROUND_COLOR = Toolkit.adjustBrightness(THEME_COLOR, THEME_COLOR.getBrightness() - 0.2, 0.3); 92 | TREE_SWITCHED_BACKGROUND_FOCUS_COLOR = Toolkit.adjustBrightness(THEME_COLOR, THEME_COLOR.getBrightness() + 0.2, 0.3); 93 | TREE_SWITCHED_TEXT_COLOR = Toolkit.adjustBrightness(Toolkit.adjustHue(TREE_SWITCHED_BACKGROUND_COLOR, TREE_SWITCHED_BACKGROUND_COLOR.getHue() + 180, 1), LIGHT_THEME ? 0.2 : 1); 94 | TREE_SWITCHED_TEXT_FOCUS_COLOR = Toolkit.adjustBrightness(Toolkit.adjustHue(TREE_SWITCHED_BACKGROUND_FOCUS_COLOR, TREE_SWITCHED_BACKGROUND_FOCUS_COLOR.getHue() + 180, 1), LIGHT_THEME ? 0.6 : 0.8); 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/FrameApp.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice; 2 | 3 | import com.kieferlam.javafxblur.Blur; 4 | import javafx.application.Application; 5 | import javafx.scene.Scene; 6 | import javafx.scene.image.Image; 7 | import javafx.scene.layout.Background; 8 | import javafx.scene.paint.Color; 9 | import javafx.scene.shape.Rectangle; 10 | import javafx.stage.Stage; 11 | import javafx.stage.StageStyle; 12 | import mai_onsyn.AnimeFX.Frame.Layout.AutoPane; 13 | import mai_onsyn.AnimeFX.Frame.Layout.NamePopup; 14 | import mai_onsyn.AnimeFX.Frame.Module.DiffusionButton; 15 | import mai_onsyn.VeloVoice.App.Runtime; 16 | import mai_onsyn.VeloVoice.App.Theme; 17 | import mai_onsyn.VeloVoice.Utils.Util; 18 | 19 | import static mai_onsyn.VeloVoice.App.AppConfig.*; 20 | import static mai_onsyn.VeloVoice.App.Runtime.*; 21 | 22 | public class FrameApp extends Application { 23 | 24 | public static AutoPane rootPane; 25 | public static Stage STAGE; 26 | @Override 27 | public void start(Stage stage) { 28 | stage.setTitle("VeloVoice"); 29 | STAGE = stage; 30 | AutoPane root = new AutoPane(); 31 | rootPane = root; 32 | root.setOnMousePressed(_ -> root.requestFocus()); 33 | stage.setOnShown(_ -> root.requestFocus()); 34 | Scene scene = new Scene(root); 35 | stage.setScene(scene); 36 | if (isWindowSupport) { 37 | scene.setFill(Color.TRANSPARENT); 38 | root.setBackground(Background.EMPTY); 39 | stage.initStyle(StageStyle.TRANSPARENT); 40 | } 41 | NamePopup.setStage(stage); 42 | 43 | 44 | 45 | drawRoot(root); 46 | 47 | 48 | 49 | root.setPrefSize(1280, 720); 50 | stage.setMinWidth(960); 51 | stage.setMinHeight(540); 52 | stage.getIcons().add(new Image("textures/icon.png")); 53 | stage.show(); 54 | 55 | if (isWindowSupport) Blur.applyBlur(stage, Theme.blurMode); 56 | 57 | logger.prompt("Application launched!"); 58 | if (systemSupportButLibraryNotExist) logger.error("找不到文件:javafxblur.dll"); 59 | } 60 | 61 | private void drawRoot(AutoPane root) { 62 | AutoPane contentPane = new AutoPane(); 63 | 64 | AutoPane treeArea = FrameFactory.getTreeArea(); 65 | AutoPane textArea = FrameFactory.getTextArea(); 66 | AutoPane operationArea = FrameFactory.getOperationArea(); 67 | DiffusionButton settingButton = FrameFactory.getSettingsButton(); 68 | FrameFactory.drawProgressArea(); 69 | 70 | contentPane.setPosition(treeArea, false, true, false, false, 50, 0.75, 50, 50); 71 | contentPane.setPosition(textArea, true, true, false, false, 0.28, 0.35, 50, 50); 72 | contentPane.setPosition(operationArea, true, false, false, false, 0.68, 50, 50, 50); 73 | contentPane.setPosition(settingButton, false, false, false, false, 5, 45, 5, 45); 74 | contentPane.setPosition(progressPane, false, false, false, false, 50, 50, 40, 10); 75 | contentPane.flipRelativeMode(settingButton, AutoPane.Motion.LEFT); 76 | contentPane.flipRelativeMode(settingButton, AutoPane.Motion.BOTTOM); 77 | contentPane.flipRelativeMode(progressPane, AutoPane.Motion.TOP); 78 | progressPane.setVisible(false); 79 | 80 | contentPane.getChildren().addAll(treeArea, textArea, operationArea, settingButton, progressPane); 81 | if (isWindowSupport) { 82 | //window shadow 83 | Rectangle WINDOW_SHADOW = new Rectangle(); 84 | WINDOW_SHADOW.setFill(Theme.LIGHT_THEME ? Theme.LIGHT_THEME_COLOR : Theme.DARK_THEME_COLOR); 85 | WINDOW_SHADOW.setOpacity(0.01); 86 | root.setPosition(WINDOW_SHADOW, false, 0, 0, 0, 0); 87 | AutoPane windowBar = FrameFactory.getWindowModule(STAGE); 88 | root.setPosition(windowBar, false, 0, 0, 0, 28); 89 | root.setPosition(contentPane, false, 0, 0, 28, 0); 90 | root.flipRelativeMode(windowBar, AutoPane.Motion.BOTTOM); 91 | root.getChildren().addAll(WINDOW_SHADOW, windowBar, contentPane); 92 | Util.DrawUtil.addDrawFunc(STAGE, root); 93 | } 94 | else { 95 | root.setPosition(contentPane, false, 0, 0, 0, 0); 96 | root.getChildren().add(contentPane); 97 | } 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/Main.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice; 2 | 3 | import javafx.application.Application; 4 | import mai_onsyn.VeloVoice.App.ConfigListener; 5 | import mai_onsyn.VeloVoice.App.Runtime; 6 | import mai_onsyn.VeloVoice.App.Theme; 7 | 8 | import java.util.Objects; 9 | 10 | import static mai_onsyn.VeloVoice.App.AppConfig.isWindowSupport; 11 | 12 | public class Main { 13 | 14 | static { 15 | Thread configSaveThread = ConfigListener.CONFIG_SAVE_THREAD; //加载ConfigListener类的静态代码块 16 | configSaveThread.start(); 17 | try { 18 | if (isWindowSupport) System.loadLibrary("javafxblur"); //Blur.loadBlurLibrary(); 19 | } catch (UnsatisfiedLinkError e) { 20 | Runtime.systemSupportButLibraryNotExist = true; 21 | isWindowSupport = false; 22 | Theme.enableWinUI = false; 23 | } 24 | } 25 | 26 | public static void main(String[] args) { 27 | 28 | if (args.length != 0) { 29 | if (Objects.equals(args[0], "no-gui")) { 30 | Runtime.consoleMode = true; 31 | ConsoleApp.main(args); 32 | return; 33 | } 34 | } 35 | //System.out.println(Arrays.toString(args)); 36 | 37 | Application.launch(FrameApp.class); 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/NetWork/Crawler/HttpFactory.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.NetWork.Crawler; 2 | 3 | import mai_onsyn.VeloVoice.App.AppConfig; 4 | import org.jsoup.Jsoup; 5 | import org.jsoup.nodes.Document; 6 | 7 | import java.io.IOException; 8 | import java.net.*; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.concurrent.ExecutionException; 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.Future; 15 | 16 | public class HttpFactory { 17 | 18 | // public static void main(String[] args) throws Exception { 19 | // 20 | // Structure> structure = new WenKu8("https://www.wenku8.net/book/1538.htm").getContents(); 21 | // 22 | // System.out.println(structure.toString()); 23 | // 24 | // structure.getChildren().getFirst().getChildren().getFirst().getData().forEach(System.out::println); 25 | // 26 | // } 27 | 28 | @Deprecated 29 | public static Document getDocument2(String url) { 30 | try { 31 | return Jsoup.parse(URI.create(url).toURL(), AppConfig.timeoutSeconds * 1000); 32 | } catch (IOException e) { 33 | throw new RuntimeException("Connect Failed: " + url, e); 34 | } 35 | } 36 | 37 | public static Document getDocument(String url) { 38 | int retries = 0; 39 | while (retries < AppConfig.retryCount) { 40 | try { 41 | return Jsoup.parse(URI.create(url).toURL(), AppConfig.timeoutSeconds * 1000); 42 | } catch (IOException e) { 43 | retries++; 44 | System.out.println("retry: " + retries); 45 | if (retries >= AppConfig.retryCount) { 46 | throw new RuntimeException("Connect Failed after " + AppConfig.retryCount + " retries: " + url, e); 47 | } 48 | try { 49 | Thread.sleep(500); 50 | } catch (InterruptedException ie) { 51 | Thread.currentThread().interrupt(); 52 | throw new RuntimeException("Thread interrupted while waiting for retry", ie); 53 | } 54 | } 55 | } 56 | throw new RuntimeException("Unreachable code"); 57 | } 58 | 59 | public static List getDocuments(List urls) { 60 | ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); 61 | 62 | List> futures = new ArrayList<>(); 63 | 64 | for (String url : urls) { 65 | futures.add(executor.submit(() -> getDocument(url))); 66 | } 67 | 68 | List documents = new ArrayList<>(); 69 | 70 | for (Future future : futures) { 71 | try { 72 | documents.add(future.get()); 73 | } catch (InterruptedException | ExecutionException e) { 74 | throw new RuntimeException("Failed to get document", e); 75 | } 76 | } 77 | 78 | return documents; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/NetWork/Crawler/NovelSite.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.NetWork.Crawler; 2 | 3 | import mai_onsyn.VeloVoice.Utils.Structure; 4 | 5 | import java.util.List; 6 | 7 | public interface NovelSite { 8 | 9 | Structure> getContents(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/NetWork/TTS/ChatTTSClient.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.NetWork.TTS; 2 | 3 | import java.net.URI; 4 | import java.net.http.HttpRequest; 5 | import java.util.List; 6 | import java.util.UUID; 7 | 8 | public class ChatTTSClient implements TTSClient{ 9 | @Override 10 | public void connect() { 11 | 12 | } 13 | 14 | @Override 15 | public boolean isOpen() { 16 | return false; 17 | } 18 | 19 | @Override 20 | public void close() { 21 | 22 | } 23 | 24 | @Override 25 | public List sendText(UUID uuid, String text) throws InterruptedException { 26 | HttpRequest request = HttpRequest.newBuilder() 27 | .uri(URI.create("http://127.0.0.1:9966/")) 28 | .GET() 29 | .build(); 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/NetWork/TTS/FixedEdgeTTSClient.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.NetWork.TTS; 2 | 3 | import mai_onsyn.VeloVoice.App.AppConfig; 4 | import mai_onsyn.VeloVoice.App.Constants; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | import static mai_onsyn.VeloVoice.App.Runtime.logger; 10 | 11 | public class FixedEdgeTTSClient implements TTSClient { 12 | 13 | private EdgeTTSClient client; 14 | 15 | public FixedEdgeTTSClient() { 16 | } 17 | 18 | @Override 19 | public void connect() { 20 | int tryCount = -1; 21 | while (tryCount < AppConfig.retryCount) { 22 | tryCount++; 23 | client = new EdgeTTSClient(); 24 | 25 | client.connect(); 26 | 27 | if (!client.isOpen()) { 28 | client.close(); 29 | Constants.UpdateSec_MS_GEC(); 30 | if (logger != null) logger.warn(String.format("线程%s连接错误,尝试第%d次重连", Thread.currentThread().getName(), tryCount + 1)); 31 | else System.out.println(String.format("线程%s连接错误,尝试第%d次重连", Thread.currentThread().getName(), tryCount + 1)); 32 | //client.connect(); 33 | continue; 34 | } 35 | 36 | break; 37 | } 38 | } 39 | 40 | @Override 41 | public boolean isOpen() { 42 | return client.isOpen(); 43 | } 44 | 45 | @Override 46 | public void close() { 47 | client.close(); 48 | } 49 | 50 | @Override 51 | public List sendText(UUID uuid, String text) throws InterruptedException { 52 | List bytes = client.sendText(uuid, text); 53 | 54 | if (!checkBytes(bytes)) { 55 | reconnect(); 56 | bytes = client.sendText(uuid, text); 57 | } 58 | 59 | return bytes; 60 | } 61 | 62 | private void reconnect() { 63 | close(); 64 | connect(); 65 | System.out.println("Reconnected"); 66 | } 67 | 68 | private boolean checkBytes(List bytes) { 69 | if (bytes == null || bytes.isEmpty()) return false; 70 | 71 | boolean hasNull = false; 72 | for (byte[] b : bytes) { 73 | if (b == null) { 74 | hasNull = true; 75 | break; 76 | } 77 | } 78 | 79 | return !hasNull; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/NetWork/TTS/TTSClient.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.NetWork.TTS; 2 | 3 | import java.util.List; 4 | import java.util.UUID; 5 | 6 | public interface TTSClient { 7 | void connect(); 8 | 9 | boolean isOpen(); 10 | 11 | void close(); 12 | 13 | List sendText(UUID uuid, String text) throws InterruptedException; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/NetWork/Voice.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.NetWork; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | //import com.alibaba.fastjson2.JSON; 6 | //import com.alibaba.fastjson2.JSONArray; 7 | 8 | import java.io.*; 9 | import java.net.URI; 10 | import java.net.http.HttpClient; 11 | import java.net.http.HttpRequest; 12 | import java.net.http.HttpResponse; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import static mai_onsyn.VeloVoice.App.Constants.*; 17 | 18 | public class Voice { 19 | 20 | private static final List voiceList = new ArrayList<>(); 21 | static { 22 | flush(); 23 | } 24 | 25 | private static void listVoice() { 26 | HttpRequest request = HttpRequest.newBuilder() 27 | .uri(URI.create(VOICES_LIST_URL)) 28 | .header("Authority", "speech.platform.bing.com") 29 | .header("Sec-CH-UA", "\" Not;A Brand\";v=\"99\", \"Microsoft Edge\";v=\"91\", \"Chromium\";v=\"91\"") 30 | .header("Sec-CH-UA-Mobile", "?0") 31 | .header("Cache-Control", "no-cache") 32 | .header("User-Agent", USER_AGENT) 33 | .header("Accept", "*/*") 34 | .header("Sec-Fetch-Site", "none") 35 | .header("Sec-Fetch-Mode", "cors") 36 | .header("Sec-Fetch-Dest", "empty") 37 | .header("Accept-Encoding", "gzip, deflate, br") 38 | .header("Accept-Language", "en-US,en;q=0.9") 39 | .version(HttpClient.Version.HTTP_2).GET().build(); 40 | 41 | HttpClient httpClient = HttpClient.newBuilder().build(); 42 | HttpResponse response; 43 | try { 44 | response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); 45 | 46 | if (response.body() != null) { 47 | //JSONArray jsonArray = JSON.parseArray(response.body()); 48 | JSONArray jsonArray = JSONArray.parseArray(response.body()); 49 | voiceList.clear(); 50 | for (Object o : jsonArray) { 51 | voiceList.add(com.alibaba.fastjson.JSON.parseObject(o.toString())); 52 | } 53 | voiceList.sort((o1, o2) -> { 54 | String shortName1 = o1.getString("ShortName"); 55 | String shortName2 = o2.getString("ShortName"); 56 | 57 | return -shortName1.compareTo(shortName2); 58 | }); 59 | System.out.println(JSONObject.toJSONString(jsonArray, true)); 60 | } 61 | } catch (IOException | InterruptedException e) { 62 | throw new RuntimeException(e); 63 | } 64 | } 65 | 66 | public static JSONObject get(String shortName) { 67 | for (JSONObject voice : voiceList) { 68 | if (voice.getString("ShortName").equals(shortName)) { 69 | return voice; 70 | } 71 | } 72 | return null; 73 | } 74 | 75 | public static List getVoiceList() { 76 | return voiceList; 77 | } 78 | 79 | public static void flush() { 80 | listVoice(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/Text/TTS.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.Text; 2 | 3 | import mai_onsyn.VeloVoice.App.AppConfig; 4 | import mai_onsyn.VeloVoice.NetWork.TTSExecutor; 5 | import mai_onsyn.VeloVoice.Utils.Structure; 6 | import mai_onsyn.VeloVoice.Utils.Util; 7 | 8 | import java.io.File; 9 | import java.util.List; 10 | 11 | import static mai_onsyn.VeloVoice.App.AppConfig.*; 12 | import static mai_onsyn.VeloVoice.App.Runtime.*; 13 | 14 | public class TTS { 15 | 16 | public static TTSExecutor executor; 17 | 18 | public static void startNewTask(Structure> tree, File structureRoot) { 19 | currentFile = ""; 20 | totalCount = countStructure(tree, 0); 21 | totalProgress.setValue(0); 22 | executor = new TTSExecutor(AppConfig.connectThread); 23 | if (!executor.connect()) { 24 | return; 25 | } 26 | try { 27 | if (tree.getChildren().size() == 1) runTask(tree.getChildren().getFirst(), structureRoot, false, 0, 1); 28 | else for (int i = 0; i < tree.getChildren().size(); i++) { 29 | runTask(tree.getChildren().get(i), structureRoot, isAppendOrdinal, i + 1, tree.getChildren().size()); 30 | } 31 | } catch (InterruptedException e) { 32 | Thread.currentThread().interrupt(); 33 | } 34 | executor.shutdown(); 35 | currentFile = ""; 36 | } 37 | 38 | private static void runTask(Structure> tree, File structureRoot, boolean ordinal, int counter, int rootSize) throws InterruptedException { 39 | //System.out.println(Thread.currentThread().isInterrupted()); 40 | if (Thread.currentThread().isInterrupted()) { 41 | //logger.debug("TTS exit"); 42 | return; //终止信号 43 | } 44 | if (tree.getData() == null) { 45 | structureRoot = new File(structureRoot, ordinal ? String.format("%s. %s", Util.padZero(counter, rootSize), tree.getName()) : tree.getName()); 46 | if (!structureRoot.exists()) if (!structureRoot.mkdirs()) { 47 | if (logger != null) logger.error("无法创建音频输出文件夹 - " + structureRoot.getAbsolutePath()); 48 | else System.out.println("无法创建音频输出文件夹 - " + structureRoot.getAbsolutePath()); 49 | return; 50 | } 51 | 52 | if (tree.getChildren().size() == 1) runTask(tree.getChildren().getFirst(), structureRoot, false, 0, 1); 53 | else for (int i = 0; i < tree.getChildren().size(); i++) { 54 | runTask(tree.getChildren().get(i), structureRoot, isAppendOrdinal, i + 1, tree.getChildren().size()); 55 | } 56 | } 57 | else { 58 | currentFile = tree.getName(); 59 | if (logger != null) logger.info("当前 - \"" + currentFile + "\""); 60 | executor.execute2(tree.getData(), structureRoot, ordinal ? String.format("%s. %s", Util.padZero(counter, rootSize), tree.getName()) : tree.getName()); 61 | } 62 | } 63 | 64 | private static int countStructure(Structure> structure, int count) { 65 | // 检查当前Structure是否包含数据(即List) 66 | if (structure.getData() != null) { 67 | // 将当前Structure的数据(List)的大小加到总数上 68 | count += structure.getData().size(); 69 | } 70 | 71 | // 检查当前Structure是否有子节点 72 | if (structure.getChildren() != null) { 73 | // 遍历所有子节点,并递归调用countStructure方法 74 | for (Structure> child : structure.getChildren()) { 75 | count = countStructure(child, count); 76 | } 77 | } 78 | 79 | // 返回更新后的总数 80 | return count; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/Text/TextLoader.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.Text; 2 | 3 | import org.jsoup.parser.Parser; 4 | import org.mozilla.universalchardet.UniversalDetector; 5 | 6 | import java.io.File; 7 | import java.nio.charset.Charset; 8 | import java.nio.file.Files; 9 | 10 | public class TextLoader { 11 | 12 | public static String load(File file) { 13 | try { 14 | byte[] fileContent = Files.readAllBytes(file.toPath()); 15 | 16 | UniversalDetector detector = new UniversalDetector(null); 17 | detector.handleData(fileContent, 0, fileContent.length); 18 | detector.dataEnd(); 19 | 20 | return decodeHtmlEntities(new String(fileContent, Charset.forName(detector.getDetectedCharset()))); 21 | } catch (Exception e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | //部分小说txt里有html转义符 需要解析 27 | private static String decodeHtmlEntities(String s) { 28 | return Parser.unescapeEntities(s, false); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/Utils/AudioPlayer.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.Utils; 2 | 3 | import javazoom.jl.decoder.JavaLayerException; 4 | import javazoom.jl.player.Player; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | import static mai_onsyn.VeloVoice.App.Runtime.logger; 11 | 12 | public class AudioPlayer { 13 | 14 | public static void play(List byteArrayList) { 15 | try { 16 | byte[] mergedAudio = mergeByteArrays(byteArrayList); 17 | 18 | try (ByteArrayInputStream stream = new ByteArrayInputStream(mergedAudio)) { 19 | Player player = new Player(stream); 20 | player.play(); 21 | } 22 | } catch (JavaLayerException | IOException e) { 23 | logger.error("播放声音时出错:" + e); 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | public static byte[] mergeByteArrays(List byteArrayList) { 29 | int totalLength = 0; 30 | for (byte[] byteArray : byteArrayList) { 31 | totalLength += byteArray.length; 32 | } 33 | 34 | byte[] result = new byte[totalLength]; 35 | int currentIndex = 0; 36 | for (byte[] byteArray : byteArrayList) { 37 | System.arraycopy(byteArray, 0, result, currentIndex, byteArray.length); 38 | currentIndex += byteArray.length; 39 | } 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/Utils/Chapter.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.Utils; 2 | 3 | import java.util.List; 4 | 5 | public class Chapter { 6 | 7 | private String title; 8 | 9 | private List contents; 10 | 11 | public static Chapter parseChapter(List texts) { 12 | 13 | Chapter chapter = new Chapter(); 14 | chapter.contents = texts; 15 | chapter.title = texts.getFirst(); 16 | 17 | return chapter; 18 | } 19 | 20 | public String getTitle() { 21 | return title; 22 | } 23 | 24 | public List getContents() { 25 | return contents; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice/Utils/Volume.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice.Utils; 2 | 3 | import java.io.File; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class Volume { 10 | 11 | private final String volumeName; 12 | 13 | private final List chapters = new ArrayList<>(); 14 | 15 | public Volume(String volumeName) { 16 | this.volumeName = volumeName; 17 | } 18 | 19 | public void addChapter(Chapter... chapter) { 20 | this.chapters.addAll(List.of(chapter)); 21 | } 22 | 23 | public List getChapters() { 24 | return chapters; 25 | } 26 | 27 | public String getVolumeName() { 28 | return volumeName; 29 | } 30 | 31 | public void writeToFile(File parent) { 32 | for (int i = 0; i < chapters.size(); i++) { 33 | File output = new File(parent, String.format("%s/%d. %s.txt", volumeName, i + 1, chapters.get(i).getTitle())); 34 | output.getParentFile().mkdirs(); 35 | try (FileWriter fw = new FileWriter(output)) { 36 | List contents = chapters.get(i).getContents(); 37 | fw.write(contents.getFirst()); 38 | fw.write("\n\n"); 39 | for (int j = 1; j < contents.size(); j++) { 40 | fw.write(" " + contents.get(j) + "\n\n"); 41 | } 42 | } catch (IOException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/App/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.App; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import javafx.scene.paint.Color; 5 | 6 | import java.io.File; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.util.Objects; 11 | 12 | public class ConfigManager { 13 | 14 | private final Config config; 15 | private final Thread thread; 16 | private final File path; 17 | 18 | private String lastString = ""; 19 | 20 | public ConfigManager(Config config, File file, long waitTime) { 21 | this.config = config; 22 | this.path = file; 23 | 24 | try { 25 | load(); 26 | } catch (Exception e) { 27 | save(config.toString()); 28 | throw new RuntimeException(e); 29 | } 30 | 31 | thread = Thread.ofVirtual().name("Config-Listener-Thread").unstarted(() -> { 32 | try { 33 | while (true) { 34 | String thisString = config.toString(); 35 | if (!Objects.equals(thisString, lastString)) { 36 | save(thisString); 37 | lastString = thisString; 38 | } 39 | Thread.sleep(waitTime); 40 | } 41 | } catch (InterruptedException e) { 42 | throw new RuntimeException(e); 43 | } 44 | }); 45 | } 46 | 47 | public void start() { 48 | thread.start(); 49 | } 50 | 51 | private void load() { 52 | if (!path.exists()) { 53 | save(config.toString()); 54 | } 55 | try { 56 | String json = Files.readString(path.toPath()); 57 | JSONObject jsonObject = JSONObject.parseObject(json); 58 | loadItem(config, jsonObject); 59 | } catch (IOException _) {} 60 | } 61 | 62 | private void loadItem(Config config, JSONObject jsonObject) throws IOException { 63 | for (String key : config.keySet()) { 64 | if (!jsonObject.containsKey(key)) throw new IOException("Key \"" + key + "\" not found in config file"); 65 | switch (config.getType(key)) { 66 | case STRING -> config.put(key, jsonObject.getString(key)); 67 | case INTEGER -> config.put(key, jsonObject.getInteger(key)); 68 | case BOOLEAN -> config.put(key, jsonObject.getBoolean(key)); 69 | case DOUBLE -> config.put(key, jsonObject.getDouble(key)); 70 | case COLOR -> config.put(key, Color.web(jsonObject.getString(key))); 71 | case MAP -> config.put(key, Config.parseMap(jsonObject.getString(key))); 72 | case CONFIG -> { 73 | JSONObject subJsonObject = jsonObject.getJSONObject(key); 74 | loadItem(config.getConfig(key), subJsonObject); 75 | } 76 | } 77 | } 78 | } 79 | 80 | private void save(String s) { 81 | try (FileWriter fw = new FileWriter(path)) { 82 | fw.write(s); 83 | } catch (IOException e) { 84 | throw new RuntimeException(e); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/App/Resource.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.App; 2 | 3 | import javafx.scene.image.WritableImage; 4 | import mai_onsyn.AnimeFX2.Utls.Toolkit; 5 | 6 | import java.io.IOException; 7 | 8 | public class Resource { 9 | public static final WritableImage horn; 10 | 11 | 12 | static { 13 | try { 14 | horn = new WritableImage(Toolkit.loadImage("textures/icons/app/horn.png").getPixelReader(), 512, 512); 15 | } catch (IOException e) { 16 | throw new RuntimeException(e); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/App/Runtime.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.App; 2 | 3 | import javafx.beans.property.SimpleBooleanProperty; 4 | import javafx.beans.property.SimpleIntegerProperty; 5 | import mai_onsyn.AnimeFX2.LanguageManager; 6 | import mai_onsyn.AnimeFX2.ThemeManager; 7 | import mai_onsyn.VeloVoice2.NetWork.Item.LocalTXT; 8 | import mai_onsyn.VeloVoice2.NetWork.Item.Source; 9 | 10 | import java.io.File; 11 | import java.util.ArrayList; 12 | import java.util.LinkedHashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public class Runtime { 17 | 18 | public static final SimpleBooleanProperty isRunning = new SimpleBooleanProperty(false); 19 | public static final Map sources = new LinkedHashMap<>(); 20 | static { //在这里添加自定义的加载源 21 | sources.put("LocalTXT", new LocalTXT()); 22 | } 23 | 24 | public static final Config config = new Config(); 25 | public static final Config edgeTTSConfig = new Config(); 26 | public static final Config textConfig = new Config(); 27 | 28 | static { 29 | config.registerInteger("TimeoutMillis", 10000); 30 | config.registerInteger("MaxRetries", 3); 31 | config.registerString("AudioSaveFolder", ""); 32 | config.registerString("PreviewText", "Hello, World!"); 33 | 34 | config.registerConfig("EdgeTTS", edgeTTSConfig); 35 | { 36 | edgeTTSConfig.registerString("SelectedLanguage", "中文"); 37 | edgeTTSConfig.registerString("SelectedModel", "zh-CN-XiaoxiaoNeural"); 38 | edgeTTSConfig.registerDouble("VoiceRate", 1.0); 39 | edgeTTSConfig.registerDouble("VoiceVolume", 1.0); 40 | edgeTTSConfig.registerDouble("VoicePitch", 1.0); 41 | edgeTTSConfig.registerInteger("ThreadCount", 2); 42 | } 43 | 44 | config.registerConfig("Text", textConfig); 45 | { 46 | textConfig.registerInteger("SplitLength", 512); 47 | textConfig.registerString("ForceSplitChars", "\n"); 48 | textConfig.registerString("SplitChars", "。?!…, "); 49 | textConfig.registerString("LoadSource", "LocalTXT"); 50 | textConfig.registerString("LoadUri", ""); 51 | } 52 | 53 | for (Map.Entry entry : sources.entrySet()) { 54 | config.registerConfig(entry.getKey(), entry.getValue().getConfig()); 55 | } 56 | 57 | 58 | ConfigManager cfgManager = new ConfigManager(config, new File(System.getProperty("user.dir") + "\\config.json"), 1000); 59 | cfgManager.start(); 60 | } 61 | 62 | public static final ThemeManager themeManager = new ThemeManager(); 63 | public static final LanguageManager languageManager = new LanguageManager("lang"); 64 | 65 | public static Thread EDGE_TTS_THREAD; 66 | 67 | public static int currentTotalCount = 0; 68 | public static int totalCount = 0; 69 | public static final SimpleIntegerProperty totalFinished = new SimpleIntegerProperty(0); 70 | public static final SimpleIntegerProperty currentFinished = new SimpleIntegerProperty(0); 71 | 72 | 73 | public static final List cycleTasks = new ArrayList<>(); 74 | static { 75 | Thread.ofVirtual().start(() -> { 76 | try { 77 | while (true) { 78 | for (Runnable task : cycleTasks) { 79 | task.run(); 80 | } 81 | Thread.sleep(1000); 82 | } 83 | } catch (InterruptedException _) { 84 | } 85 | }); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Audio/AfterEffects.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.Audio; 2 | 3 | import mai_onsyn.VeloVoice2.Text.Sentence; 4 | 5 | import java.util.List; 6 | 7 | public class AfterEffects { 8 | 9 | public static List process(List sentences) { 10 | 11 | return sentences; 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Audio/AudioPlayer.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.Audio; 2 | 3 | import javazoom.jl.decoder.JavaLayerException; 4 | import javazoom.jl.player.Player; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.IOException; 8 | 9 | import static mai_onsyn.VeloVoice.App.Runtime.logger; 10 | 11 | public class AudioPlayer { 12 | public static void play(byte[] mergedAudio) { 13 | try { 14 | try (ByteArrayInputStream stream = new ByteArrayInputStream(mergedAudio)) { 15 | Player player = new Player(stream); 16 | player.play(); 17 | } 18 | } catch (JavaLayerException | IOException e) { 19 | logger.error("播放声音时出错:" + e); 20 | e.printStackTrace(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Audio/AudioSaver.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.Audio; 2 | 3 | import com.jonathanedgecombe.srt.InvalidTimestampFormatException; 4 | import com.jonathanedgecombe.srt.Subtitle; 5 | import com.jonathanedgecombe.srt.SubtitleFile; 6 | import com.jonathanedgecombe.srt.Timestamp; 7 | import mai_onsyn.VeloVoice2.Text.Sentence; 8 | 9 | import java.io.File; 10 | import java.io.FileNotFoundException; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.util.List; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | import java.util.concurrent.atomic.AtomicLong; 16 | 17 | public class AudioSaver { 18 | 19 | private static final int UNIT_DURATION = 6; // byte length of 1 millisecond 20 | 21 | public static void save(List sentences, File file, String name) { 22 | File mp3File = new File(file, name + ".mp3"); 23 | 24 | try { 25 | if (!mp3File.getParentFile().exists()) mp3File.getParentFile().mkdirs(); 26 | if (!mp3File.exists()) mp3File.createNewFile(); 27 | } catch (IOException e) { 28 | throw new RuntimeException(e); 29 | } 30 | 31 | try (FileOutputStream fos = new FileOutputStream(mp3File)) { 32 | for (Sentence sentence : sentences) { 33 | fos.write(sentence.getAudio()); 34 | } 35 | 36 | } catch (IOException e) { 37 | throw new RuntimeException(e); 38 | } 39 | 40 | //saveSRT(sentences, file, name); 41 | } 42 | 43 | private static void saveSRT(List sentences, File file, String name) { 44 | SubtitleFile srt = new SubtitleFile(); 45 | int tick = 0; 46 | 47 | try { 48 | 49 | for (Sentence sentence : sentences) { 50 | Subtitle subtitle = new Subtitle(new Timestamp(formatDuration(tick / UNIT_DURATION)), new Timestamp(formatDuration((tick+=sentence.countBytes()) / UNIT_DURATION))); 51 | 52 | subtitle.addLine(sentence.getText()); 53 | 54 | srt.addSubtitle(subtitle); 55 | } 56 | 57 | srt.save(new File(file, name + ".srt")); 58 | 59 | } catch (InvalidTimestampFormatException | IOException e) { 60 | throw new RuntimeException(e); 61 | } 62 | } 63 | 64 | private static String formatDuration(long milliseconds) { 65 | 66 | long h = milliseconds / 3600000; 67 | long m = (milliseconds % 3600000) / 60000; 68 | long s = ((milliseconds % 3600000) % 60000) / 1000; 69 | long S = milliseconds % 1000; 70 | 71 | return String.format("%02d:%02d:%02d,%03d", h, m, s, S); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/FrameApp.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import javafx.application.Application; 5 | import javafx.scene.Scene; 6 | import javafx.scene.image.Image; 7 | import javafx.stage.Stage; 8 | import mai_onsyn.AnimeFX2.layout.AutoPane; 9 | import mai_onsyn.VeloVoice2.App.Constants; 10 | import mai_onsyn.VeloVoice2.App.Runtime; 11 | import mai_onsyn.VeloVoice2.FrameFactory.LocalTXTHeaderEditor2; 12 | import mai_onsyn.VeloVoice2.FrameFactory.LogFactory; 13 | import mai_onsyn.VeloVoice2.FrameFactory.MainFactory; 14 | 15 | import static mai_onsyn.VeloVoice2.FrameFactory.LocalTXTHeadersEditor.rulesCfgStage; 16 | import static mai_onsyn.VeloVoice2.FrameFactory.LogFactory.logger; 17 | 18 | public class FrameApp extends Application { 19 | @Override 20 | public void start(Stage stage) throws Exception { 21 | 22 | AutoPane root = new AutoPane(); 23 | root.setOnMousePressed(_ -> root.requestFocus()); 24 | 25 | MainFactory.drawMainFrame(root); 26 | LogFactory.drawLogFrame(); 27 | Runtime.languageManager.switchLanguage("en_us"); 28 | 29 | Scene scene = new Scene(root, 1280, 720); 30 | stage.setMinWidth(960); 31 | stage.setMinHeight(540); 32 | stage.getIcons().add(new Image("textures/icon.png")); 33 | stage.setTitle("VeloVoice"); 34 | stage.setScene(scene); 35 | 36 | logger.info("Application started"); 37 | root.requestFocus(); 38 | 39 | // LocalTXTHeaderEditor2 headerItemsEditor = new LocalTXTHeaderEditor2(JSONArray.parseArray(Runtime.sources.get("LocalTXT").getConfig().getString("HeaderItems"))); 40 | // stage.setScene(new Scene(headerItemsEditor, 600, 450)); 41 | // headerItemsEditor.requestFocus(); 42 | stage.show(); 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/FrameFactory/LogFactory.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.FrameFactory; 2 | 3 | import javafx.scene.Scene; 4 | import javafx.scene.image.Image; 5 | import javafx.stage.Stage; 6 | import mai_onsyn.AnimeFX2.Module.AXLogger; 7 | import mai_onsyn.AnimeFX2.Module.AXProgressBar; 8 | import mai_onsyn.AnimeFX2.layout.AutoPane; 9 | 10 | import static mai_onsyn.VeloVoice2.App.Runtime.*; 11 | 12 | public class LogFactory { 13 | 14 | public static final Stage logStage = new Stage(); 15 | public static final AXLogger logger = new AXLogger(); 16 | 17 | public static final AutoPane root = new AutoPane(); 18 | 19 | public static final AXProgressBar totalProgressBar = new AXProgressBar(); 20 | public static final AXProgressBar currentProgressBar = new AXProgressBar(); 21 | 22 | static { 23 | totalFinished.addListener((o, ov, nv) -> { 24 | totalProgressBar.setProgress(totalFinished.doubleValue() / totalCount); 25 | }); 26 | currentFinished.addListener((o, ov, nv) -> { 27 | currentProgressBar.setProgress(currentFinished.doubleValue() / currentTotalCount); 28 | }); 29 | } 30 | 31 | public static void drawLogFrame() { 32 | Scene scene = new Scene(root, 600, 450); 33 | logStage.setScene(scene); 34 | logStage.setMinWidth(400); 35 | logStage.setMinHeight(300); 36 | logStage.getIcons().add(new Image("textures/icon.png")); 37 | logStage.setTitle("Log"); 38 | 39 | root.getChildren().addAll(logger, totalProgressBar, currentProgressBar); 40 | 41 | root.setPosition(logger, false, 0, 0, 0, 50); 42 | root.setPosition(totalProgressBar, false, 0, 0, 50, 25); 43 | root.setPosition(currentProgressBar, false, 0, 0, 25, 0); 44 | root.flipRelativeMode(totalProgressBar, AutoPane.Motion.TOP); 45 | root.flipRelativeMode(currentProgressBar, AutoPane.Motion.TOP); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Main.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2; 2 | 3 | import javafx.application.Application; 4 | import mai_onsyn.VeloVoice2.NetWork.TTS.FixedEdgeTTSClient; 5 | 6 | import static mai_onsyn.VeloVoice2.App.Runtime.*; 7 | 8 | public class Main { 9 | 10 | public static void main(String[] args) { 11 | init(); 12 | Application.launch(FrameApp.class); 13 | } 14 | 15 | 16 | private static void init() { 17 | FixedEdgeTTSClient.setVoice(edgeTTSConfig.getString("SelectedModel")); 18 | FixedEdgeTTSClient.setVoicePitch(edgeTTSConfig.getDouble("VoicePitch")); 19 | FixedEdgeTTSClient.setVoiceRate(edgeTTSConfig.getDouble("VoiceRate")); 20 | FixedEdgeTTSClient.setVoiceVolume(edgeTTSConfig.getDouble("VoiceVolume")); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/NetWork/Item/Source.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.NetWork.Item; 2 | 3 | import javafx.scene.image.Image; 4 | import mai_onsyn.AnimeFX2.Module.AXButton; 5 | import mai_onsyn.AnimeFX2.Utls.AXTreeItem; 6 | import mai_onsyn.VeloVoice2.App.Config; 7 | 8 | public abstract class Source { 9 | 10 | protected Config config = new Config(); 11 | 12 | public Config getConfig() { 13 | return config; 14 | } 15 | 16 | protected abstract String getHomePage(); 17 | 18 | protected abstract String getDetails(); 19 | 20 | protected abstract String getNameSpace(); 21 | 22 | protected abstract Image getIcon(); 23 | 24 | public abstract void process(String uri, AXTreeItem root); 25 | 26 | public AXButton getItemButton() { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/NetWork/TTS/EdgeTTSVoice.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.NetWork.TTS; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | import java.io.*; 7 | 8 | public class EdgeTTSVoice { 9 | 10 | private static final JSONArray jsonArray; 11 | 12 | static { 13 | StringBuilder sb = new StringBuilder(); 14 | try (InputStream inputStream = EdgeTTSVoice.class.getClassLoader().getResourceAsStream("edgeTTS.json"); 15 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")) 16 | ) { 17 | String line; 18 | while ((line = reader.readLine()) != null) { 19 | sb.append(line).append("\n"); 20 | } 21 | } catch (IOException e) { 22 | throw new RuntimeException(e); 23 | } 24 | jsonArray = JSONArray.parseArray(sb.toString().trim()); 25 | } 26 | 27 | public static JSONObject getVoice(String name) { 28 | for (Object obj : jsonArray) { 29 | if (((JSONObject) obj).get("ShortName").equals(name)) { 30 | return (JSONObject) obj; 31 | } 32 | } 33 | return null; 34 | } 35 | public static JSONArray getVoices() { 36 | return jsonArray; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/NetWork/TTS/TTSClient.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.NetWork.TTS; 2 | 3 | import mai_onsyn.VeloVoice2.Text.Sentence; 4 | 5 | public interface TTSClient { 6 | void connect() throws Exception; 7 | void close(); 8 | Sentence process(String s) throws Exception; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/NetWork/TTSPool.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.NetWork; 2 | 3 | import mai_onsyn.VeloVoice2.Audio.AfterEffects; 4 | import mai_onsyn.VeloVoice2.Audio.AudioSaver; 5 | import mai_onsyn.VeloVoice2.NetWork.TTS.FixedEdgeTTSClient; 6 | import mai_onsyn.VeloVoice2.NetWork.TTS.TTSClient; 7 | import mai_onsyn.VeloVoice2.Text.Sentence; 8 | 9 | import java.io.File; 10 | import java.util.*; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | import static mai_onsyn.VeloVoice2.App.Runtime.*; 16 | import static mai_onsyn.VeloVoice2.FrameFactory.LogFactory.logger; 17 | 18 | //多线程TTS处理 19 | public class TTSPool { 20 | public enum ClientType { 21 | EDGE 22 | } 23 | 24 | private final int size; 25 | private final ClientFactory clientFactory; 26 | private final TTSClient[] clients; 27 | 28 | public TTSPool(ClientType type, int size) { 29 | clientFactory = new ClientFactory(type); 30 | this.size = size; 31 | clients = new TTSClient[size]; 32 | for (int i = 0; i < size; i++) { 33 | clients[i] = clientFactory.createClient(); 34 | } 35 | } 36 | 37 | public void connect() throws InterruptedException { 38 | logger.info("Connecting to TTS server by %d threads...", size); 39 | CountDownLatch countDownLatch = new CountDownLatch(size); 40 | for (TTSClient client : clients) { 41 | Thread.ofVirtual().start(() -> { 42 | try { 43 | client.connect(); 44 | } catch (Exception e) { 45 | throw new RuntimeException(e); 46 | } 47 | countDownLatch.countDown(); 48 | }); 49 | } 50 | try { 51 | countDownLatch.await(config.getInteger("TimeoutMillis"), TimeUnit.MILLISECONDS); 52 | logger.info("%d threads connected to TTS server.", size); 53 | } catch (Exception e) { 54 | logger.warn("Failed to connect because: %s", e.getMessage()); 55 | throw new InterruptedException(); 56 | } 57 | } 58 | 59 | public void close() { 60 | for (TTSClient client : clients) { 61 | client.close(); 62 | } 63 | logger.info("All threads have been shut down"); 64 | } 65 | 66 | public void execute(List input, File outputFolder, String fileName) throws InterruptedException { 67 | Map resultPool = new HashMap<>(); 68 | 69 | AtomicInteger processIndex = new AtomicInteger(); 70 | CountDownLatch countDownLatch = new CountDownLatch(input.size()); 71 | 72 | for (int i = 0; i < clients.length; i++) { 73 | TTSClient client = clients[i]; 74 | Thread.ofVirtual().name("TaskThread-" + i).start(() -> { 75 | while (processIndex.get() < input.size()) { 76 | String text; 77 | int thisIndex; 78 | synchronized (this) { 79 | thisIndex = processIndex.getAndIncrement(); 80 | if (thisIndex < input.size()) text = input.get(thisIndex); 81 | else break; 82 | } 83 | 84 | try { 85 | Sentence sentence = client.process(text); 86 | resultPool.put(thisIndex, sentence); 87 | currentFinished.set(currentFinished.get() + 1); 88 | totalFinished.set(totalFinished.get() + 1); 89 | countDownLatch.countDown(); 90 | } catch (Exception e) { 91 | throw new RuntimeException(e); 92 | } 93 | } 94 | }); 95 | } 96 | 97 | countDownLatch.await(); 98 | 99 | List result = new ArrayList<>(resultPool.size()); 100 | for (Map.Entry entry : resultPool.entrySet()) { 101 | result.add(entry.getKey(), entry.getValue()); 102 | } 103 | 104 | //after处理,保存 105 | AudioSaver.save(AfterEffects.process(result), outputFolder, fileName); 106 | } 107 | 108 | private record ClientFactory(ClientType type) { 109 | 110 | public TTSClient createClient() { 111 | return switch (type) { 112 | case EDGE -> new FixedEdgeTTSClient(); 113 | }; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Text/Sentence.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.Text; 2 | 3 | import java.util.List; 4 | 5 | public record Sentence(String text, List audio) { 6 | public byte[] getAudio() { 7 | int length = 0; 8 | for (byte[] audio : audio) { 9 | length += audio.length; 10 | } 11 | byte[] result = new byte[length]; 12 | int offset = 0; 13 | for (byte[] audio : audio) { 14 | System.arraycopy(audio, 0, result, offset, audio.length); 15 | offset += audio.length; 16 | } 17 | return result; 18 | } 19 | 20 | public String getText() { 21 | return text; 22 | } 23 | 24 | public int countBytes() { 25 | int count = 0; 26 | for (byte[] audio : audio) { 27 | count += audio.length; 28 | } 29 | return count; 30 | } 31 | 32 | public List getAudioList() { 33 | return audio; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Text/TTS.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.Text; 2 | 3 | import javafx.beans.property.SimpleStringProperty; 4 | import mai_onsyn.AnimeFX2.Utls.AXDataTreeItem; 5 | import mai_onsyn.AnimeFX2.Utls.AXTreeItem; 6 | import mai_onsyn.VeloVoice2.NetWork.TTSPool; 7 | 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static mai_onsyn.VeloVoice2.App.Runtime.*; 13 | import static mai_onsyn.VeloVoice2.FrameFactory.LogFactory.logger; 14 | import static mai_onsyn.VeloVoice2.FrameFactory.LogFactory.totalProgressBar; 15 | 16 | public class TTS { 17 | 18 | public static TTSPool pool; 19 | 20 | private static final List executeItems = new ArrayList<>(); 21 | 22 | public static void start(AXTreeItem root, File outputFolder) throws InterruptedException { 23 | 24 | pool = new TTSPool(TTSPool.ClientType.EDGE, edgeTTSConfig.getInteger("ThreadCount")); 25 | pool.connect(); 26 | 27 | executeItems.clear(); 28 | 29 | initItem(root, outputFolder, null); 30 | totalFinished.set(0); 31 | totalCount = countPieces(); 32 | startItem(); 33 | 34 | pool.close(); 35 | } 36 | 37 | private static void initItem(AXTreeItem item, File folder, String name) { 38 | if (item instanceof AXDataTreeItem d) { 39 | if (d.getData() instanceof SimpleStringProperty s) { 40 | String content = s.get(); 41 | 42 | executeItems.add(new ExecuteItem(TextUtil.splitText(content), folder, String.format("%s", name))); 43 | } 44 | } 45 | else { 46 | for (AXTreeItem i : item.getChildrenAsItem()) { 47 | if (i instanceof AXDataTreeItem) initItem(i, folder, i.getHeadName()); 48 | else initItem(i, new File(folder, i.getHeadName()), null); 49 | } 50 | } 51 | } 52 | 53 | private static void startItem() throws InterruptedException { 54 | for (int i = 0; i < executeItems.size(); i++) { 55 | ExecuteItem e = executeItems.get(i); 56 | currentFinished.set(0); 57 | currentTotalCount = e.texts.size(); 58 | 59 | logger.debug("Start file: %s", e.name); 60 | pool.execute(e.texts, e.folder, e.name); 61 | logger.debug("Complected file: %s", e.name); 62 | } 63 | } 64 | 65 | private static int countPieces() { 66 | int count = 0; 67 | for (ExecuteItem e : executeItems) { 68 | count += e.texts.size(); 69 | } 70 | return count; 71 | } 72 | 73 | private record ExecuteItem(List texts, File folder, String name) {} 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/mai_onsyn/VeloVoice2/Text/TextUtil.java: -------------------------------------------------------------------------------- 1 | package mai_onsyn.VeloVoice2.Text; 2 | 3 | import org.jsoup.parser.Parser; 4 | import org.mozilla.universalchardet.UniversalDetector; 5 | 6 | import java.io.File; 7 | import java.nio.charset.Charset; 8 | import java.nio.file.Files; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static mai_onsyn.VeloVoice2.App.Runtime.textConfig; 13 | 14 | public class TextUtil { 15 | 16 | public static List splitText(String text) { 17 | 18 | List lines = new ArrayList<>(); 19 | char[] forceSplitChars = textConfig.getString("ForceSplitChars").toCharArray(); 20 | int lastIndex = 0; 21 | for (int i = 0; i < text.toCharArray().length; i++) { 22 | for (char forceSplitChar : forceSplitChars) { 23 | if (text.charAt(i) == forceSplitChar) { 24 | String line = text.substring(lastIndex, i + 1).trim(); 25 | if (!line.isEmpty()) { 26 | lines.add(line); 27 | } 28 | lastIndex = i + 1; 29 | } 30 | } 31 | } 32 | lines.add(text.substring(lastIndex).trim()); 33 | 34 | List splitText = new ArrayList<>(); 35 | 36 | for (String line : lines) { 37 | collect(splitText, line); 38 | } 39 | 40 | return splitText; 41 | } 42 | 43 | public static String load(File file) { 44 | try { 45 | byte[] fileContent = Files.readAllBytes(file.toPath()); 46 | 47 | UniversalDetector detector = new UniversalDetector(null); 48 | detector.handleData(fileContent, 0, fileContent.length); 49 | detector.dataEnd(); 50 | 51 | return decodeHtmlEntities(new String(fileContent, Charset.forName(detector.getDetectedCharset()))); 52 | } catch (Exception e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | 58 | //部分小说txt里有html转义符 需要解析 59 | private static String decodeHtmlEntities(String s) { 60 | return Parser.unescapeEntities(s, false); 61 | } 62 | 63 | private static void collect(List parent, String s) { 64 | if (s.isEmpty()) return; 65 | if (s.length() < textConfig.getInteger("SplitLength")) { 66 | parent.add(s); 67 | return; 68 | } 69 | 70 | String[] ab = dichotomy(s); 71 | collect(parent, ab[0]); 72 | collect(parent, ab[1]); 73 | } 74 | 75 | private static String[] dichotomy(String s) { 76 | int halfLength = s.length() / 2; 77 | 78 | for (char c : textConfig.getString("SplitChars").toCharArray()) { 79 | for (int i = 0; i < halfLength - 2; i++) { 80 | if (s.charAt(halfLength + i) == c) 81 | return new String[] { 82 | s.substring(0, halfLength + 1 + i), 83 | s.substring(halfLength + 1 + i) 84 | }; 85 | if (s.charAt(halfLength - i) == c) 86 | return new String[] { 87 | s.substring(0, halfLength + 1 - i), 88 | s.substring(halfLength + 1 - i) 89 | }; 90 | } 91 | } 92 | return new String[] { 93 | s.substring(0, halfLength + 1), 94 | s.substring(halfLength + 1) 95 | }; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/resources/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "frame.contextMenu.newFile": "New File", 3 | "frame.contextMenu.newFolder": "New Folder", 4 | "frame.contextMenu.paste": "Paste", 5 | "frame.contextMenu.rename": "Rename", 6 | "frame.contextMenu.expandAll": "Expand All", 7 | "frame.contextMenu.clear": "Clear", 8 | "frame.contextMenu.copy": "Copy", 9 | "frame.contextMenu.cut": "Cut", 10 | "frame.contextMenu.pasteAppend": "Paste(Append)", 11 | "frame.contextMenu.moveUp": "Move Up", 12 | "frame.contextMenu.moveDown": "Move Down", 13 | "frame.contextMenu.delete": "Delete", 14 | "frame.contextMenu.undo": "Undo", 15 | "frame.contextMenu.selectAll": "Select All", 16 | "frame.main.item.label.edgeTTS.lang": "Language", 17 | "frame.main.item.label.edgeTTS.model": "Model", 18 | "frame.main.item.label.edgeTTS.rate": "Voice Rate", 19 | "frame.main.item.label.edgeTTS.volume": "Voice Volume", 20 | "frame.main.item.label.edgeTTS.pitch": "Voice Pith", 21 | "frame.main.item.label.edgeTTS.threadCount": "TTS Thread Count", 22 | "frame.main.textField.audioFolder": "path to save mp3 file", 23 | "frame.main.item.label.audioFolder": "Audio Save Folder", 24 | 25 | "frame.main.item.label.textProcess.load.title": "Load", 26 | "frame.main.item.label.load.loadSource": "Load Source", 27 | "frame.main.textField.textLoadUri": "uri", 28 | "frame.main.item.label.load.textLoadUri": "Load URI" 29 | } -------------------------------------------------------------------------------- /src/main/resources/lang/lang_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "en_us": { 3 | "name": "English (US)", 4 | "local": "English" 5 | }, 6 | "zh_cn": { 7 | "name": "Chinese (Mainland)", 8 | "local": "简体中文" 9 | }, 10 | "zh_tw": { 11 | "name": "Chinese (Taiwan)", 12 | "local": "繁體中文" 13 | }, 14 | "ja_jp": { 15 | "name": "Japanese", 16 | "local": "日本語" 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/resources/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "A": "你好" 3 | } -------------------------------------------------------------------------------- /src/main/resources/style.css: -------------------------------------------------------------------------------- 1 | .scroll-pane { 2 | -fx-background-color: transparent; 3 | } 4 | 5 | .scroll-pane .viewport { 6 | -fx-background-color: transparent; 7 | } 8 | 9 | .scroll-pane .corner { 10 | -fx-background-color: transparent; 11 | } 12 | 13 | .scroll-bar { 14 | -fx-background-color: transparent; 15 | -fx-background: transparent; 16 | -fx-pref-width: 8px; 17 | } 18 | 19 | .scroll-bar .track { 20 | -fx-background-color: transparent; 21 | -fx-border-color: transparent; 22 | } 23 | 24 | .scroll-bar .increment-button, 25 | .scroll-bar .decrement-button { 26 | -fx-background-color: transparent; 27 | -fx-border-color: transparent; 28 | -fx-padding: 0; 29 | } 30 | 31 | .scroll-bar .thumb { 32 | -fx-background-color: #88888880; 33 | -fx-background-radius: 5px; 34 | } 35 | 36 | 37 | 38 | 39 | .text-area { 40 | -fx-background-color: transparent; /* 背景透明 */ 41 | -fx-control-inner-background: transparent; /* 内部背景透明 */ 42 | -fx-background-insets: 0; /* 移除背景填充 */ 43 | -fx-background-radius: 0; /* 移除背景圆角 */ 44 | -fx-padding: 0; /* 移除内边距 */ 45 | -fx-border-color: transparent; /* 边框透明 */ 46 | } 47 | 48 | .text-area:focused { 49 | -fx-background-color: transparent; /* 聚焦时背景透明 */ 50 | -fx-control-inner-background: transparent; /* 聚焦时内部背景透明 */ 51 | -fx-border-color: transparent; /* 聚焦时边框透明 */ 52 | } 53 | 54 | .text-area .scroll-pane { 55 | -fx-background-color: transparent; 56 | } 57 | 58 | .text-area .scroll-pane .viewport{ 59 | -fx-background-color: transparent; 60 | } 61 | 62 | 63 | .text-area .scroll-pane .content{ 64 | -fx-background-color: transparent; 65 | } 66 | 67 | .text-area .scroll-bar { 68 | -fx-background-color: transparent; 69 | -fx-background: transparent; 70 | -fx-pref-width: 8px; 71 | } 72 | 73 | .text-area .scroll-bar:horizontal .increment-button, 74 | .text-area .scroll-bar:horizontal .decrement-button, 75 | .text-area .scroll-bar:vertical .increment-button, 76 | .text-area .scroll-bar:vertical .decrement-button { 77 | -fx-background-color: transparent; 78 | -fx-background-radius: 0; 79 | -fx-padding: 0; 80 | } 81 | 82 | /* 设置滚动条滑块样式 */ 83 | .text-area .scroll-bar .thumb { 84 | -fx-background-color: #88888880; 85 | -fx-background-radius: 5px; 86 | } 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | .text-field { 96 | -fx-background-color: transparent; 97 | /*-fx-control-inner-background: transparent;*/ 98 | -fx-background-insets: 0; 99 | -fx-background-radius: 0; 100 | -fx-padding: 0; 101 | -fx-border-color: transparent; 102 | } -------------------------------------------------------------------------------- /src/main/resources/styles/scroll-pane.css: -------------------------------------------------------------------------------- 1 | .scroll-pane { 2 | -fx-background-color: transparent; 3 | } 4 | 5 | .scroll-pane .viewport { 6 | -fx-background-color: transparent; 7 | } 8 | 9 | .scroll-pane .corner { 10 | -fx-background-color: transparent; 11 | } 12 | 13 | .scroll-bar { 14 | -fx-background-color: transparent; 15 | -fx-background: transparent; 16 | -fx-pref-width: 8px; 17 | } 18 | 19 | .scroll-bar .track { 20 | -fx-background-color: transparent; 21 | -fx-border-color: transparent; 22 | } 23 | 24 | .scroll-bar .increment-button, 25 | .scroll-bar .decrement-button { 26 | -fx-background-color: transparent; 27 | -fx-border-color: transparent; 28 | -fx-padding: 0; 29 | } 30 | 31 | .scroll-bar .thumb { 32 | -fx-background-color: #88888880; 33 | -fx-background-radius: 5px; 34 | } -------------------------------------------------------------------------------- /src/main/resources/styles/slider.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | -fx-background-color: transparent; 3 | -fx-padding: 10px; 4 | } 5 | 6 | .slider .thumb { 7 | -fx-background-color: #00000000; 8 | } 9 | 10 | .slider .increment-button, 11 | .slider .decrement-button { 12 | -fx-background-color: #333333; 13 | -fx-padding: 5px; 14 | } 15 | 16 | .slider .tick-mark { 17 | -fx-stroke: #000000; 18 | -fx-stroke-width: 1px; 19 | } 20 | 21 | .slider .tick-label { 22 | -fx-text-fill: #000000; 23 | -fx-font-size: 12px; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/styles/text-area.css: -------------------------------------------------------------------------------- 1 | .text-area { 2 | -fx-background-color: transparent; /* 背景透明 */ 3 | -fx-control-inner-background: transparent; /* 内部背景透明 */ 4 | -fx-background-insets: 0; /* 移除背景填充 */ 5 | -fx-background-radius: 0; /* 移除背景圆角 */ 6 | -fx-padding: 0; /* 移除内边距 */ 7 | -fx-border-color: transparent; /* 边框透明 */ 8 | } 9 | 10 | .text-area:focused { 11 | -fx-background-color: transparent; /* 聚焦时背景透明 */ 12 | -fx-control-inner-background: transparent; /* 聚焦时内部背景透明 */ 13 | -fx-border-color: transparent; /* 聚焦时边框透明 */ 14 | } 15 | 16 | .text-area .scroll-pane { 17 | -fx-background-color: transparent; 18 | } 19 | 20 | .text-area .scroll-pane .viewport{ 21 | -fx-background-color: transparent; 22 | } 23 | 24 | 25 | .text-area .scroll-pane .content{ 26 | -fx-background-color: transparent; 27 | } 28 | 29 | .text-area .scroll-bar { 30 | -fx-background-color: transparent; 31 | -fx-background: transparent; 32 | -fx-pref-width: 8px; 33 | } 34 | 35 | .text-area .scroll-bar:horizontal .increment-button, 36 | .text-area .scroll-bar:horizontal .decrement-button, 37 | .text-area .scroll-bar:vertical .increment-button, 38 | .text-area .scroll-bar:vertical .decrement-button { 39 | -fx-background-color: transparent; 40 | -fx-background-radius: 0; 41 | -fx-padding: 0; 42 | } 43 | 44 | /* 设置滚动条滑块样式 */ 45 | .text-area .scroll-bar .thumb { 46 | -fx-background-color: #88888880; 47 | -fx-background-radius: 5px; 48 | } -------------------------------------------------------------------------------- /src/main/resources/styles/text-field.css: -------------------------------------------------------------------------------- 1 | .text-field { 2 | -fx-background-color: transparent; 3 | /*-fx-control-inner-background: transparent;*/ 4 | -fx-background-insets: 0; 5 | -fx-background-radius: 0; 6 | -fx-padding: 0; 7 | -fx-border-color: transparent; 8 | } -------------------------------------------------------------------------------- /src/main/resources/textures/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/bg.png -------------------------------------------------------------------------------- /src/main/resources/textures/close_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/close_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/close_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/close_light.png -------------------------------------------------------------------------------- /src/main/resources/textures/file_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/file_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/file_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/file_light.png -------------------------------------------------------------------------------- /src/main/resources/textures/folder_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/folder_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/folder_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/folder_light.png -------------------------------------------------------------------------------- /src/main/resources/textures/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icon.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/app/horn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/app/horn.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/config.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/clear.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/copy.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/cut.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/delete.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/down.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/expand.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/file_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/file_new.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/folder_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/folder_new.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/paste.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/paste_append.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/paste_append.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/rename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/rename.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/select_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/select_all.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/undo.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/context_menu/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/context_menu/up.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/file.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/folder.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/fork.png -------------------------------------------------------------------------------- /src/main/resources/textures/icons/triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/icons/triangle.png -------------------------------------------------------------------------------- /src/main/resources/textures/maximize_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/maximize_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/maximize_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/maximize_light.png -------------------------------------------------------------------------------- /src/main/resources/textures/minimize_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/minimize_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/minimize_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/minimize_light.png -------------------------------------------------------------------------------- /src/main/resources/textures/root_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/root_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/root_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/root_light.png -------------------------------------------------------------------------------- /src/main/resources/textures/setting_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/setting_dark.png -------------------------------------------------------------------------------- /src/main/resources/textures/setting_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mai-Onsyn/VeloVoice/43c4c3327088ed304358a1b3580c02c614461ef4/src/main/resources/textures/setting_light.png -------------------------------------------------------------------------------- /src/test/java/FrameLauncher.java: -------------------------------------------------------------------------------- 1 | public class FrameLauncher { 2 | 3 | public static void main(String[] args) { 4 | FrameTest.main(args); 5 | //Test03.main(args); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/Frames/TestButtonStyle.java: -------------------------------------------------------------------------------- 1 | package Frames; 2 | 3 | import javafx.scene.paint.Color; 4 | import javafx.scene.text.Font; 5 | import mai_onsyn.AnimeFX2.Styles.AXButtonStyle; 6 | import mai_onsyn.AnimeFX2.Utls.Toolkit; 7 | 8 | public class TestButtonStyle implements AXButtonStyle { 9 | @Override 10 | public Color getBGColor() { 11 | return Color.TRANSPARENT; 12 | } 13 | 14 | @Override 15 | public Color getHoverShadow() { 16 | return Toolkit.adjustOpacity(Color.CORNFLOWERBLUE, 0.2); 17 | } 18 | 19 | @Override 20 | public Color getPressedShadow() { 21 | return Toolkit.adjustOpacity(Color.CORNFLOWERBLUE, 0.5); 22 | } 23 | 24 | @Override 25 | public Color getTextColor() { 26 | return Color.BLACK; 27 | } 28 | 29 | @Override 30 | public Color getTextHoverColor() { 31 | return Color.GRAY; 32 | } 33 | 34 | @Override 35 | public Color getBorderColor() { 36 | return Color.GREEN; 37 | } 38 | 39 | @Override 40 | public Color getFillColor() { 41 | return Color.LIGHTGREEN; 42 | } 43 | 44 | @Override 45 | public Font getTextFont() { 46 | return new Font(16); 47 | } 48 | 49 | @Override 50 | public double getAnimeRate() { 51 | return 1; 52 | } 53 | 54 | @Override 55 | public double getBorderRadius() { 56 | return 3; 57 | } 58 | 59 | @Override 60 | public double getBorderArcSize() { 61 | return 40; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/Test01.java: -------------------------------------------------------------------------------- 1 | import com.alibaba.fastjson.JSONObject; 2 | import javafx.util.Pair; 3 | import mai_onsyn.VeloVoice2.NetWork.Item.LocalTXT; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | public class Test01 { 9 | 10 | public static void main(String[] args) { 11 | 12 | JSONObject o1 = JSONObject.parseObject("{\"a\": \"\\n\"}"); 13 | System.out.println(Arrays.toString(o1.getString("a").toCharArray())); 14 | 15 | String jsonString = JSONObject.toJSONString(o1, true); 16 | 17 | System.out.println(jsonString); 18 | 19 | JSONObject o2 = JSONObject.parseObject(jsonString); 20 | System.out.println(Arrays.toString(o2.getString("a").toCharArray())); 21 | } 22 | } -------------------------------------------------------------------------------- /src/test/java/Test02.java: -------------------------------------------------------------------------------- 1 | import com.jonathanedgecombe.srt.InvalidTimestampFormatException; 2 | import com.jonathanedgecombe.srt.Subtitle; 3 | import com.jonathanedgecombe.srt.SubtitleFile; 4 | import com.jonathanedgecombe.srt.Timestamp; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | public class Test02 { 10 | 11 | public static void main(String[] args) throws InvalidTimestampFormatException, IOException { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/Test03.java: -------------------------------------------------------------------------------- 1 | import mai_onsyn.VeloVoice2.App.Runtime; 2 | import mai_onsyn.VeloVoice2.NetWork.TTS.FixedEdgeTTSClient; 3 | import mai_onsyn.VeloVoice2.NetWork.TTS.FixedEdgeTTSClient; 4 | import mai_onsyn.VeloVoice2.NetWork.TTSPool; 5 | import mai_onsyn.VeloVoice2.Text.Sentence; 6 | 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.util.List; 10 | 11 | public class Test03 { 12 | 13 | 14 | public static void main(String[] args) throws Exception { 15 | 16 | FixedEdgeTTSClient.setVoice("zh-CN-XiaoxiaoNeural"); 17 | 18 | TTSPool client = new TTSPool(TTSPool.ClientType.EDGE, 2); 19 | client.connect(); 20 | 21 | client.execute(List.of("a", "b", "c"), new File("D:/Users/Desktop"), "example"); 22 | 23 | client.close(); 24 | // Sentence sentence = client.process("你好,我是小智,很高兴为你服务。"); 25 | // 26 | // 27 | // client.close(); 28 | // 29 | // FileOutputStream fos = new FileOutputStream("D:/Users/Desktop/a.mp3"); 30 | // fos.write(sentence.getAudio()); 31 | // fos.close(); 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /src/test/java/Test04.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.net.HttpURLConnection; 4 | import java.net.URL; 5 | import java.net.URLEncoder; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | public class Test04 { 9 | private static final String API_URL = "http://127.0.0.1:9966/tts"; 10 | 11 | public static void main(String[] args) { 12 | try { 13 | // 构造查询参数 14 | String query = String.format( 15 | "text=%s&prompt=%s&voice=%s&temperature=%.1f&top_p=%.1f&top_k=%d&refine_max_new_token=%s&infer_max_new_token=%s&skip_refine=%d&is_split=%d&custom_voice=%d", 16 | URLEncoder.encode("检查音色是否正确,测试", StandardCharsets.UTF_8), // 文本 17 | URLEncoder.encode("", StandardCharsets.UTF_8), // prompt 18 | URLEncoder.encode("1111", StandardCharsets.UTF_8), // voice 19 | 0.3, 0.7, 20, "384", "2048", 0, 1, 0 20 | ); 21 | 22 | // 完整 URL 23 | String fullUrl = API_URL + "?" + query; 24 | System.out.println("Request URL: " + fullUrl); 25 | 26 | // 调用 API 27 | String response = callChatTTSAPI(fullUrl); 28 | System.out.println("Response: " + response); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | 34 | public static String callChatTTSAPI(String fullUrl) throws Exception { 35 | URL url = new URL(fullUrl); 36 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 37 | 38 | // 设置请求方法 39 | connection.setRequestMethod("GET"); 40 | 41 | // 设置请求头 42 | connection.setRequestProperty("Accept", "application/json"); 43 | 44 | // 读取响应 45 | int responseCode = connection.getResponseCode(); 46 | System.out.println("Response Code: " + responseCode); 47 | 48 | if (responseCode == HttpURLConnection.HTTP_OK) { 49 | StringBuilder response = new StringBuilder(); 50 | try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) { 51 | String responseLine; 52 | while ((responseLine = br.readLine()) != null) { 53 | response.append(responseLine.trim()); 54 | } 55 | } 56 | return response.toString(); 57 | } else { 58 | throw new Exception("Error: HTTP " + responseCode); 59 | } 60 | } 61 | } 62 | --------------------------------------------------------------------------------