├── .gitignore ├── .vscode └── extensions.json ├── LICENSE ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public ├── tauri.svg └── vite.svg ├── src-tauri ├── 2 ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── capabilities │ └── default.json ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── src │ ├── lib.rs │ ├── main.rs │ └── utils │ │ ├── api.rs │ │ ├── custom_result.rs │ │ ├── mod.rs │ │ ├── sub_marker.rs │ │ └── tts.rs ├── tauri.conf.json └── test │ ├── output.srt │ ├── sub_title_test.rs │ ├── 复杂文本.json │ ├── 复杂文本.mp3 │ ├── 复杂文本.srt │ ├── 复杂文本.txt │ ├── 复杂文本2.json │ ├── 复杂文本2.mp3 │ ├── 复杂文本2.srt │ ├── 复杂文本2.txt │ ├── 复杂文本3.json │ ├── 复杂文本3.mp3 │ ├── 复杂文本3.txt │ ├── 英文文本.json │ ├── 英文文本.mp3 │ ├── 英文文本.srt │ └── 英文文本.txt ├── src ├── App.vue ├── assets │ ├── base.css │ ├── gitee.png │ ├── github.svg │ └── vue.svg ├── auto-imports.d.ts ├── components.d.ts ├── main.js ├── pages │ ├── SetPage.vue │ └── TTS.vue ├── router │ └── index.js ├── store │ ├── option.js │ └── voices.js └── utils │ ├── function.js │ ├── mitt.js │ └── sqlite.js └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | build 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "tauri-apps.tauri-vscode", 5 | "rust-lang.rust-analyzer" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Manson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tauri + Vue 3 2 | 3 | 使用Tauri开发的微软配音工具 4 | 5 | ## 注意事项 6 | 7 | - 第一次使用Tauri发布公开软件,据说Tauri的兼容性不好,有问题请提交。 8 | - 音频使用base64传输,文本过多时会有性能问题 9 | 10 | ## 已完成功能 11 | 12 | - [x] 基本配音功能 13 | - [x] 字幕生成功能 14 | - [x] 配音界面的设置保存 15 | - [x] 设置页面 16 | 17 | ## 后续计划 18 | 19 | - [ ] 多角色配音 20 | - [ ] 长文本切片(可提高性能) 21 | - [ ] 试听朗读(可用于小说阅读) 22 | - [ ] 自定义停顿间隔(暂时无法适应字幕) 23 | - [ ] 多端适配(Mac,Linux,安卓) 24 | - [ ] 解决base64引发的长音频性能问题 25 | 26 | ## 开发 27 | 28 | ### 克隆代码 29 | 30 | ``` 31 | git clone https://gitee.com/lieranhuasha/tts-tauri.git 32 | 或 33 | git clone https://github.com/zs1083339604/tts-tauri.git 34 | ``` 35 | 36 | ### 安装依赖 37 | 38 | ``` 39 | cd tts-tauri 40 | npm install 41 | ``` 42 | 43 | ### 运行 44 | 45 | ``` 46 | npm run tauri dev 47 | ``` 48 | 49 | ### 注意事项 50 | 51 | 使用Tauri开发,需要完成前置条件:[Tauri前置条件](https://tauri.app/zh-cn/start/prerequisites/) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tauri + Vue 3 App 8 | 18 | 19 | 20 | 21 |
22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tts-tauri", 3 | "private": true, 4 | "version": "0.2.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "tauri": "tauri" 11 | }, 12 | "dependencies": { 13 | "@element-plus/icons-vue": "^2.3.1", 14 | "@tauri-apps/api": "^2", 15 | "@tauri-apps/plugin-dialog": "^2.2.1", 16 | "@tauri-apps/plugin-opener": "^2", 17 | "@tauri-apps/plugin-shell": "^2.2.1", 18 | "@tauri-apps/plugin-sql": "^2.2.0", 19 | "element-plus": "^2.9.8", 20 | "mitt": "^3.0.1", 21 | "pinia": "^3.0.2", 22 | "unplugin-auto-import": "^19.1.2", 23 | "unplugin-icons": "^22.1.0", 24 | "unplugin-vue-components": "^28.5.0", 25 | "vite-plugin-inspect": "^11.0.1", 26 | "vue": "^3.5.13", 27 | "vue-router": "^4.5.1" 28 | }, 29 | "devDependencies": { 30 | "@tauri-apps/cli": "^2", 31 | "@vitejs/plugin-vue": "^5.2.1", 32 | "vite": "^6.0.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/tauri.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /src-tauri/2: -------------------------------------------------------------------------------- 1 | 2 | added 1 package in 4s 3 | 4 | 45 packages are looking for funding 5 | run `npm fund` for details 6 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tts-tauri" 3 | version = "0.1.0" 4 | description = "A Tauri App" 5 | authors = ["you"] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [lib] 11 | # The `_lib` suffix may seem redundant but it is necessary 12 | # to make the lib name unique and wouldn't conflict with the bin name. 13 | # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 14 | name = "tts_tauri_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2", features = [] } 19 | 20 | [dependencies] 21 | tauri = { version = "2", features = [] } 22 | tauri-plugin-opener = "2" 23 | serde = { version = "1", features = ["derive"] } 24 | serde_json = "1" 25 | chrono = "0.4.40" 26 | sha2 = "0.10.8" 27 | uuid = {version = "1.16.0", features = ["v4"] } 28 | url = "2.5.4" 29 | regex = "1.11.1" 30 | hex = "0.4.3" 31 | tokio = {version = "1.44.2", features = ["full"] } 32 | tokio-tungstenite = {version = "0.26.2", features = ["native-tls"] } 33 | futures-util = "0.3.31" 34 | reqwest = "0.12.15" 35 | base64 = "0.22.1" 36 | tauri-plugin-dialog = "2" 37 | tauri-plugin-shell = "2" 38 | 39 | [dependencies.tauri-plugin-sql] 40 | features = ["sqlite"] # or "postgres", or "mysql" 41 | version = "2.0.0" 42 | 43 | [profile.dev] 44 | incremental = true # 以较小的步骤编译您的二进制文件。 45 | 46 | [profile.release] 47 | codegen-units = 1 # 允许 LLVM 执行更好的优化。 48 | lto = true # 启用链接时优化。 49 | opt-level = "s" # 优先考虑小的二进制文件大小。如果您更喜欢速度,请使用 `3`。 50 | panic = "abort" # 通过禁用 panic 处理程序来提高性能。 51 | strip = true # 确保移除调试符号。 52 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": [ 6 | "main" 7 | ], 8 | "permissions": [ 9 | "core:default", 10 | "opener:default", 11 | "sql:default", 12 | "sql:allow-execute", 13 | "core:window:allow-close", 14 | "dialog:default", 15 | "shell:default" 16 | ] 17 | } -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ 2 | use utils::api::{get_exe_path, get_voices_list, start_tts, get_app_version}; 3 | pub mod utils; 4 | 5 | #[tauri::command] 6 | fn greet(name: &str) -> String { 7 | format!("Hello, {}! You've been greeted from Rust!", name) 8 | } 9 | 10 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 11 | pub fn run() { 12 | tauri::Builder::default() 13 | .plugin(tauri_plugin_shell::init()) 14 | .plugin(tauri_plugin_dialog::init()) 15 | .plugin(tauri_plugin_opener::init()) 16 | .plugin(tauri_plugin_sql::Builder::default().build()) 17 | .invoke_handler(tauri::generate_handler![ 18 | greet, 19 | get_voices_list, 20 | start_tts, 21 | get_exe_path, 22 | get_app_version 23 | ]) 24 | .run(tauri::generate_context!()) 25 | .expect("error while running tauri application"); 26 | } 27 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | tts_tauri_lib::run() 6 | } 7 | -------------------------------------------------------------------------------- /src-tauri/src/utils/api.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::custom_result::CustomResult; 2 | use crate::utils::sub_marker::generate_srt; 3 | use crate::utils::tts::TTS; 4 | use base64::{engine::general_purpose, Engine as _}; 5 | use futures_util::{sink::SinkExt, StreamExt}; 6 | use reqwest::get; 7 | use serde::Deserialize; 8 | use serde_json::Value; 9 | use serde_json::{from_str, json}; 10 | use std::fs::{self, File}; 11 | use std::io::Write; 12 | use std::path::Path; 13 | use std::process::Command; 14 | use tokio_tungstenite::{ 15 | connect_async, 16 | tungstenite::{client::IntoClientRequest, Message}, 17 | }; 18 | use uuid::Uuid; 19 | 20 | #[derive(Deserialize)] 21 | pub struct TTSData { 22 | voice: String, 23 | text: String, 24 | pitch: i32, 25 | rate: i32, 26 | volume: i32, 27 | gender_sub_marker: bool, 28 | sub_marker_type: String, 29 | merge_by_number_number: i32, 30 | root_path: String, 31 | open_folders: bool, 32 | } 33 | 34 | #[tauri::command] 35 | pub async fn get_voices_list() -> Result { 36 | let url = "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4"; 37 | 38 | let response = get(url) 39 | .await 40 | .map_err(|e| CustomResult::error(Some(e.to_string()), None))?; 41 | 42 | if response.status().is_success() { 43 | let body = response 44 | .text() 45 | .await 46 | .map_err(|e| CustomResult::error(Some(e.to_string()), None))?; 47 | let json: serde_json::Value = 48 | from_str(body.as_str()).map_err(|e| CustomResult::error(Some(e.to_string()), None))?; 49 | return Ok(CustomResult::success(None, Some(json))); 50 | } 51 | 52 | return Err(CustomResult::error( 53 | Some(response.status().to_string()), 54 | None, 55 | )); 56 | } 57 | 58 | #[tauri::command] 59 | pub async fn start_tts(data: TTSData) -> Result { 60 | let voice = data.voice; 61 | let text = data.text; 62 | let pitch = data.pitch; 63 | let rate = data.rate; 64 | let volume = data.volume; 65 | let sub_marker_switch = data.gender_sub_marker; 66 | let sub_marker_option = data.sub_marker_type; 67 | let merge_number = data.merge_by_number_number; 68 | let mut root_path = data.root_path; 69 | let open_folders = data.open_folders; 70 | 71 | // 检查参数 72 | if voice.is_empty() || text.is_empty() || sub_marker_option.is_empty() { 73 | return Err(CustomResult::error(Some("参数错误".to_string()), None)); 74 | } 75 | 76 | // 格式化数据 77 | let pitch_str = if pitch >= 0 { 78 | format!("+{}", pitch) 79 | } else { 80 | format!("{}", pitch) 81 | }; 82 | 83 | let rate_str = if rate >= 0 { 84 | format!("+{}", rate) 85 | } else { 86 | format!("{}", rate) 87 | }; 88 | 89 | let volumn_str = if volume >= 0 { 90 | format!("+{}", volume) 91 | } else { 92 | format!("{}", volume) 93 | }; 94 | 95 | let tts_client = TTS {}; 96 | let send_request_id = Uuid::new_v4().to_string().replace('-', ""); 97 | let sec_ms_gec_value = tts_client.generate_sec_ms_gec()?.data["hax"].clone(); 98 | let sec_ms_gec = sec_ms_gec_value 99 | .as_str() 100 | .ok_or_else(|| CustomResult::error(Some("生成令牌字符串失败".to_string()), None))?; 101 | let audio_output_format = "audio-24khz-48kbitrate-mono-mp3"; 102 | let binary_delim = "Path:audio\r\n"; 103 | 104 | let url_str = format!( 105 | "wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?\ 106 | TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4&Sec-MS-GEC={}&Sec-MS-GEC-Version=1-130.0.2849.68&ConnectionId={}", 107 | sec_ms_gec, send_request_id 108 | ); 109 | 110 | let mut request = url_str 111 | .into_client_request() 112 | .map_err(|e| CustomResult::error(Some(format!("编码URL失败:{}", e.to_string())), None))?; 113 | 114 | // 添加请求头 115 | request 116 | .headers_mut() 117 | .insert("Pragma", "no-cache".parse().unwrap()); 118 | request 119 | .headers_mut() 120 | .insert("Cache-Control", "no-cache".parse().unwrap()); 121 | request.headers_mut().insert( 122 | "Origin", 123 | "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold" 124 | .parse() 125 | .unwrap(), 126 | ); 127 | request.headers_mut().insert("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0".parse().unwrap()); 128 | request 129 | .headers_mut() 130 | .insert("Accept-Encoding", "gzip, deflate, br".parse().unwrap()); 131 | request 132 | .headers_mut() 133 | .insert("Accept-Language", "en-US,en;q=0.9".parse().unwrap()); 134 | 135 | let (mut socket, _) = connect_async(request) 136 | .await 137 | .map_err(|e| CustomResult::error(Some(format!("连接wss失败:{}", e.to_string())), None))?; 138 | 139 | // 发送音频格式设定 140 | let audio_config = tts_client.convert_to_audio_format_websocket_string(audio_output_format); 141 | socket 142 | .send(Message::Text(audio_config.into())) 143 | .await 144 | .map_err(|e| CustomResult::error(Some(format!("发送音频格式失败:{}", e)), None))?; 145 | 146 | // 发送 SSML 文本 147 | let ssml = tts_client.convert_to_ssml_websocket_string( 148 | &send_request_id, 149 | &voice, 150 | &text, 151 | &pitch_str, 152 | &rate_str, 153 | &volumn_str, 154 | ); 155 | socket 156 | .send(Message::Text(ssml.into())) 157 | .await 158 | .map_err(|e| CustomResult::error(Some(format!("发送SSML文本失败:{}", e)), None))?; 159 | 160 | // 接收数据 161 | let mut audio_data: Vec = Vec::new(); 162 | let mut messages: Vec = vec![]; 163 | 164 | while let Some(msg) = socket.next().await { 165 | let msg = 166 | msg.map_err(|e| CustomResult::error(Some(format!("读取WS输出失败:{}", e)), None))?; 167 | 168 | match msg { 169 | Message::Text(txt) => { 170 | if txt.contains("Path:turn.end") { 171 | break; 172 | } else if txt.contains("Path:audio.metadata") { 173 | // 找到第一个空行之后的第一个非空行,这应该是 JSON 数据的开始 174 | if let Some(start_index) = txt.find("\r\n\r\n") { 175 | let json_start = start_index + 2; // 跳过两个换行符 176 | let json_part = &txt[json_start..]; 177 | // 尝试解析 JSON 178 | if let Ok(json) = serde_json::from_str(json_part) { 179 | messages.push(json); 180 | } else { 181 | eprintln!("JSON 解析失败: {}", json_part); 182 | } 183 | } 184 | } 185 | } 186 | Message::Binary(bin) => { 187 | if let Some(index) = bin 188 | .windows(binary_delim.len()) 189 | .position(|w| w == binary_delim.as_bytes()) 190 | { 191 | audio_data.extend_from_slice(&bin[index + binary_delim.len()..]); 192 | } 193 | } 194 | _ => {} 195 | } 196 | } 197 | 198 | if root_path.is_empty() { 199 | root_path = ".".to_string(); 200 | } 201 | 202 | let path_str = format!("{}/{}", root_path, send_request_id); 203 | let folder_path = Path::new(&path_str); 204 | if !folder_path.exists() { 205 | fs::create_dir_all(folder_path) 206 | .map_err(|e| CustomResult::error(Some(format!("创建文件夹失败:{}", e)), None))?; 207 | } 208 | 209 | // 保存音频数据 210 | let output_path = format!("{}/output_{}.mp3", path_str, send_request_id); 211 | let mut file = File::create(&output_path) 212 | .map_err(|e| CustomResult::error(Some(format!("创建音频文件失败:{}", e)), None))?; 213 | file.write_all(&audio_data) 214 | .map_err(|e| CustomResult::error(Some(format!("写入音频文件失败:{}", e)), None))?; 215 | 216 | // 写入JSON数据(仅用于测试) 217 | // let json_path = format!("{}/output_{}.json", path_str, send_request_id); 218 | // let mut json_file = File::create(&json_path) 219 | // .map_err(|e| CustomResult::error(Some(format!("创建JSON文件失败:{}", e)), None))?; 220 | // let json_str = serde_json::to_string(&messages) 221 | // .map_err(|e| CustomResult::error(Some(format!("序列化JSON失败:{}", e)), None))?; 222 | // json_file 223 | // .write_all(json_str.as_bytes()) 224 | // .map_err(|e| CustomResult::error(Some(format!("写入JSON文件失败:{}", e)), None))?; 225 | 226 | // 写入文本数据(仅用于测试) 227 | // let text_path = format!("{}/output_{}.txt", path_str, send_request_id); 228 | // let mut text_file = File::create(&text_path) 229 | // .map_err(|e| CustomResult::error(Some(format!("创建文本文件失败:{}", e)), None))?; 230 | // let text = text.replace("\n", "\r\n"); 231 | // text_file 232 | // .write_all(text.as_bytes()) 233 | // .map_err(|e| CustomResult::error(Some(format!("写入文本文件失败:{}", e)), None))?; 234 | 235 | if sub_marker_switch { 236 | let _ = generate_srt( 237 | &messages, 238 | &format!("{}/output_{}.srt", path_str, send_request_id), 239 | &sub_marker_option, 240 | merge_number, 241 | Some(&text), 242 | ); 243 | } 244 | 245 | if open_folders { 246 | // 打开文件夹 247 | let final_path = if cfg!(target_os = "windows") { 248 | path_str.replace("/", "\\") 249 | } else { 250 | path_str 251 | }; 252 | Command::new("explorer") 253 | .arg(Path::new(final_path.as_str())) 254 | .status() 255 | .map_err(|e| { 256 | CustomResult::error( 257 | Some(format!( 258 | "打开文件夹失败:{}
请手动前往文件夹查看:{}", 259 | e, final_path 260 | )), 261 | None, 262 | ) 263 | })?; 264 | } 265 | 266 | // 编码成base64 267 | let base64_audio = encode_audio_to_base64(&output_path)?; 268 | 269 | Ok(CustomResult::success( 270 | None, 271 | Some(json!({"audio": base64_audio})), 272 | )) 273 | } 274 | 275 | #[tauri::command] 276 | pub async fn get_exe_path() -> Result { 277 | let exe_path = std::env::current_exe() 278 | .map_err(|e| CustomResult::error(Some(format!("当前执行路径获取失败:{}", e)), None))?; 279 | 280 | if let Some(exe_dir) = exe_path.parent() { 281 | return Ok(CustomResult::success( 282 | None, 283 | Some(json!({"path": exe_dir.display().to_string()})), 284 | )); 285 | } else { 286 | return Err(CustomResult::error( 287 | Some(format!("无法获取可执行文件的父目录")), 288 | None, 289 | )); 290 | } 291 | } 292 | 293 | #[tauri::command] 294 | pub async fn get_app_version(app_handle: tauri::AppHandle) -> Result { 295 | let package_info = app_handle.package_info(); 296 | let version = package_info.version.to_string(); 297 | 298 | Ok(CustomResult::success( 299 | None, 300 | Some(json!({"version": version})), 301 | )) 302 | } 303 | 304 | fn encode_audio_to_base64(file_path: &str) -> Result { 305 | let audio_bytes = std::fs::read(file_path) 306 | .map_err(|e| CustomResult::error(Some(format!("读取音频文件失败:{}", e)), None))?; 307 | 308 | let encoded = general_purpose::STANDARD.encode(audio_bytes); 309 | 310 | Ok(encoded) 311 | } 312 | -------------------------------------------------------------------------------- /src-tauri/src/utils/custom_result.rs: -------------------------------------------------------------------------------- 1 | use serde_json::{json, Value}; 2 | 3 | #[derive(serde::Serialize)] 4 | pub struct CustomResult { 5 | pub code: i32, 6 | pub msg: String, 7 | pub data: Value, 8 | } 9 | 10 | impl CustomResult { 11 | pub fn new(code: i32, msg: String, data: Value) -> Self { 12 | Self { code, msg, data } 13 | } 14 | 15 | pub fn success(msg: Option, data: Option) -> Self { 16 | Self::new( 17 | 200, 18 | msg.unwrap_or("Success".to_string()), 19 | data.unwrap_or(json!(null)), 20 | ) 21 | } 22 | 23 | pub fn error(msg: Option, data: Option) -> Self { 24 | Self::new( 25 | 500, 26 | msg.unwrap_or("error".to_string()), 27 | data.unwrap_or(json!(null)), 28 | ) 29 | } 30 | 31 | pub fn to_string(&self) -> String { 32 | serde_json::to_string(self).unwrap() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src-tauri/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod api; 2 | pub mod custom_result; 3 | pub mod sub_marker; 4 | pub mod tts; 5 | -------------------------------------------------------------------------------- /src-tauri/src/utils/sub_marker.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::fs::File; 3 | use std::io::Write; 4 | 5 | use super::custom_result::CustomResult; 6 | 7 | #[derive(Debug, Clone)] 8 | struct Cue { 9 | index: usize, 10 | start: String, 11 | end: String, 12 | content: String, 13 | } 14 | 15 | pub struct SubMaker { 16 | cues: Vec, 17 | } 18 | 19 | impl SubMaker { 20 | pub fn new() -> Self { 21 | SubMaker { cues: Vec::new() } 22 | } 23 | 24 | pub fn feed(&mut self, msg: &serde_json::Value) -> Result { 25 | if msg["Type"] != "WordBoundary" { 26 | return Err(CustomResult::error( 27 | Some("错误的Message类型,仅支持'WordBoundary'".to_string()), 28 | None, 29 | )); 30 | } 31 | 32 | let offset = msg["Data"]["Offset"].as_u64().unwrap(); 33 | let duration = msg["Data"]["Duration"].as_u64().unwrap(); 34 | let text = msg["Data"]["text"]["Text"].as_str().unwrap().to_string(); 35 | 36 | let start = self.microseconds_to_time(offset); 37 | let end = self.microseconds_to_time(offset + duration); 38 | 39 | self.cues.push(Cue { 40 | index: self.cues.len() + 1, 41 | start, 42 | end, 43 | content: text, 44 | }); 45 | 46 | Ok(CustomResult::success(None, None)) 47 | } 48 | 49 | pub fn merge_by_number(&mut self, words: i32) -> Result { 50 | if words == 0 || self.cues.is_empty() { 51 | return Ok(CustomResult::success(None, None)); 52 | } 53 | 54 | let mut new_cues = Vec::new(); 55 | let mut current_cue = self.cues[0].clone(); 56 | 57 | for cue in self.cues.iter().skip(1) { 58 | let word_count = current_cue.content.split_whitespace().count(); 59 | 60 | if word_count < words.try_into().unwrap() { 61 | current_cue.end = cue.end.clone(); 62 | current_cue.content = format!("{} {}", current_cue.content, cue.content); 63 | } else { 64 | new_cues.push(current_cue); 65 | current_cue = cue.clone(); 66 | } 67 | } 68 | 69 | new_cues.push(current_cue); 70 | self.cues = new_cues; 71 | self.reset_index(); 72 | 73 | Ok(CustomResult::success(None, None)) 74 | } 75 | 76 | pub fn merge_by_punctuation(&mut self, all_text: &str) -> Result { 77 | if all_text.is_empty() { 78 | return Err(CustomResult::error( 79 | Some("你选择了标点分句,但参考句子是空的".to_string()), 80 | None, 81 | )); 82 | } 83 | 84 | let mut new_cues: Vec = Vec::new(); 85 | let mut current_index = 0; 86 | let mut last_match_end: isize = -1; 87 | let mut match_start_index = 0; 88 | let punctuation_re = regex::Regex::new(r#"[。!??,;,()\[\]()【】{}、\.\?!;:<>《》「」『』“”‘’"…\n]+"#).map_err(|e| CustomResult::error(Some(e.to_string()), None))?; 89 | 90 | for i in 0..self.cues.len() { 91 | let cue = &self.cues[i]; 92 | let search_text = &cue.content; 93 | 94 | if let Some(position) = all_text[current_index..].find(search_text) { 95 | let position = current_index + position; 96 | if last_match_end == -1 { 97 | last_match_end = position as isize; 98 | } 99 | 100 | let mut next_position = position + search_text.len(); 101 | let mut max_while = 0; 102 | let mut chars = all_text[next_position..].chars(); 103 | 104 | // 如果是英文,这里有时会变成空格,需要过滤一下 105 | while let Some(next_char) = chars.next() { 106 | // 如果不是空格 或 超过了最大限制则退出 107 | if next_char != ' ' || max_while == 10 { 108 | break; 109 | } 110 | next_position += next_char.len_utf8(); 111 | max_while += 1; 112 | } 113 | 114 | // 判断下一个字符是否是标点 115 | let next_char = all_text[next_position..].chars().next().unwrap_or(' '); 116 | if punctuation_re.is_match(&next_char.to_string()) { 117 | let start = self.cues[match_start_index].start.clone(); 118 | let end_pos = next_position; 119 | let extracted_text = &all_text[last_match_end as usize..end_pos]; 120 | new_cues.push(Cue { 121 | index: cue.index, 122 | start, 123 | end: cue.end.clone(), 124 | content: extracted_text.to_string(), 125 | }); 126 | last_match_end = -1; 127 | match_start_index = i + 1; 128 | } 129 | 130 | current_index = position; 131 | } 132 | } 133 | 134 | self.cues = new_cues; 135 | self.reset_index(); 136 | 137 | Ok(CustomResult::success(None, None)) 138 | } 139 | 140 | pub fn get_srt(&self) -> String { 141 | self.cues 142 | .iter() 143 | .map(|cue| { 144 | format!( 145 | "{}\n{} --> {}\n{}\n", 146 | cue.index, 147 | cue.start, 148 | cue.end, 149 | cue.content.trim() 150 | ) 151 | }) 152 | .collect::>() 153 | .join("\n") 154 | } 155 | 156 | fn reset_index(&mut self) { 157 | for (i, cue) in self.cues.iter_mut().enumerate() { 158 | cue.index = i + 1; 159 | } 160 | } 161 | 162 | fn microseconds_to_time(&self, microseconds: u64) -> String { 163 | let milliseconds = (microseconds / 10000) as u64; 164 | let hours = milliseconds / 3600000; 165 | let minutes = (milliseconds % 3600000) / 60000; 166 | let seconds = (milliseconds % 60000) / 1000; 167 | let ms = milliseconds % 1000; 168 | format!("{:02}:{:02}:{:02},{:03}", hours, minutes, seconds, ms) 169 | } 170 | } 171 | 172 | // 写入 SRT 文件 173 | pub fn generate_srt( 174 | messages: &[serde_json::Value], 175 | srt_path: &str, 176 | option: &str, 177 | number: i32, 178 | all_text: Option<&str>, 179 | ) -> Result { 180 | let mut maker = SubMaker::new(); 181 | 182 | for msg in messages { 183 | if let Some(meta) = msg.get("Metadata").and_then(|v| v.get(0)) { 184 | if meta["Type"] == "WordBoundary" { 185 | let _ = maker.feed(meta)?; 186 | } 187 | } 188 | } 189 | 190 | match option { 191 | "mergeByPunctuation" => { 192 | let _ = maker.merge_by_punctuation(all_text.unwrap_or(""))?; 193 | } 194 | "mergeByNumber" => { 195 | let _ = maker.merge_by_number(number)?; 196 | } 197 | _ => {} 198 | } 199 | 200 | let srt_content = maker.get_srt(); 201 | let mut file = 202 | File::create(srt_path).map_err(|e| CustomResult::error(Some(e.to_string()), None))?; 203 | file.write_all(srt_content.as_bytes()) 204 | .map_err(|e| CustomResult::error(Some(e.to_string()), None))?; 205 | Ok(CustomResult::success(None, None)) 206 | } 207 | -------------------------------------------------------------------------------- /src-tauri/src/utils/tts.rs: -------------------------------------------------------------------------------- 1 | //! 本文件内代码参考开源项目:https://github.com/rany2/edge-tts/tree/master 2 | 3 | use crate::utils::custom_result::CustomResult; 4 | use chrono::Utc; 5 | use hex; 6 | use serde_json::json; 7 | use sha2::{Digest, Sha256}; 8 | 9 | pub struct TTS {} 10 | 11 | impl TTS { 12 | fn num_to_str(&self, num: &str) -> String { 13 | if !num.contains('e') { 14 | return num.to_string(); 15 | } 16 | let mut num_str = num 17 | .trim_matches(|c| c == ' ' || c == '"' || c == '=' || c == '\'') 18 | .to_string(); 19 | num_str.retain(|c| c.is_digit(10)); 20 | let mut result = String::new(); 21 | let mut n = num_str.parse::().unwrap_or(0); 22 | while n > 0 { 23 | let v = n % 10; 24 | n /= 10; 25 | result.insert(0, std::char::from_digit(v as u32, 10).unwrap()); 26 | } 27 | result 28 | } 29 | 30 | pub fn convert_to_audio_format_websocket_string(&self, output_format: &str) -> String { 31 | format!( 32 | "X-Timestamp:{}\r\nContent-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n{{\"context\":{{\"synthesis\":{{\"audio\":{{\"metadataoptions\":{{\"sentenceBoundaryEnabled\":\"false\",\"wordBoundaryEnabled\":\"true\"}},\"outputFormat\":\"{}\"}}}}}}}}", 33 | self.date_to_string(), 34 | output_format 35 | ) 36 | } 37 | 38 | fn convert_to_ssml_text( 39 | &self, 40 | voice: &str, 41 | text: &str, 42 | pitch: &str, 43 | rate: &str, 44 | volume: &str, 45 | ) -> String { 46 | format!( 47 | "{}", 48 | voice, pitch, rate, volume, text 49 | ) 50 | } 51 | 52 | pub fn convert_to_ssml_websocket_string( 53 | &self, 54 | request_id: &str, 55 | voice: &str, 56 | msg: &str, 57 | pitch: &str, 58 | rate: &str, 59 | volume: &str, 60 | ) -> String { 61 | format!( 62 | "X-RequestId:{}\r\nContent-Type:application/ssml+xml\r\nX-Timestamp:{}Z\r\nPath:ssml\r\n\r\n{}", 63 | request_id, 64 | self.date_to_string(), 65 | self.convert_to_ssml_text(voice, msg, pitch, rate, volume) 66 | ) 67 | } 68 | 69 | fn date_to_string(&self) -> String { 70 | let now = Utc::now(); 71 | now.format("%a %b %d %Y %H:%M:%S GMT+0000 (Coordinated Universal Time)") 72 | .to_string() 73 | } 74 | 75 | fn get_unix_timestamp(&self) -> f64 { 76 | Utc::now().timestamp() as f64 77 | } 78 | 79 | pub fn generate_sec_ms_gec(&self) -> Result { 80 | let mut ticks = self.get_unix_timestamp(); 81 | ticks += 11644473600.0; 82 | ticks = (ticks / 300.0).floor() * 300.0; 83 | let ticks_int = (ticks * 1e7) as i64; 84 | let str_to_hash = format!( 85 | "{}{}", 86 | self.num_to_str(&ticks_int.to_string()), 87 | "6A5AA1D4EAFF4E9FB37E23D68491D6F4" 88 | ); 89 | let mut hasher = Sha256::new(); 90 | hasher.update(&str_to_hash); 91 | let result = hasher.finalize(); 92 | let hex_str = hex::encode(result).to_uppercase(); 93 | 94 | Ok(CustomResult::success(None, Some(json!({ "hax": hex_str })))) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "tts-tauri", 4 | "version": "0.2.0", 5 | "identifier": "com.tts-tauri.app", 6 | "build": { 7 | "beforeDevCommand": "npm run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "npm run build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "tts-tauri-吾爱破解论坛首发", 16 | "width": 1280, 17 | "height": 720, 18 | "minWidth": 1024, 19 | "minHeight": 576 20 | } 21 | ], 22 | "security": { 23 | "csp": null 24 | } 25 | }, 26 | "bundle": { 27 | "active": true, 28 | "targets": "all", 29 | "icon": [ 30 | "icons/32x32.png", 31 | "icons/128x128.png", 32 | "icons/128x128@2x.png", 33 | "icons/icon.icns", 34 | "icons/icon.ico" 35 | ], 36 | "windows": { 37 | "webviewInstallMode": { 38 | "type": "embedBootstrapper" 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src-tauri/test/output.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:00,100 --> 00:00:02,112 3 | 点开控制面板中的数据库 4 | 5 | 2 6 | 00:00:02,375 --> 00:00:03,575 7 | 创建一个数据库 8 | 9 | 3 10 | 00:00:03,812 --> 00:00:04,587 11 | 输入密码 12 | 13 | 4 14 | 00:00:04,812 --> 00:00:05,650 15 | 选择版本 16 | 17 | 5 18 | 00:00:05,987 --> 00:00:07,512 19 | 这里我选择5.7 20 | 21 | 6 22 | 00:00:07,725 --> 00:00:11,250 23 | 因为我这个项目用的5版本的Mysql 24 | 25 | 7 26 | 00:00:11,512 --> 00:00:13,100 27 | 选择8可能会出错 28 | 29 | 8 30 | 00:00:13,337 --> 00:00:16,937 31 | 创建完成后就可以看到数据库的连接信息了 32 | 33 | 9 34 | 00:00:17,500 --> 00:00:20,350 35 | 点这个可以打开数据库管理系统 36 | 37 | 10 38 | 00:00:20,600 --> 00:00:21,787 39 | 我把表导入一下 40 | 41 | 11 42 | 00:00:22,362 --> 00:00:23,975 43 | 这样就把表创建好了 44 | 45 | 12 46 | 00:00:24,300 --> 00:00:26,637 47 | 把代码里的连接信息更新完后 48 | 49 | 13 50 | 00:00:26,825 --> 00:00:28,975 51 | 就可以上传api的代码了 52 | 53 | 14 54 | 00:00:29,200 --> 00:00:31,725 55 | 忽略规则文件和数据库文件 56 | 57 | 15 58 | 00:00:32,062 --> 00:00:34,100 59 | 现在就可以正常使用网站了 60 | 61 | 16 62 | 00:00:34,662 --> 00:00:36,937 63 | 最后是虚拟主机注意事项 64 | 65 | 17 66 | 00:00:37,500 --> 00:00:37,750 67 | 1 68 | 69 | 18 70 | 00:00:38,087 --> 00:00:40,112 71 | 免费账号出于安全原因 72 | 73 | 19 74 | 00:00:40,362 --> 00:00:42,537 75 | 会禁用下面的php函数 76 | 77 | 20 78 | 00:00:43,100 --> 00:00:43,375 79 | 2 80 | 81 | 21 82 | 00:00:43,700 --> 00:00:46,225 83 | php内存限制为64兆 84 | 85 | 22 86 | 00:00:46,500 --> 00:00:47,962 87 | 付费用户可更改 88 | 89 | 23 90 | 00:00:48,525 --> 00:00:48,887 91 | 3 92 | 93 | 24 94 | 00:00:49,250 --> 00:00:51,175 95 | 在主机的设置中可以看到 96 | 97 | 25 98 | 00:00:51,437 --> 00:00:53,387 99 | 传出连接为Not allowed 100 | 101 | 26 102 | 00:00:53,612 --> 00:00:57,850 103 | 这表示无法使用php请求外部的api接口 104 | 105 | 27 106 | 00:00:58,425 --> 00:00:58,775 107 | 4 108 | 109 | 28 110 | 00:00:59,125 --> 00:01:01,387 111 | 账户有效期虽然显示一年 112 | 113 | 29 114 | 00:01:01,625 --> 00:01:03,300 115 | 但到期后会自动续 116 | 117 | 30 118 | 00:01:03,862 --> 00:01:04,175 119 | 5 120 | 121 | 31 122 | 00:01:04,512 --> 00:01:07,062 123 | 主机支持1个域名和3个子域 124 | 125 | 32 126 | 00:01:07,375 --> 00:01:09,375 127 | 并且他们是相互独立的 128 | 129 | 33 130 | 00:01:09,737 --> 00:01:11,825 131 | 代表总共能创建4个网站 132 | 133 | 34 134 | 00:01:12,400 --> 00:01:14,100 135 | 关于流量和空间问题 136 | 137 | 35 138 | 00:01:14,412 --> 00:01:16,287 139 | 建议图片托管到图床 140 | 141 | 36 142 | 00:01:16,537 --> 00:01:17,987 143 | 文件则使用网盘 144 | 145 | 37 146 | 00:01:18,212 --> 00:01:20,900 147 | 这样1G空间和5G流量的配额 148 | 149 | 38 150 | 00:01:21,162 --> 00:01:23,112 151 | 一个小型网站完全够用 152 | 153 | 39 154 | 00:01:23,687 --> 00:01:26,750 155 | 关于三六五免费主机就介绍到这里 156 | 157 | 40 158 | 00:01:27,137 --> 00:01:29,987 159 | 后面我也会分享更多可白嫖的资源 160 | 161 | 41 162 | 00:01:30,212 --> 00:01:32,225 163 | 比如更快的php托管 164 | 165 | 42 166 | 00:01:32,500 --> 00:01:35,537 167 | 支持Python和Node js的虚拟主机 168 | 169 | 43 170 | 00:01:35,850 --> 00:01:38,075 171 | 支持自选服务器地区的主机 172 | 173 | 44 174 | 00:01:38,312 --> 00:01:41,000 175 | 你们的一键三连就是我更新的动力 176 | 177 | 45 178 | 00:01:41,275 --> 00:01:42,187 179 | 下期再见 180 | -------------------------------------------------------------------------------- /src-tauri/test/sub_title_test.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | // #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | use utils::sub_marker::generate_srt; 4 | pub mod utils; 5 | 6 | use std::fs; 7 | use std::io; 8 | use serde_json::{from_str, Value}; 9 | 10 | fn main() -> io::Result<()> { 11 | // tts_tauri_lib::run() 12 | let json_path = "test/复杂文本2.json"; 13 | let json_content = fs::read_to_string(json_path)?; 14 | let mut messages: Vec = from_str::>(&json_content)?.to_vec(); 15 | 16 | let txt_path = "test/复杂文本2.txt"; 17 | let txt_content = fs::read_to_string(txt_path)?; 18 | 19 | let result = generate_srt(&messages, "test/output.srt", "mergeByNumber", 5, Some(txt_content.as_str())); 20 | 21 | match result { 22 | Ok(_) => print!("成功"), 23 | Err(_) => print!("失败") 24 | }; 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /src-tauri/test/复杂文本.json: -------------------------------------------------------------------------------- 1 | [{"Metadata":[{"Data":{"Duration":2875000,"Offset":1000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"如果"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":3875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"您"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":5125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"有"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5625000,"Offset":6375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"兴趣"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":13875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"请"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5375000,"Offset":15875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"阅读"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6000000,"Offset":26750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"谢谢"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3875000,"Offset":38375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"以下"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":42375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4375000,"Offset":43875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"详细"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4750000,"Offset":48250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"内容"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3750000,"Offset":58625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"饭量"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":62375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"很"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":64250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"好"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1000000,"Offset":66250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":67375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"女孩"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":71500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"笑"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":80250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"从"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3750000,"Offset":82500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"那时"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":86250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"起"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":91625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"她"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":93125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"就"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":94625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"变得"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":97375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"爱"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":99125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"哭"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3000000,"Offset":102000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"笑"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":110625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"就"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":112500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"在"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":114250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"那"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":115375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"一"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":116500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"年"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":118250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":119125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"师"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":121125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"走"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":126625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3875000,"Offset":128000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"邀请"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":131875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"她"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":133375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"要不"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":135875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"去"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4125000,"Offset":140125000,"text":{"BoundaryType":"WordBoundary","Length":4,"Text":"Idea"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":144375000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"Pocket"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4375000,"Offset":149500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"玩玩"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":159625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"在"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":161875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"去"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1000000,"Offset":163500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4625000,"Offset":164500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"路上"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":171875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"她"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":173375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"跟"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":174750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":176125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"说"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":179875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":181000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"要"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":182375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"拍"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3875000,"Offset":184750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"AV"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":188625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"了"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3875000,"Offset":192125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"当天"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":196000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"我们"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":198250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"只是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":201000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"和"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4875000,"Offset":202625000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"制作人"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":207750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"喝"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":209375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"了"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":210250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"茶"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":216750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"然后"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":220125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"约"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":221750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"在"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":223250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"2月"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":227125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"再"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":228625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"面谈"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2375000,"Offset":239250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"5个"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":241625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"月"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":243500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"后"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4000000,"Offset":249125000,"text":{"BoundaryType":"WordBoundary","Length":4,"Text":"Idea"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6500000,"Offset":253250000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"Pocket"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":260750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"终于"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":264250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"允许"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":267875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"她"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":269375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"再次"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":273125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"拍摄"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":283875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":285250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"不想"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":288750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"歧视"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":292625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"任何"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":296250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"一"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":297375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"位"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5250000,"Offset":299000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"粉丝"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":307500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"能"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":308750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"被"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2625000,"Offset":310125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"他们"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4000000,"Offset":313000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"亲切"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":317000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"地"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":318000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"称为"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4875000,"Offset":321000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"社长"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":328250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":329625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"感到"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":333000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"很"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4875000,"Offset":335000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"高兴"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":345500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":347125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"住院"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1000000,"Offset":350500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":351750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"第"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":353375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"7个"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3000000,"Offset":356625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"月"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":362250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"她"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4250000,"Offset":364000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"收到"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2375000,"Offset":368250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"了"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4250000,"Offset":371625000,"text":{"BoundaryType":"WordBoundary","Length":5,"Text":"FANZA"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":376000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"成人"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":379375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"奖"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":750000,"Offset":381250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5375000,"Offset":382125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"提名"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":393250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":394750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"在"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":8250000,"Offset":396625000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"1000多个"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2625000,"Offset":405875000,"text":{"BoundaryType":"WordBoundary","Length":4,"Text":"LINE"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4125000,"Offset":408625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"好友"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":412750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"里"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":415000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"发出"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":750000,"Offset":418125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"了"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":419000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"号召"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":429750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"当然"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":433000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"还有"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4375000,"Offset":435875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"我们"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":441250000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"Miyano"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":446250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"家"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":448125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":449250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"所有"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":452750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"成员"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":463500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"她"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":465000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"从"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":466625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"一"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":467750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"名"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":470125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"精英"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4875000,"Offset":473500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"出道"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":480500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"尝"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":482625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"到"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3750000,"Offset":484250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"失败"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":750000,"Offset":488000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5250000,"Offset":488875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"痛苦"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":497375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"现在"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":500750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3875000,"Offset":502750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"时候"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":506875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"再次"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4250000,"Offset":510375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"闪耀"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":514625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"了"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":515500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"吧"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3875000,"Offset":523250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"竞争"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4625000,"Offset":527125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"对手"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":532000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"从"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":533750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"出道"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":537125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"起"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":541625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"就"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":543250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":8750000,"Offset":544500000,"text":{"BoundaryType":"WordBoundary","Length":4,"Text":"光芒四射"}},"Type":"WordBoundary"}]}] -------------------------------------------------------------------------------- /src-tauri/test/复杂文本.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/test/复杂文本.mp3 -------------------------------------------------------------------------------- /src-tauri/test/复杂文本.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:00,100 --> 00:00:01,200 3 | 如果您有兴趣 4 | 5 | 2 6 | 00:00:01,387 --> 00:00:02,125 7 | 请阅读 8 | 9 | 3 10 | 00:00:02,675 --> 00:00:04,225 11 | 谢谢以下 12 | 13 | 4 14 | 00:00:04,237 --> 00:00:55,325 15 | 是详细内容饭量很好的女孩笑从那时起她就变得爱哭笑就在那一年的师走我邀请她要不去IdeaPocket玩玩在去的路上她跟我说我要拍AV了当天我们只是和制作人喝了茶然后约在2月再面谈5个月后IdeaPocket终于允许她再次拍摄我不想歧视任何一位粉丝能被他们亲切地称为社长我感到很高兴我住院的第7个月她收到了FANZA成人奖的提名我在1000多个LINE好友里发出了号召当然还有我们Miyano家的所有成员她从一名精英出道尝到失败的痛苦现在是时候再次闪耀了吧竞争对手从出道起就是光芒四射 16 | -------------------------------------------------------------------------------- /src-tauri/test/复杂文本.txt: -------------------------------------------------------------------------------- 1 | 如果您有兴趣,请阅读。谢谢! 2 | 3 | (以下是详细内容) 4 | 5 | 饭量很好的女孩(笑)。 6 | 从那时起,她就变得爱哭(笑)。就在那一年的师走,我邀请她“要不去Idea Pocket玩玩?” 7 | 在去的路上,她跟我说“我要拍AV了”......当天我们只是和制作人喝了茶,然后约在2月再面谈。 8 | 5个月后,Idea Pocket终于允许她再次拍摄。 9 | 我不想歧视任何一位粉丝,能被他们亲切地称为“社长”,我感到很高兴。 10 | 我住院的第7个月,她收到了FANZA成人奖的提名。 11 | 我在1000多个LINE好友里发出了号召。 12 | 当然还有我们Miyano家的所有成员。 13 | 她从一名精英出道,尝到失败的痛苦,现在是时候再次闪耀了吧?竞争对手从出道起就是光芒四射。 -------------------------------------------------------------------------------- /src-tauri/test/复杂文本2.json: -------------------------------------------------------------------------------- 1 | [{"Metadata":[{"Data":{"Duration":2500000,"Offset":1000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"那么"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":3750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"可以"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2875000,"Offset":6125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"借助"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4875000,"Offset":9125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"这个"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":7125000,"Offset":15000000,"text":{"BoundaryType":"WordBoundary","Length":10,"Text":"cloudflare"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":22125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":23375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"D"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":25500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"1"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":31625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"这个"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":35000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":36500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"可以"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":39125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"直接"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3750000,"Offset":42375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"添加"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5500000,"Offset":46375000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"数据库"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":51875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":56250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"也是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5125000,"Offset":59125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"免费"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":64250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":68250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"额度"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2625000,"Offset":71875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"个人"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5125000,"Offset":74625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"使用"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":80750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"也是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4125000,"Offset":83625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"完全"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4125000,"Offset":87875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"够用"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":92000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":99500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"这"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":101250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"三"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":103500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"者"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":105375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"结合"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4750000,"Offset":109125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"起来"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":114875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"就"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2375000,"Offset":116375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"可以"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":118875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"做"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2250000,"Offset":120500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"一个"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4125000,"Offset":123000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"完全"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":127250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"动态"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":130750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4250000,"Offset":131875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"前后"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":136250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"端"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":138375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"分离"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":141625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4500000,"Offset":142625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"网站"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":152875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"并且"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":156250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"他们"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":158750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"在"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":160500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"国内"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3375000,"Offset":164250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"访问"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":167625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5375000,"Offset":168625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"速度"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2875000,"Offset":175000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"也是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4000000,"Offset":178000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"非常"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":182000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"快"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":184750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2875000,"Offset":192125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"如果"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":195000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"有"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5250000,"Offset":196375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"同学"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":201875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"想"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2625000,"Offset":204625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"继续"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4875000,"Offset":207375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"了解"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6625000,"Offset":213375000,"text":{"BoundaryType":"WordBoundary","Length":10,"Text":"cloudflare"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":220000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":221375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"D"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":223000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"1"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":225250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"以及"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6500000,"Offset":228750000,"text":{"BoundaryType":"WordBoundary","Length":7,"Text":"Workers"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2750000,"Offset":237500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"可以"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":240250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"直接"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":243500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"到"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2875000,"Offset":245125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"这里"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":248000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5125000,"Offset":249000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"网页"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":255125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"来"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3750000,"Offset":256625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"查看"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":260625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"官方"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4500000,"Offset":264000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"文档"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2625000,"Offset":272250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"关于"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":275000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5625000,"Offset":276500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"后面"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4000000,"Offset":282375000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"是否"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":286375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"会"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":288000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"再"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":289375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"做"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5125000,"Offset":290875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"教程"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5500000,"Offset":298000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"介绍"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6750000,"Offset":304500000,"text":{"BoundaryType":"WordBoundary","Length":7,"Text":"Workers"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":312250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"以及"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":315500000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"D"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":317125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"1"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3750000,"Offset":318875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"或者"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3500000,"Offset":322625000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"其他"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":326375000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"更"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":328000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"快"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":329875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4000000,"Offset":330875000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"网站"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":335125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"托管"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4500000,"Offset":338750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"服务"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1875000,"Offset":347000000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"你们"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":348875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":350000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"一"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":351250000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"键"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2500000,"Offset":353125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"三"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3250000,"Offset":355625000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"连"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1750000,"Offset":360000000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"就"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1125000,"Offset":361750000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"是"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1375000,"Offset":363125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"我"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3625000,"Offset":364500000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"更新"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":368125000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"的"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4125000,"Offset":369125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"动力"}},"Type":"WordBoundary"}]}] -------------------------------------------------------------------------------- /src-tauri/test/复杂文本2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/test/复杂文本2.mp3 -------------------------------------------------------------------------------- /src-tauri/test/复杂文本2.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:01,000 --> 00:00:28,000 3 | 那么可以借助这个cloudflare的D1 4 | 5 | 2 6 | 00:00:31,625 --> 00:00:53,750 7 | 这个是可以直接添加数据库的 8 | 9 | 3 10 | 00:00:56,250 --> 00:01:06,000 11 | 也是免费的 12 | 13 | 4 14 | 00:01:08,250 --> 00:01:33,875 15 | 额度个人使用也是完全够用的 16 | 17 | 5 18 | 00:01:39,500 --> 00:02:27,125 19 | 这三者结合起来就可以做一个完全动态的前后端分离的网站 20 | 21 | 6 22 | 00:02:32,875 --> 00:03:06,500 23 | 并且他们在国内访问的速度也是非常快的 24 | 25 | 7 26 | 00:03:12,125 --> 00:03:55,250 27 | 如果有同学想继续了解cloudflare的D1以及Workers 28 | 29 | 8 30 | 00:03:57,500 --> 00:04:28,500 31 | 可以直接到这里的网页来查看官方文档 32 | 33 | 9 34 | 00:04:32,250 --> 00:04:56,000 35 | 关于我后面是否会再做教程 36 | 37 | 10 38 | 00:04:58,000 --> 00:05:43,250 39 | 介绍Workers以及D1或者其他更快的网站托管服务 40 | 41 | 11 42 | 00:05:47,000 --> 00:06:13,250 43 | 你们的一键三连就是我更新的动力 44 | -------------------------------------------------------------------------------- /src-tauri/test/复杂文本2.txt: -------------------------------------------------------------------------------- 1 | 那么可以借助这个cloudflare的D1,这个是可以直接添加数据库的,也是免费的,额度个人使用也是完全够用的。这三者结合起来就可以做一个完全动态的前后端分离的网站。并且他们在国内访问的速度也是非常快的。如果有同学想继续了解cloudflare的D1以及Workers,可以直接到这里的网页来查看官方文档,关于我后面是否会再做教程,介绍Workers以及D1或者其他更快的网站托管服务,你们的一键三连就是我更新的动力。 -------------------------------------------------------------------------------- /src-tauri/test/复杂文本3.json: -------------------------------------------------------------------------------- 1 | [{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1000000,"Duration":2000000,"text":{"Text":"\u70b9","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":3000000,"Duration":2125000,"text":{"Text":"\u5f00","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":5375000,"Duration":7250000,"text":{"Text":"\u63a7\u5236\u9762\u677f","Length":4,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":12625000,"Duration":1500000,"text":{"Text":"\u4e2d","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":14125000,"Duration":750000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":15000000,"Duration":6125000,"text":{"Text":"\u6570\u636e\u5e93","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":23750000,"Duration":4000000,"text":{"Text":"\u521b\u5efa","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":27750000,"Duration":1750000,"text":{"Text":"\u4e00\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":29625000,"Duration":6125000,"text":{"Text":"\u6570\u636e\u5e93","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":38125000,"Duration":3375000,"text":{"Text":"\u8f93\u5165","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":41500000,"Duration":4375000,"text":{"Text":"\u5bc6\u7801","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":48125000,"Duration":3375000,"text":{"Text":"\u9009\u62e9","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":51625000,"Duration":4875000,"text":{"Text":"\u7248\u672c","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":59875000,"Duration":2875000,"text":{"Text":"\u8fd9\u91cc","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":62875000,"Duration":1375000,"text":{"Text":"\u6211","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":64250000,"Duration":3875000,"text":{"Text":"\u9009\u62e9","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":68375000,"Duration":6750000,"text":{"Text":"5.7","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":77250000,"Duration":2875000,"text":{"Text":"\u56e0\u4e3a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":80125000,"Duration":1125000,"text":{"Text":"\u6211","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":81375000,"Duration":2250000,"text":{"Text":"\u8fd9\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":83625000,"Duration":5625000,"text":{"Text":"\u9879\u76ee","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":89500000,"Duration":1750000,"text":{"Text":"\u7528","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":91250000,"Duration":1000000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":92500000,"Duration":1500000,"text":{"Text":"5","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":94000000,"Duration":3875000,"text":{"Text":"\u7248\u672c","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":97875000,"Duration":2000000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":100125000,"Duration":12375000,"text":{"Text":"Mysql","Length":5,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":115125000,"Duration":3625000,"text":{"Text":"\u9009\u62e9","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":118875000,"Duration":2375000,"text":{"Text":"8","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":121500000,"Duration":2750000,"text":{"Text":"\u53ef\u80fd","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":124250000,"Duration":1375000,"text":{"Text":"\u4f1a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":125750000,"Duration":5250000,"text":{"Text":"\u51fa\u9519","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":133375000,"Duration":3875000,"text":{"Text":"\u521b\u5efa","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":137375000,"Duration":3750000,"text":{"Text":"\u5b8c\u6210","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":141125000,"Duration":3125000,"text":{"Text":"\u540e","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":145250000,"Duration":1500000,"text":{"Text":"\u5c31","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":146750000,"Duration":2250000,"text":{"Text":"\u53ef\u4ee5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":149000000,"Duration":3625000,"text":{"Text":"\u770b\u5230","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":152875000,"Duration":5250000,"text":{"Text":"\u6570\u636e\u5e93","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":158125000,"Duration":875000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":159250000,"Duration":3250000,"text":{"Text":"\u8fde\u63a5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":162625000,"Duration":4750000,"text":{"Text":"\u4fe1\u606f","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":167375000,"Duration":2000000,"text":{"Text":"\u4e86","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":175000000,"Duration":2250000,"text":{"Text":"\u70b9","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":177250000,"Duration":5000000,"text":{"Text":"\u8fd9\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":183250000,"Duration":2625000,"text":{"Text":"\u53ef\u4ee5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":185875000,"Duration":4250000,"text":{"Text":"\u6253\u5f00","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":190375000,"Duration":13125000,"text":{"Text":"\u6570\u636e\u5e93\u7ba1\u7406\u7cfb\u7edf","Length":7,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":206000000,"Duration":1250000,"text":{"Text":"\u6211","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":207250000,"Duration":1375000,"text":{"Text":"\u628a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":208625000,"Duration":1875000,"text":{"Text":"\u8868","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":210500000,"Duration":3375000,"text":{"Text":"\u5bfc\u5165","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":214000000,"Duration":3875000,"text":{"Text":"\u4e00\u4e0b","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":223625000,"Duration":3500000,"text":{"Text":"\u8fd9\u6837","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":227125000,"Duration":1125000,"text":{"Text":"\u5c31","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":228250000,"Duration":1375000,"text":{"Text":"\u628a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":229625000,"Duration":1750000,"text":{"Text":"\u8868","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":231625000,"Duration":3250000,"text":{"Text":"\u521b\u5efa","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":235000000,"Duration":2500000,"text":{"Text":"\u597d","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":237500000,"Duration":2250000,"text":{"Text":"\u4e86","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":243000000,"Duration":1500000,"text":{"Text":"\u628a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":244625000,"Duration":3500000,"text":{"Text":"\u4ee3\u7801","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":248125000,"Duration":1125000,"text":{"Text":"\u91cc","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":249250000,"Duration":875000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":250375000,"Duration":3375000,"text":{"Text":"\u8fde\u63a5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":253875000,"Duration":3875000,"text":{"Text":"\u4fe1\u606f","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":257750000,"Duration":3375000,"text":{"Text":"\u66f4\u65b0","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":261125000,"Duration":2250000,"text":{"Text":"\u5b8c","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":263375000,"Duration":3000000,"text":{"Text":"\u540e","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":268250000,"Duration":1500000,"text":{"Text":"\u5c31","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":269750000,"Duration":2250000,"text":{"Text":"\u53ef\u4ee5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":272000000,"Duration":5125000,"text":{"Text":"\u4e0a\u4f20","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":277375000,"Duration":4875000,"text":{"Text":"api","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":282250000,"Duration":1000000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":283375000,"Duration":4250000,"text":{"Text":"\u4ee3\u7801","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":287625000,"Duration":2125000,"text":{"Text":"\u4e86","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":292000000,"Duration":3625000,"text":{"Text":"\u5ffd\u7565","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":295875000,"Duration":3500000,"text":{"Text":"\u89c4\u5219","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":299500000,"Duration":5250000,"text":{"Text":"\u6587\u4ef6","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":305750000,"Duration":1500000,"text":{"Text":"\u548c","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":307375000,"Duration":5125000,"text":{"Text":"\u6570\u636e\u5e93","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":312625000,"Duration":4625000,"text":{"Text":"\u6587\u4ef6","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":320625000,"Duration":3625000,"text":{"Text":"\u73b0\u5728","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":324250000,"Duration":1250000,"text":{"Text":"\u5c31","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":325500000,"Duration":2625000,"text":{"Text":"\u53ef\u4ee5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":328125000,"Duration":3750000,"text":{"Text":"\u6b63\u5e38","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":331875000,"Duration":2500000,"text":{"Text":"\u4f7f\u7528","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":334500000,"Duration":4625000,"text":{"Text":"\u7f51\u7ad9","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":339125000,"Duration":1875000,"text":{"Text":"\u4e86","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":346625000,"Duration":5000000,"text":{"Text":"\u6700\u540e","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":351625000,"Duration":1625000,"text":{"Text":"\u662f","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":353375000,"Duration":3375000,"text":{"Text":"\u865a\u62df","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":356750000,"Duration":4500000,"text":{"Text":"\u4e3b\u673a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":361500000,"Duration":2625000,"text":{"Text":"\u6ce8\u610f","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":364125000,"Duration":5250000,"text":{"Text":"\u4e8b\u9879","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":375000000,"Duration":2500000,"text":{"Text":"1","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":380875000,"Duration":3625000,"text":{"Text":"\u514d\u8d39","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":384500000,"Duration":4625000,"text":{"Text":"\u8d26\u53f7","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":389375000,"Duration":2875000,"text":{"Text":"\u51fa\u4e8e","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":392375000,"Duration":3750000,"text":{"Text":"\u5b89\u5168","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":396250000,"Duration":4875000,"text":{"Text":"\u539f\u56e0","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":403625000,"Duration":1625000,"text":{"Text":"\u4f1a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":405375000,"Duration":2875000,"text":{"Text":"\u7981\u7528","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":408500000,"Duration":3625000,"text":{"Text":"\u4e0b\u9762","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":412125000,"Duration":1625000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":414000000,"Duration":5625000,"text":{"Text":"php","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":419750000,"Duration":5625000,"text":{"Text":"\u51fd\u6570","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":431000000,"Duration":2750000,"text":{"Text":"2","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":437000000,"Duration":7250000,"text":{"Text":"php","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":444500000,"Duration":4250000,"text":{"Text":"\u5185\u5b58","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":448750000,"Duration":3375000,"text":{"Text":"\u9650\u5236","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":452125000,"Duration":2000000,"text":{"Text":"\u4e3a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":454375000,"Duration":7875000,"text":{"Text":"64\u5146","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":465000000,"Duration":3250000,"text":{"Text":"\u4ed8\u8d39","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":468375000,"Duration":4500000,"text":{"Text":"\u7528\u6237","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":473125000,"Duration":1750000,"text":{"Text":"\u53ef","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":474875000,"Duration":4750000,"text":{"Text":"\u66f4\u6539","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":485250000,"Duration":3625000,"text":{"Text":"3","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":492500000,"Duration":2000000,"text":{"Text":"\u5728","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":494625000,"Duration":3250000,"text":{"Text":"\u4e3b\u673a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":497875000,"Duration":750000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":498750000,"Duration":3375000,"text":{"Text":"\u8bbe\u7f6e","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":502125000,"Duration":2000000,"text":{"Text":"\u4e2d","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":504375000,"Duration":2250000,"text":{"Text":"\u53ef\u4ee5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":506625000,"Duration":5125000,"text":{"Text":"\u770b\u5230","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":514375000,"Duration":3500000,"text":{"Text":"\u4f20\u51fa","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":518000000,"Duration":3875000,"text":{"Text":"\u8fde\u63a5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":521875000,"Duration":3375000,"text":{"Text":"\u4e3a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":526250000,"Duration":2875000,"text":{"Text":"Not","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":529250000,"Duration":4625000,"text":{"Text":"allowed","Length":7,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":536125000,"Duration":1625000,"text":{"Text":"\u8fd9","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":537750000,"Duration":4500000,"text":{"Text":"\u8868\u793a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":542500000,"Duration":3625000,"text":{"Text":"\u65e0\u6cd5","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":546125000,"Duration":4375000,"text":{"Text":"\u4f7f\u7528","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":550750000,"Duration":7125000,"text":{"Text":"php","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":558875000,"Duration":4000000,"text":{"Text":"\u8bf7\u6c42","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":563000000,"Duration":3375000,"text":{"Text":"\u5916\u90e8","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":566375000,"Duration":1625000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":568250000,"Duration":4875000,"text":{"Text":"api","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":573250000,"Duration":5250000,"text":{"Text":"\u63a5\u53e3","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":584250000,"Duration":3500000,"text":{"Text":"4","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":591250000,"Duration":3375000,"text":{"Text":"\u8d26\u6237","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":594750000,"Duration":7250000,"text":{"Text":"\u6709\u6548\u671f","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":602250000,"Duration":3000000,"text":{"Text":"\u867d\u7136","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":605375000,"Duration":3375000,"text":{"Text":"\u663e\u793a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":608875000,"Duration":5000000,"text":{"Text":"\u4e00\u5e74","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":616250000,"Duration":1500000,"text":{"Text":"\u4f46","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":617875000,"Duration":3875000,"text":{"Text":"\u5230\u671f","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":621750000,"Duration":2750000,"text":{"Text":"\u540e","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":624750000,"Duration":1500000,"text":{"Text":"\u4f1a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":626375000,"Duration":3375000,"text":{"Text":"\u81ea\u52a8","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":629750000,"Duration":3250000,"text":{"Text":"\u7eed","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":638625000,"Duration":3125000,"text":{"Text":"5","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":645125000,"Duration":4000000,"text":{"Text":"\u4e3b\u673a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":649250000,"Duration":3375000,"text":{"Text":"\u652f\u6301","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":652875000,"Duration":2000000,"text":{"Text":"1\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":655000000,"Duration":5250000,"text":{"Text":"\u57df\u540d","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":661250000,"Duration":1625000,"text":{"Text":"\u548c","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":663000000,"Duration":3250000,"text":{"Text":"3\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":666250000,"Duration":1875000,"text":{"Text":"\u5b50","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":668125000,"Duration":2500000,"text":{"Text":"\u57df","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":673750000,"Duration":5250000,"text":{"Text":"\u5e76\u4e14","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":680125000,"Duration":2250000,"text":{"Text":"\u4ed6\u4eec","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":682500000,"Duration":1125000,"text":{"Text":"\u662f","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":683875000,"Duration":3750000,"text":{"Text":"\u76f8\u4e92","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":687750000,"Duration":4250000,"text":{"Text":"\u72ec\u7acb","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":692000000,"Duration":1750000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":697375000,"Duration":3750000,"text":{"Text":"\u4ee3\u8868","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":701375000,"Duration":3750000,"text":{"Text":"\u603b\u5171","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":705250000,"Duration":1250000,"text":{"Text":"\u80fd","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":706625000,"Duration":3625000,"text":{"Text":"\u521b\u5efa","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":710500000,"Duration":2875000,"text":{"Text":"4\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":713500000,"Duration":4750000,"text":{"Text":"\u7f51\u7ad9","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":724000000,"Duration":3000000,"text":{"Text":"\u5173\u4e8e","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":727125000,"Duration":3625000,"text":{"Text":"\u6d41\u91cf","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":731000000,"Duration":1000000,"text":{"Text":"\u548c","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":732125000,"Duration":3875000,"text":{"Text":"\u7a7a\u95f4","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":736125000,"Duration":4875000,"text":{"Text":"\u95ee\u9898","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":744125000,"Duration":2750000,"text":{"Text":"\u5efa\u8bae","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":747000000,"Duration":5125000,"text":{"Text":"\u56fe\u7247","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":752375000,"Duration":3625000,"text":{"Text":"\u6258\u7ba1","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":756000000,"Duration":1250000,"text":{"Text":"\u5230","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":757375000,"Duration":5500000,"text":{"Text":"\u56fe\u5e8a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":765375000,"Duration":4625000,"text":{"Text":"\u6587\u4ef6","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":770125000,"Duration":1375000,"text":{"Text":"\u5219","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":771625000,"Duration":2875000,"text":{"Text":"\u4f7f\u7528","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":774625000,"Duration":5250000,"text":{"Text":"\u7f51\u76d8","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":782125000,"Duration":3250000,"text":{"Text":"\u8fd9\u6837","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":785625000,"Duration":1625000,"text":{"Text":"1","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":787250000,"Duration":1500000,"text":{"Text":"G","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":788875000,"Duration":5000000,"text":{"Text":"\u7a7a\u95f4","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":794125000,"Duration":1500000,"text":{"Text":"\u548c","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":795750000,"Duration":1875000,"text":{"Text":"5","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":797625000,"Duration":1750000,"text":{"Text":"G","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":799500000,"Duration":3375000,"text":{"Text":"\u6d41\u91cf","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":802875000,"Duration":625000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":803625000,"Duration":5375000,"text":{"Text":"\u914d\u989d","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":811625000,"Duration":2125000,"text":{"Text":"\u4e00\u4e2a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":813875000,"Duration":3625000,"text":{"Text":"\u5c0f\u578b","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":817625000,"Duration":4625000,"text":{"Text":"\u7f51\u7ad9","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":822500000,"Duration":4125000,"text":{"Text":"\u5b8c\u5168","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":826750000,"Duration":4375000,"text":{"Text":"\u591f\u7528","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":836875000,"Duration":4000000,"text":{"Text":"\u5173\u4e8e","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":841125000,"Duration":3750000,"text":{"Text":"\u4e09\u516d","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":844875000,"Duration":1500000,"text":{"Text":"\u4e94","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":846625000,"Duration":3750000,"text":{"Text":"\u514d\u8d39","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":850500000,"Duration":5125000,"text":{"Text":"\u4e3b\u673a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":856625000,"Duration":1500000,"text":{"Text":"\u5c31","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":858250000,"Duration":3250000,"text":{"Text":"\u4ecb\u7ecd","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":861500000,"Duration":1500000,"text":{"Text":"\u5230","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":863000000,"Duration":4500000,"text":{"Text":"\u8fd9\u91cc","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":871375000,"Duration":4750000,"text":{"Text":"\u540e\u9762","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":876125000,"Duration":1375000,"text":{"Text":"\u6211","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":877500000,"Duration":1250000,"text":{"Text":"\u4e5f","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":878750000,"Duration":1500000,"text":{"Text":"\u4f1a","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":880375000,"Duration":4125000,"text":{"Text":"\u5206\u4eab","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":884750000,"Duration":3375000,"text":{"Text":"\u66f4\u591a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":888375000,"Duration":1750000,"text":{"Text":"\u53ef","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":890125000,"Duration":3875000,"text":{"Text":"\u767d\u5ad6","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":894000000,"Duration":875000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":895000000,"Duration":4875000,"text":{"Text":"\u8d44\u6e90","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":902125000,"Duration":3500000,"text":{"Text":"\u6bd4\u5982","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":905875000,"Duration":1625000,"text":{"Text":"\u66f4","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":907500000,"Duration":2250000,"text":{"Text":"\u5feb","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":909750000,"Duration":1500000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":911500000,"Duration":5500000,"text":{"Text":"php","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":917125000,"Duration":5125000,"text":{"Text":"\u6258\u7ba1","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":925000000,"Duration":4500000,"text":{"Text":"\u652f\u6301","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":929750000,"Duration":5375000,"text":{"Text":"Python","Length":6,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":936125000,"Duration":2000000,"text":{"Text":"\u548c","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":938250000,"Duration":2500000,"text":{"Text":"Node","Length":4,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":940875000,"Duration":5125000,"text":{"Text":"js","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":946000000,"Duration":1000000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":947250000,"Duration":3125000,"text":{"Text":"\u865a\u62df","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":950375000,"Duration":5000000,"text":{"Text":"\u4e3b\u673a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":958500000,"Duration":4375000,"text":{"Text":"\u652f\u6301","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":963125000,"Duration":3625000,"text":{"Text":"\u81ea\u9009","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":966875000,"Duration":4500000,"text":{"Text":"\u670d\u52a1\u5668","Length":3,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":971500000,"Duration":3000000,"text":{"Text":"\u5730\u533a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":974500000,"Duration":1000000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":975625000,"Duration":5125000,"text":{"Text":"\u4e3b\u673a","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":983125000,"Duration":2250000,"text":{"Text":"\u4f60\u4eec","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":985375000,"Duration":875000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":986500000,"Duration":1250000,"text":{"Text":"\u4e00","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":987750000,"Duration":1750000,"text":{"Text":"\u952e","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":989625000,"Duration":2625000,"text":{"Text":"\u4e09","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":992250000,"Duration":3500000,"text":{"Text":"\u8fde","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":996750000,"Duration":1625000,"text":{"Text":"\u5c31","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":998375000,"Duration":1250000,"text":{"Text":"\u662f","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":999625000,"Duration":1375000,"text":{"Text":"\u6211","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1001000000,"Duration":3500000,"text":{"Text":"\u66f4\u65b0","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1004500000,"Duration":1000000,"text":{"Text":"\u7684","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1005625000,"Duration":4375000,"text":{"Text":"\u52a8\u529b","Length":2,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1012750000,"Duration":2250000,"text":{"Text":"\u4e0b","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1015000000,"Duration":1750000,"text":{"Text":"\u671f","Length":1,"BoundaryType":"WordBoundary"}}}]},{"Metadata":[{"Type":"WordBoundary","Data":{"Offset":1016875000,"Duration":5000000,"text":{"Text":"\u518d\u89c1","Length":2,"BoundaryType":"WordBoundary"}}}]}] -------------------------------------------------------------------------------- /src-tauri/test/复杂文本3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/test/复杂文本3.mp3 -------------------------------------------------------------------------------- /src-tauri/test/复杂文本3.txt: -------------------------------------------------------------------------------- 1 | 点开控制面板中的数据库,创建一个数据库,输入密码,选择版本,这里我选择5.7,因为我这个项目用的5版本的Mysql,选择8可能会出错,创建完成后就可以看到数据库的连接信息了。点这个可以打开数据库管理系统,我把表导入一下。这样就把表创建好了,把代码里的连接信息更新完后,就可以上传api的代码了,忽略规则文件和数据库文件,现在就可以正常使用网站了。 2 | 最后是虚拟主机注意事项。 3 | 1,免费账号出于安全原因,会禁用下面的php函数。 4 | 2,php内存限制为64兆,付费用户可更改。 5 | 3,在主机的设置中可以看到,传出连接为Not allowed,这表示无法使用php请求外部的api接口。 6 | 4,账户有效期虽然显示一年,但到期后会自动续。 7 | 5,主机支持1个域名和3个子域,并且他们是相互独立的,代表总共能创建4个网站。 8 | 关于流量和空间问题,建议图片托管到图床,文件则使用网盘,这样1G空间和5G流量的配额,一个小型网站完全够用。 9 | 关于三六五免费主机就介绍到这里,后面我也会分享更多可白嫖的资源,比如更快的php托管,支持Python和Node js的虚拟主机,支持自选服务器地区的主机,你们的一键三连就是我更新的动力,下期再见。 -------------------------------------------------------------------------------- /src-tauri/test/英文文本.json: -------------------------------------------------------------------------------- 1 | [{"Metadata":[{"Data":{"Duration":3000000,"Offset":1000000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"Got"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2875000,"Offset":4125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"it"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2125000,"Offset":10625000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"You"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1250000,"Offset":12875000,"text":{"BoundaryType":"WordBoundary","Length":4,"Text":"want"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":750000,"Offset":14250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"to"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4000000,"Offset":15125000,"text":{"BoundaryType":"WordBoundary","Length":7,"Text":"iterate"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":19250000,"text":{"BoundaryType":"WordBoundary","Length":7,"Text":"through"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":750000,"Offset":20875000,"text":{"BoundaryType":"WordBoundary","Length":1,"Text":"a"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5500000,"Offset":21750000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"PHP"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4750000,"Offset":27375000,"text":{"BoundaryType":"WordBoundary","Length":5,"Text":"array"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":7750000,"Offset":33875000,"text":{"BoundaryType":"WordBoundary","Length":8,"Text":"identify"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1625000,"Offset":41875000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"and"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4750000,"Offset":43625000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"remove"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4375000,"Offset":49000000,"text":{"BoundaryType":"WordBoundary","Length":8,"Text":"elements"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":53625000,"text":{"BoundaryType":"WordBoundary","Length":5,"Text":"where"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1000000,"Offset":55250000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"the"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5500000,"Offset":56375000,"text":{"BoundaryType":"WordBoundary","Length":5,"Text":"value"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":62125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"is"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":63750000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"an"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":3125000,"Offset":66125000,"text":{"BoundaryType":"WordBoundary","Length":5,"Text":"empty"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4750000,"Offset":69375000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"string"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4625000,"Offset":77125000,"text":{"BoundaryType":"WordBoundary","Length":9,"Text":"including"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":4750000,"Offset":81875000,"text":{"BoundaryType":"WordBoundary","Length":7,"Text":"strings"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5375000,"Offset":86750000,"text":{"BoundaryType":"WordBoundary","Length":8,"Text":"composed"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":750000,"Offset":92250000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"of"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5125000,"Offset":93125000,"text":{"BoundaryType":"WordBoundary","Length":8,"Text":"multiple"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6625000,"Offset":98375000,"text":{"BoundaryType":"WordBoundary","Length":6,"Text":"spaces"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":1500000,"Offset":105125000,"text":{"BoundaryType":"WordBoundary","Length":2,"Text":"or"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":7000000,"Offset":106750000,"text":{"BoundaryType":"WordBoundary","Length":8,"Text":"newlines"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2000000,"Offset":117500000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"and"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":2375000,"Offset":119625000,"text":{"BoundaryType":"WordBoundary","Length":4,"Text":"then"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":6000000,"Offset":122125000,"text":{"BoundaryType":"WordBoundary","Length":8,"Text":"re-index"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":875000,"Offset":128250000,"text":{"BoundaryType":"WordBoundary","Length":3,"Text":"the"}},"Type":"WordBoundary"}]},{"Metadata":[{"Data":{"Duration":5000000,"Offset":129250000,"text":{"BoundaryType":"WordBoundary","Length":5,"Text":"array"}},"Type":"WordBoundary"}]}] -------------------------------------------------------------------------------- /src-tauri/test/英文文本.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src-tauri/test/英文文本.mp3 -------------------------------------------------------------------------------- /src-tauri/test/英文文本.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:00,100 --> 00:00:00,700 3 | Got it 4 | 5 | 2 6 | 00:00:01,062 --> 00:00:03,212 7 | You want to iterate through a PHP array 8 | 9 | 3 10 | 00:00:03,387 --> 00:00:07,412 11 | identify and remove elements where the value is an empty string 12 | 13 | 4 14 | 00:00:07,712 --> 00:00:11,375 15 | including strings composed of multiple spaces or newlines 16 | 17 | 5 18 | 00:00:11,750 --> 00:00:12,812 19 | and thenre-index 20 | 21 | 6 22 | 00:00:12,825 --> 00:00:13,425 23 | the array 24 | -------------------------------------------------------------------------------- /src-tauri/test/英文文本.txt: -------------------------------------------------------------------------------- 1 | Got it! You want to iterate through a PHP array, identify and remove elements where the value is an empty string (including strings composed of multiple spaces or newlines), and then re-index the array. -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 107 | 108 | -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ev-c-white: #ffffff; 3 | --ev-c-white-soft: #f8f8f8; 4 | --ev-c-white-mute: #f2f2f2; 5 | 6 | --ev-c-black: #1b1b1f; 7 | --ev-c-black-soft: #222222; 8 | --ev-c-black-mute: #282828; 9 | 10 | --ev-c-gray-1: #515c67; 11 | --ev-c-gray-2: #414853; 12 | --ev-c-gray-3: #32363f; 13 | 14 | --ev-c-text-1: rgba(255, 255, 245, 0.86); 15 | --ev-c-text-2: rgba(235, 235, 245, 0.6); 16 | --ev-c-text-3: rgba(235, 235, 245, 0.38); 17 | 18 | --ev-button-alt-border: transparent; 19 | --ev-button-alt-text: var(--ev-c-text-1); 20 | --ev-button-alt-bg: var(--ev-c-gray-3); 21 | --ev-button-alt-hover-border: transparent; 22 | --ev-button-alt-hover-text: var(--ev-c-text-1); 23 | --ev-button-alt-hover-bg: var(--ev-c-gray-2); 24 | } 25 | 26 | :root { 27 | --color-background: var(--ev-c-black); 28 | --color-background-soft: var(--ev-c-black-soft); 29 | --color-background-mute: var(--ev-c-black-mute); 30 | 31 | --color-text: var(--ev-c-text-1); 32 | } 33 | 34 | *, 35 | *::before, 36 | *::after { 37 | box-sizing: border-box; 38 | margin: 0; 39 | font-weight: normal; 40 | } 41 | 42 | ul { 43 | list-style: none; 44 | } 45 | 46 | body { 47 | height: 100%;; 48 | line-height: 1.6; 49 | font-family: 50 | Inter, 51 | -apple-system, 52 | BlinkMacSystemFont, 53 | 'Segoe UI', 54 | Roboto, 55 | Oxygen, 56 | Ubuntu, 57 | Cantarell, 58 | 'Fira Sans', 59 | 'Droid Sans', 60 | 'Helvetica Neue', 61 | sans-serif; 62 | text-rendering: optimizeLegibility; 63 | -webkit-font-smoothing: antialiased; 64 | -moz-osx-font-smoothing: grayscale; 65 | } 66 | 67 | html{ 68 | height: 100%; 69 | } 70 | 71 | #app{ 72 | height: 100%; 73 | } -------------------------------------------------------------------------------- /src/assets/gitee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zs1083339604/tts-tauri/559be104806e6d0cb77e9c34715643aff71cefe5/src/assets/gitee.png -------------------------------------------------------------------------------- /src/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | // biome-ignore lint: disable 7 | export {} 8 | declare global { 9 | const EffectScope: typeof import('vue')['EffectScope'] 10 | const computed: typeof import('vue')['computed'] 11 | const createApp: typeof import('vue')['createApp'] 12 | const customRef: typeof import('vue')['customRef'] 13 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 14 | const defineComponent: typeof import('vue')['defineComponent'] 15 | const effectScope: typeof import('vue')['effectScope'] 16 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 17 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 18 | const h: typeof import('vue')['h'] 19 | const inject: typeof import('vue')['inject'] 20 | const isProxy: typeof import('vue')['isProxy'] 21 | const isReactive: typeof import('vue')['isReactive'] 22 | const isReadonly: typeof import('vue')['isReadonly'] 23 | const isRef: typeof import('vue')['isRef'] 24 | const markRaw: typeof import('vue')['markRaw'] 25 | const nextTick: typeof import('vue')['nextTick'] 26 | const onActivated: typeof import('vue')['onActivated'] 27 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 28 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 29 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 30 | const onDeactivated: typeof import('vue')['onDeactivated'] 31 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 32 | const onMounted: typeof import('vue')['onMounted'] 33 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 34 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 35 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 36 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 37 | const onUnmounted: typeof import('vue')['onUnmounted'] 38 | const onUpdated: typeof import('vue')['onUpdated'] 39 | const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] 40 | const provide: typeof import('vue')['provide'] 41 | const reactive: typeof import('vue')['reactive'] 42 | const readonly: typeof import('vue')['readonly'] 43 | const ref: typeof import('vue')['ref'] 44 | const resolveComponent: typeof import('vue')['resolveComponent'] 45 | const shallowReactive: typeof import('vue')['shallowReactive'] 46 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 47 | const shallowRef: typeof import('vue')['shallowRef'] 48 | const toRaw: typeof import('vue')['toRaw'] 49 | const toRef: typeof import('vue')['toRef'] 50 | const toRefs: typeof import('vue')['toRefs'] 51 | const toValue: typeof import('vue')['toValue'] 52 | const triggerRef: typeof import('vue')['triggerRef'] 53 | const unref: typeof import('vue')['unref'] 54 | const useAttrs: typeof import('vue')['useAttrs'] 55 | const useCssModule: typeof import('vue')['useCssModule'] 56 | const useCssVars: typeof import('vue')['useCssVars'] 57 | const useId: typeof import('vue')['useId'] 58 | const useModel: typeof import('vue')['useModel'] 59 | const useSlots: typeof import('vue')['useSlots'] 60 | const useTemplateRef: typeof import('vue')['useTemplateRef'] 61 | const watch: typeof import('vue')['watch'] 62 | const watchEffect: typeof import('vue')['watchEffect'] 63 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 64 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 65 | } 66 | // for type re-export 67 | declare global { 68 | // @ts-ignore 69 | export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' 70 | import('vue') 71 | } 72 | -------------------------------------------------------------------------------- /src/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // @ts-nocheck 3 | // Generated by unplugin-vue-components 4 | // Read more: https://github.com/vuejs/core/pull/3399 5 | // biome-ignore lint: disable 6 | export {} 7 | 8 | /* prettier-ignore */ 9 | declare module 'vue' { 10 | export interface GlobalComponents { 11 | ElButton: typeof import('element-plus/es')['ElButton'] 12 | ElForm: typeof import('element-plus/es')['ElForm'] 13 | ElFormItem: typeof import('element-plus/es')['ElFormItem'] 14 | ElIcon: typeof import('element-plus/es')['ElIcon'] 15 | ElInput: typeof import('element-plus/es')['ElInput'] 16 | ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] 17 | ElMenu: typeof import('element-plus/es')['ElMenu'] 18 | ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] 19 | ElOption: typeof import('element-plus/es')['ElOption'] 20 | ElRadio: typeof import('element-plus/es')['ElRadio'] 21 | ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] 22 | ElSelect: typeof import('element-plus/es')['ElSelect'] 23 | ElSlider: typeof import('element-plus/es')['ElSlider'] 24 | ElSwitch: typeof import('element-plus/es')['ElSwitch'] 25 | RouterLink: typeof import('vue-router')['RouterLink'] 26 | RouterView: typeof import('vue-router')['RouterView'] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import ElementPlus from 'element-plus' 4 | import 'element-plus/dist/index.css' 5 | import { createPinia } from 'pinia' 6 | import router from './router' 7 | 8 | const pinia = createPinia(); 9 | const app = createApp(App); 10 | app.use(router); 11 | app.use(ElementPlus); 12 | app.use(pinia); 13 | app.mount('#app'); -------------------------------------------------------------------------------- /src/pages/SetPage.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 86 | 87 | -------------------------------------------------------------------------------- /src/pages/TTS.vue: -------------------------------------------------------------------------------- 1 | 238 | 239 | 323 | 324 | 367 | 368 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHistory} from 'vue-router' 2 | 3 | import TTS from '../pages/TTS.vue' 4 | import SetPage from '../pages/SetPage.vue' 5 | 6 | const routes = [ 7 | { path: '/', redirect: '/tts'}, 8 | { path: '/tts', component: TTS }, 9 | { path: '/set', component: SetPage }, 10 | ] 11 | 12 | const router = createRouter({ 13 | history: createWebHistory(), 14 | routes, 15 | }) 16 | 17 | export default router; -------------------------------------------------------------------------------- /src/store/option.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { invoke } from "@tauri-apps/api/core"; 3 | import { select, update } from "../utils/sqlite"; 4 | import mitter from "../utils/mitt" 5 | 6 | export const useOptionStore = defineStore("option", { 7 | actions: { 8 | getExePath(){ 9 | return new Promise(async (resolve, reject) => { 10 | try { 11 | const result = await invoke("get_exe_path"); 12 | if(result.code == 200){ 13 | this.app_exe_path = result.data.path.replaceAll("\\", "/"); 14 | if(this.app_exe_path.endsWith("/")){ 15 | this.app_exe_path = this.app_exe_path.slice(0, -1); 16 | } 17 | 18 | resolve(); 19 | }else{ 20 | reject(result.msg); 21 | } 22 | } catch (error) { 23 | reject(error) 24 | } 25 | }) 26 | }, 27 | getOption(){ 28 | return new Promise((resolve, reject) => { 29 | select("option", ["softOption", "ttsOption"], "id = ?", [1]).then((result) => { 30 | if(result.rows[0].ttsOption == ""){ 31 | // 使用默认值 32 | this.ttsOption = { 33 | language: "中文(大陆)", 34 | voice: "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoxiaoNeural)", 35 | pitch: 0, 36 | rate: 0, 37 | volume: 0, 38 | gender_sub_marker: true, 39 | sub_marker_type: "mergeByPunctuation", 40 | merge_by_number_number: 1 41 | } 42 | }else{ 43 | this.ttsOption = JSON.parse(result.rows[0].ttsOption); 44 | } 45 | 46 | if(result.rows[0].softOption == ""){ 47 | // 使用默认值 48 | this.softOption = { 49 | savePath: this.app_exe_path + "/output", 50 | openFolders: true, 51 | saveTTSOptions: true 52 | } 53 | }else{ 54 | this.softOption = JSON.parse(result.rows[0].softOption); 55 | } 56 | resolve(); 57 | }).catch((error)=>{ 58 | reject(error); 59 | }) 60 | }) 61 | }, 62 | updataTTSOption(data){ 63 | return new Promise((resolve, reject) => { 64 | const obj = {...JSON.parse(JSON.stringify(data)), text: ""}; 65 | update("option", {ttsOption: JSON.stringify(obj)}, "id = ?", [1]).then((result) => { 66 | // 深拷贝对象,上次写代码直接用toRaw就出Bug了 67 | this.ttsOption = obj; 68 | resolve(); 69 | }).catch((error)=>{ 70 | reject(error); 71 | }) 72 | }); 73 | }, 74 | updataSoftOption(data){ 75 | return new Promise((resolve, reject) => { 76 | const newObjString = JSON.stringify(data); 77 | 78 | update("option", {softOption: newObjString}, "id = ?", [1]).then((result) => { 79 | this.softOption = JSON.parse(newObjString); 80 | resolve(); 81 | }).catch((error)=>{ 82 | reject(error); 83 | }) 84 | }); 85 | } 86 | }, 87 | state() { 88 | return{ 89 | app_exe_path: "", 90 | softOption: null, 91 | ttsOption: null 92 | } 93 | }, 94 | }) 95 | -------------------------------------------------------------------------------- /src/store/voices.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { invoke } from "@tauri-apps/api/core"; 3 | import { select, update } from "../utils/sqlite"; 4 | import mitter from "../utils/mitt" 5 | 6 | export const useVoicesStore = defineStore("voices", { 7 | actions: { 8 | getVoicesList(){ 9 | return new Promise((resolve, reject) => { 10 | select("option", ["voices"], "id = ?", [1]).then(async (result) => { 11 | if(result.rows[0]["voices"] == ""){ 12 | try { 13 | // 从网络更新 14 | const result = await this.getVoicesListRemove(); 15 | if(result.code == 200){ 16 | // 更新数据 17 | await update("option", {voices: result.data.voices}, "id = ?", [1]); 18 | resolve(); 19 | }else{ 20 | reject(result.msg); 21 | } 22 | 23 | } catch (error) { 24 | reject(error); 25 | } 26 | }else{ 27 | // 已经有数据了 28 | this.voices = JSON.parse(result.rows[0]["voices"]); 29 | resolve(); 30 | } 31 | }).catch((error) => { 32 | reject(error); 33 | }) 34 | }) 35 | }, 36 | 37 | async getVoicesListRemove() { 38 | mitter.emit("updataVoices"); 39 | try { 40 | const result = await invoke("get_voices_list"); 41 | 42 | if(result.code == 200){ 43 | this.voices = {}; 44 | 45 | // 定义一个映射对象,将 Locale 转换为语言归属地 46 | const localeToLanguage = { 47 | 'af-ZA': '南非语', 48 | 'am-ET': '阿姆哈拉语(埃塞俄比亚)', 49 | 'ar-EG': '阿拉伯语 (埃及)', 50 | 'ar-AE': '阿拉伯语(阿联酋)', 51 | 'ar-BH': '阿拉伯语(巴林)', 52 | 'ar-DZ': '阿拉伯语(阿尔及利亚)', 53 | 'ar-SA': '阿拉伯语(沙特阿拉伯)', 54 | 'ar-IQ': '阿拉伯语(伊拉克)', 55 | 'ar-JO': '阿拉伯语(约旦)', 56 | 'ar-KW': '阿拉伯语(科威特)', 57 | 'ar-LB': '阿拉伯语(黎巴嫩)', 58 | 'ar-LY': '阿拉伯语(利比亚)', 59 | 'ar-MA': '阿拉伯语(摩洛哥)', 60 | 'ar-OM': '阿拉伯语(阿曼)', 61 | 'ar-QA': '阿拉伯语(卡塔尔)', 62 | 'ar-SY': '阿拉伯语(叙利亚)', 63 | 'ar-TN': '阿拉伯语(突尼斯)', 64 | 'az-AZ': '阿塞拜疆语', 65 | 'be-BY': '白俄罗斯语', 66 | 'bg-BG': '保加利亚语', 67 | 'bn-BD': '孟加拉语(孟加拉)', 68 | 'bs-BA': '波斯尼亚语', 69 | 'ca-ES': '加泰罗尼亚语', 70 | 'cs-CZ': '捷克语', 71 | 'cy-GB': '威尔士语', 72 | 'da-DK': '丹麦语', 73 | 'de-DE': '德语(德国)', 74 | 'de-AT': '德语(奥地利)', 75 | 'de-CH': '德语(瑞士)', 76 | 'el-GR': '希腊语', 77 | 'en-US': '英语(美国)', 78 | 'en-GB': '英语(英国)', 79 | 'en-AU': '英语(澳大利亚)', 80 | 'en-CA': '英语(加拿大)', 81 | 'en-IN': '英语(印度)', 82 | 'en-IE': '英语(爱尔兰)', 83 | 'en-NZ': '英语(新西兰)', 84 | 'en-ZA': '英语(南非)', 85 | 'es-ES': '西班牙语(西班牙)', 86 | 'es-MX': '西班牙语(墨西哥)', 87 | 'es-AR': '西班牙语(阿根廷)', 88 | 'es-CO': '西班牙语(哥伦比亚)', 89 | 'es-CL': '西班牙语(智利)', 90 | 'es-PE': '西班牙语(秘鲁)', 91 | 'et-EE': '爱沙尼亚语', 92 | 'eu-ES': '巴斯克语', 93 | 'fa-IR': '波斯语(伊朗)', 94 | 'fi-FI': '芬兰语', 95 | 'fil-PH': '菲律宾语', 96 | 'fr-FR': '法语(法国)', 97 | 'fr-CA': '法语(加拿大)', 98 | 'fr-CH': '法语(瑞士)', 99 | 'fr-BE': '法语(比利时)', 100 | 'gl-ES': '加利西亚语', 101 | 'gu-IN': '古吉拉特语', 102 | 'he-IL': '希伯来语', 103 | 'hi-IN': '印地语', 104 | 'hr-HR': '克罗地亚语', 105 | 'hu-HU': '匈牙利语', 106 | 'hy-AM': '亚美尼亚语', 107 | 'id-ID': '印尼语', 108 | 'is-IS': '冰岛语', 109 | 'it-IT': '意大利语', 110 | 'ja-JP': '日语', 111 | 'jv-ID': '爪哇语', 112 | 'ka-GE': '格鲁吉亚语', 113 | 'kk-KZ': '哈萨克语', 114 | 'km-KH': '高棉语', 115 | 'kn-IN': '卡纳达语', 116 | 'ko-KR': '韩语', 117 | 'lo-LA': '老挝语', 118 | 'lt-LT': '立陶宛语', 119 | 'lv-LV': '拉脱维亚语', 120 | 'mk-MK': '马其顿语', 121 | 'ml-IN': '马拉雅拉姆语', 122 | 'mn-MN': '蒙古语', 123 | 'mr-IN': '马拉地语', 124 | 'ms-MY': '马来语(马来西亚)', 125 | 'ms-SG': '马来语(新加坡)', 126 | 'my-MM': '缅甸语', 127 | 'ne-NP': '尼泊尔语', 128 | 'nl-NL': '荷兰语(荷兰)', 129 | 'nl-BE': '荷兰语(比利时)', 130 | 'no-NO': '挪威语', 131 | 'pa-IN': '旁遮普语', 132 | 'pl-PL': '波兰语', 133 | 'pt-PT': '葡萄牙语(葡萄牙)', 134 | 'pt-BR': '葡萄牙语(巴西)', 135 | 'ro-RO': '罗马尼亚语', 136 | 'ru-RU': '俄语', 137 | 'si-LK': '僧伽罗语', 138 | 'sk-SK': '斯洛伐克语', 139 | 'sl-SI': '斯洛文尼亚语', 140 | 'sq-AL': '阿尔巴尼亚语', 141 | 'sr-RS': '塞尔维亚语', 142 | 'su-ID': '巽他语', 143 | 'sv-SE': '瑞典语', 144 | 'sw-KE': '斯瓦希里语(肯尼亚)', 145 | 'ta-IN': '泰米尔语(印度)', 146 | 'ta-SG': '泰米尔语(新加坡)', 147 | 'te-IN': '泰卢固语', 148 | 'th-TH': '泰语', 149 | 'tr-TR': '土耳其语', 150 | 'uk-UA': '乌克兰语', 151 | 'ur-PK': '乌尔都语', 152 | 'uz-UZ': '乌兹别克语', 153 | 'vi-VN': '越南语', 154 | 'zh-CN': '中文(大陆)', 155 | 'zh-HK': '中文(香港)', 156 | 'zh-TW': '中文(台湾)', 157 | 'zh-SG': '中文(新加坡)', 158 | 'zh-MO': '中文(澳门)', 159 | 'zu-ZA': '祖鲁语', 160 | 'ar-YE': '阿拉伯语(也门)', 161 | 'bn-IN': '孟加拉语(印度)', 162 | 'en-HK': '英语(香港)', 163 | 'en-SG': '英语(新加坡)', 164 | 'en-KE': '英语(肯尼亚)', 165 | 'en-NG': '英语(尼日利亚)', 166 | 'en-PH': '英语(菲律宾)', 167 | 'en-TZ': '英语(坦桑尼亚)', 168 | 'es-BO': '西班牙语(玻利维亚)', 169 | 'es-CR': '西班牙语(哥斯达黎加)', 170 | 'es-CU': '西班牙语(古巴)', 171 | 'es-DO': '西班牙语(多米尼加共和国)', 172 | 'es-EC': '西班牙语(厄瓜多尔)', 173 | 'es-GQ': '西班牙语(赤道几内亚)', 174 | 'es-GT': '西班牙语(危地马拉)', 175 | 'es-HN': '西班牙语(洪都拉斯)', 176 | 'es-NI': '西班牙语(尼加拉瓜)', 177 | 'es-PA': '西班牙语(巴拿马)', 178 | 'es-PR': '西班牙语(波多黎各)', 179 | 'es-PY': '西班牙语(巴拉圭)', 180 | 'es-SV': '西班牙语(萨尔瓦多)', 181 | 'es-US': '西班牙语(美国)', 182 | 'es-UY': '西班牙语(乌拉圭)', 183 | 'es-VE': '西班牙语(委内瑞拉)', 184 | 'ga-IE': '爱尔兰语', 185 | 'iu-Cans-CA': '因纽特语(加拿大)', 186 | 'iu-Latn-CA': '因纽特语(加拿大)', 187 | 'mt-MT': '马耳他语', 188 | 'nb-NO': '挪威语(伯克梅尔)', 189 | 'ps-AF': '普什图语', 190 | 'so-SO': '索马里语', 191 | 'sw-TZ': '斯瓦希里语(坦桑尼亚)', 192 | 'ta-LK': '泰米尔语(斯里兰卡)', 193 | 'ta-MY': '泰米尔语(马来西亚)', 194 | 'ur-IN': '乌尔都语(印度)', 195 | 'zh-CN-liaoning': '中文(辽宁)', 196 | 'zh-CN-shaanxi': '中文(陕西)', 197 | }; 198 | 199 | // 映射性别 200 | const genderMap = { 201 | 'Female': '女', 202 | 'Male': '男' 203 | }; 204 | 205 | // 遍历 voices 数组 206 | result.data.forEach(voice => { 207 | const locale = voice.Locale; 208 | const language = localeToLanguage[locale] || locale; 209 | voice["Gender"] = genderMap[voice["Gender"]]; 210 | 211 | // 如果分类结果中还没有这个语言分类,就创建一个空数组 212 | if (!this.voices[language]) { 213 | this.voices[language] = []; 214 | } 215 | 216 | // 将当前 voice 对象添加到对应的语言分类中 217 | this.voices[language].push({ 218 | FriendlyName: voice.FriendlyName, 219 | Gender: voice.Gender, 220 | Name: voice.Name, 221 | simpleName: voice.FriendlyName.split(' ')[1] 222 | }); 223 | }); 224 | mitter.emit("updataVoicesed"); 225 | return {code: 200, msg: "成功", data: { voices: JSON.stringify(this.voices) }}; 226 | }else{ 227 | mitter.emit("updataVoicesed"); 228 | return result; 229 | } 230 | } catch (error) { 231 | mitter.emit("updataVoicesed"); 232 | return error; 233 | } 234 | }, 235 | 236 | updataVoices(){ 237 | return new Promise(async (resolve, reject) => { 238 | try { 239 | const result = await this.getVoicesListRemove(); 240 | if(result.code == 200){ 241 | // 成功 242 | await update("option", {voices: result.data.voices}, "id = ?", [1]); 243 | resolve(); 244 | }else{ 245 | reject(result.msg); 246 | } 247 | 248 | } catch (error) { 249 | reject(error); 250 | } 251 | }); 252 | } 253 | }, 254 | state() { 255 | return{ 256 | voices: {} 257 | } 258 | }, 259 | }) 260 | -------------------------------------------------------------------------------- /src/utils/function.js: -------------------------------------------------------------------------------- 1 | import { ElMessage } from "element-plus"; 2 | 3 | function show_error(error){ 4 | let error_str = typeof error == 'string' ? error : ""; 5 | 6 | if(error_str == ""){ 7 | // error是一个对象,判断是否有msg或message属性 8 | if(error.msg){ 9 | error_str = error.msg; 10 | }else if(error.message){ 11 | error_str = error.message; 12 | } 13 | } 14 | 15 | ElMessage.error({ 16 | dangerouslyUseHTMLString: true, 17 | message: error_str, 18 | }); 19 | } 20 | 21 | export { 22 | show_error 23 | } -------------------------------------------------------------------------------- /src/utils/mitt.js: -------------------------------------------------------------------------------- 1 | import mitt from "mitt"; 2 | 3 | const mitter = mitt(); 4 | 5 | export default mitter; -------------------------------------------------------------------------------- /src/utils/sqlite.js: -------------------------------------------------------------------------------- 1 | import Database from '@tauri-apps/plugin-sql'; 2 | 3 | let db = null; 4 | let isConnect = false; 5 | 6 | // 基础的数据表 7 | const databseTable = [ 8 | { 9 | name: 'option', 10 | sql: `CREATE TABLE option( 11 | id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 12 | voices TEXT DEFAULT '', 13 | ttsOption TEXT DEFAULT '', 14 | softOption TEXT DEFAULT '' 15 | ); 16 | ` 17 | } 18 | ] 19 | 20 | function connect(){ 21 | return new Promise(async (resolve, reject) => { 22 | if(isConnect){ 23 | resolve(); 24 | }else{ 25 | try { 26 | db = await Database.load('sqlite:database.db'); 27 | // 初始化数据库 28 | for (let i = 0; i < databseTable.length; i++) { 29 | // 初始化数据库,如果出错,则立即中止,并退出程序 30 | await init(databseTable[i].name, databseTable[i].sql).catch((err) => { 31 | reject(err); 32 | return; 33 | }); 34 | } 35 | // 初始化成功,连接成功 36 | isConnect = true; 37 | resolve(); 38 | 39 | } catch (error) { 40 | reject(error); 41 | } 42 | } 43 | }) 44 | } 45 | 46 | // 数据库初始化 47 | function init(name, sql) { 48 | return new Promise(async function (resolve, reject) { 49 | try { 50 | const result = await db.select("SELECT name FROM sqlite_master WHERE type='table' AND name=$1", [name]); 51 | if(result.length == 0){ 52 | // 如果表不存在,则创建表 53 | await db.execute(sql); 54 | if(name == "option"){ 55 | // 创建第一条数据,Option表必须有一条数据 56 | await insert("option", ["voices", "ttsOption", "softOption"], ["", "", ""]); 57 | } 58 | } 59 | resolve(); 60 | } catch (error) { 61 | reject(error); 62 | } 63 | }); 64 | } 65 | 66 | 67 | function disConnect(){ 68 | return new Promise(async (resolve, reject) => { 69 | try { 70 | await db.close(); 71 | 72 | db = null; 73 | isConnect = false; 74 | resolve(); 75 | } catch (error) { 76 | reject(error); 77 | } 78 | }) 79 | } 80 | 81 | // 处理条件中的? 82 | function placeholderEscape(whereClause, valuesLength){ 83 | let processedWhereClause = whereClause; 84 | let sql = ""; 85 | if (whereClause) { 86 | const wherePlaceholders = whereClause.match(/\?/g); 87 | if (wherePlaceholders) { 88 | wherePlaceholders.forEach((_, index) => { 89 | processedWhereClause = processedWhereClause.replace('?', `\$${valuesLength + index + 1}`); 90 | }); 91 | } 92 | sql += ` WHERE ${processedWhereClause}`; 93 | } 94 | 95 | return sql; 96 | } 97 | 98 | /** 99 | * 插入数据的通用函数 100 | * @param {string} tableName - 表名 101 | * @param {string[]} columns - 列名数组 102 | * @param {any[]} values - 值数组 103 | * @example insert("fingerprint", ["fingerprint", "last_ip", "last_time"], ["1", "2", "3"]).then((data)=>console.log(data)).catch(); 104 | */ 105 | function insert(tableName, columns, values){ 106 | return new Promise(async (resolve, reject) => { 107 | // 动态生成列名和占位符 108 | const columnNames = columns.join(', '); 109 | // 占位符为 $1, $2, $3... 110 | const placeholders = columns.map((_, index) => `$${index + 1}`).join(', '); 111 | 112 | const sql = `INSERT INTO ${tableName} (${columnNames}) VALUES (${placeholders})`; 113 | 114 | try { 115 | const result = await db.execute(sql, values); 116 | resolve({ 117 | sql, 118 | changes: result.rowsAffected, 119 | lastId: result.lastInsertId 120 | }) 121 | } catch (error) { 122 | reject(error); 123 | } 124 | }) 125 | } 126 | 127 | /** 128 | * 更新数据的通用函数 129 | * @param {string} tableName - 表名 130 | * @param {object} data - 要更新的列和值的对象 131 | * @param {string} whereClause - 可选的 WHERE 条件 132 | * @param {any[]} whereArgs - 可选的 WHERE 条件参数数组 133 | * @example update("fingerprint", {fingerprint: "4", last_ip: "5", last_time: "6"}, "id = ?", [4]).then((data)=>console.log(data)).catch((error)=>console.log(error)); 134 | */ 135 | function update(tableName, data, whereClause = '', whereArgs = []) { 136 | return new Promise(async (resolve, reject) => { 137 | // 提取列和值 138 | const columns = Object.keys(data); 139 | const values = Object.values(data); 140 | // 动态生成 SET 子句,使用 $1, $2, ... 占位符 141 | const setClause = columns.map((col, index) => `${col} = $${index + 1}`).join(', '); 142 | let sql = `UPDATE ${tableName} SET ${setClause}`; 143 | // 处理 WHERE 子句中的占位符 144 | sql += placeholderEscape(whereClause, values.length); 145 | // 合并更新的值和 WHERE 子句的参数 146 | const allValues = [...values, ...whereArgs]; 147 | // 执行 SQL 148 | try { 149 | const result = await db.execute(sql, allValues); 150 | resolve({ 151 | sql, 152 | changes: result.rowsAffected 153 | }) 154 | } catch (error) { 155 | reject(error); 156 | } 157 | }); 158 | } 159 | 160 | /** 161 | * 通用查询函数 162 | * @param {string} tableName - 表名 163 | * @param {string[]} columns - 要查询的列名数组,传 `*` 查询所有列 164 | * @param {string} whereClause - 可选的 WHERE 条件 165 | * @param {any[]} whereArgs - 可选的 WHERE 条件参数数组 166 | * @example select("fingerprint", ['*'], 'id = ? OR id = ?', [1,2]).then((data)=>console.log(data)).catch((error)=>console.log(error)); 167 | */ 168 | function select(tableName, columns, whereClause = '', whereArgs = []) { 169 | return new Promise(async (resolve, reject) => { 170 | // 动态生成查询语句 171 | const columnList = columns.length > 0 ? columns.join(', ') : '*'; 172 | let sql = `SELECT ${columnList} FROM ${tableName}`; 173 | if(whereClause){ 174 | sql += placeholderEscape(whereClause, 0); 175 | } 176 | 177 | // 执行查询 178 | try { 179 | const result = await db.select(sql, whereArgs); 180 | resolve({ 181 | sql, 182 | rows: result 183 | }); 184 | } catch (error) { 185 | reject(error); 186 | } 187 | }); 188 | } 189 | 190 | /** 191 | * 通用删除函数 192 | * @param {string} tableName - 表名 193 | * @param {string} whereClause - 可选的 WHERE 条件 194 | * @param {any[]} whereArgs - 可选的 WHERE 条件参数数组 195 | * @example deleteData("fingerprint", "id = ?", [1]).then((data)=>console.log(data)).catch((error)=>console.log(error)); 196 | */ 197 | function deleteData(tableName, whereClause = '', whereArgs = []) { 198 | return new Promise(async (resolve, reject) => { 199 | // 动态生成删除语句 200 | let sql = `DELETE FROM ${tableName}`; 201 | if (whereClause) { 202 | sql += placeholderEscape(whereClause, 0); 203 | } 204 | 205 | // 执行删除操作 206 | try { 207 | const result = await db.execute(sql, whereArgs); 208 | resolve({ 209 | sql, 210 | changes: result.rowsAffected 211 | }); 212 | } catch (error) { 213 | reject(error); 214 | } 215 | }); 216 | } 217 | 218 | /** 219 | * 自定义查询函数,自己写SQL和条件 220 | * @param {string} sql - SQL语句 221 | * @param {any[]} whereArgs - 可选的 WHERE 条件参数数组 222 | */ 223 | function selectCustom(sql, whereArgs) { 224 | return new Promise(async (resolve, reject) => { 225 | // 执行查询 226 | try { 227 | const result = await db.select(sql, whereArgs); 228 | resolve({ 229 | sql, 230 | rows: result 231 | }); 232 | } catch (error) { 233 | reject(error); 234 | } 235 | }); 236 | } 237 | 238 | export { 239 | connect, disConnect, insert, update, select, deleteData, selectCustom 240 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import AutoImport from 'unplugin-auto-import/vite' 4 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 5 | import IconsResolver from 'unplugin-icons/resolver' 6 | import Icons from 'unplugin-icons/vite' 7 | import path from 'path' 8 | import Components from 'unplugin-vue-components/vite' 9 | 10 | const host = process.env.TAURI_DEV_HOST; 11 | const pathSrc = path.resolve(__dirname, 'src') 12 | 13 | // https://vitejs.dev/config/ 14 | export default defineConfig(async () => ({ 15 | plugins: [vue(), 16 | AutoImport({ 17 | // Auto import functions from Vue, e.g. ref, reactive, toRef... 18 | // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等 19 | imports: ['vue'], 20 | 21 | // Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style) 22 | // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式) 23 | resolvers: [ 24 | ElementPlusResolver(), 25 | 26 | // Auto import icon components 27 | // 自动导入图标组件 28 | IconsResolver({ 29 | prefix: 'Icon', 30 | }), 31 | ], 32 | 33 | dts: path.resolve(pathSrc, 'auto-imports.d.ts'), 34 | }), 35 | 36 | Components({ 37 | resolvers: [ 38 | // Auto register icon components 39 | // 自动注册图标组件 40 | IconsResolver({ 41 | enabledCollections: ['ep'], 42 | }), 43 | // Auto register Element Plus components 44 | // 自动导入 Element Plus 组件 45 | ElementPlusResolver(), 46 | ], 47 | 48 | dts: path.resolve(pathSrc, 'components.d.ts'), 49 | }), 50 | 51 | Icons({ 52 | autoInstall: true, 53 | }) 54 | ], 55 | 56 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 57 | // 58 | // 1. prevent vite from obscuring rust errors 59 | clearScreen: false, 60 | // 2. tauri expects a fixed port, fail if that port is not available 61 | server: { 62 | port: 1420, 63 | strictPort: true, 64 | host: host || false, 65 | hmr: host 66 | ? { 67 | protocol: "ws", 68 | host, 69 | port: 1421, 70 | } 71 | : undefined, 72 | watch: { 73 | // 3. tell vite to ignore watching `src-tauri` 74 | ignored: ["**/src-tauri/**"], 75 | }, 76 | }, 77 | })); 78 | --------------------------------------------------------------------------------