Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,5 @@ temp/
.gyprc

python
true/_update-notifier-last-checked
.claude/settings.local.json
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"prebuild:linux": "npm run prepare:python:embedded && npm run build:renderer",
"build:linux": "electron-builder --linux",
"pack": "npm run build:renderer && CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --dir",
"dist": "npm run prepare:python:embedded && npm run build:renderer && electron-builder",
"dist": "npm run prepare:python:uv && npm run build:renderer && electron-builder",
"dist:no-python": "npm run build:renderer && electron-builder",
"lint": "cd src && eslint .",
"preview": "cd src && vite preview",
"clean": "node cleanup.js && rm -rf python"
Expand Down Expand Up @@ -58,6 +59,7 @@
"cross-env": "^10.0.0",
"electron": "36.5.0",
"electron-builder": "^24.6.4",
"electron-rebuild": "^3.2.9",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
Expand Down Expand Up @@ -113,6 +115,8 @@
"directories": {
"output": "dist"
},
"nodeGypRebuild": false,
"npmRebuild": false,
"files": [
"main.js",
"preload.js",
Expand Down Expand Up @@ -150,7 +154,9 @@
}
},
"win": {
"icon": "assets/icon.ico"
"icon": "assets/icon.ico",
"signAndEditExecutable": false,
"verifyUpdateCodeSignature": false
},
"linux": {
"icon": "assets/icon.png",
Expand Down
578 changes: 576 additions & 2 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
ignoredBuiltDependencies:
- '@tailwindcss/oxide'
- esbuild
- ffmpeg-static

onlyBuiltDependencies:
- better-sqlite3
- electron
59 changes: 49 additions & 10 deletions scripts/prepare-embedded-python.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const pipelineAsync = promisify(pipeline);

class EmbeddedPythonBuilder {
constructor() {
this.pythonVersion = '3.11.6';
this.buildDate = '20231002';
this.pythonVersion = '3.11.10';
this.buildDate = '20241016';
this.pythonDir = path.join(__dirname, '..', 'python');
this.forceReinstall = false;
}
Expand All @@ -30,7 +30,12 @@ class EmbeddedPythonBuilder {
console.log(` 大小: ${existingInfo.size.mb}MB (${existingInfo.size.files} 个文件)`);

// 验证关键依赖是否完整
const pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
let pythonPath;
if (process.platform === 'win32') {
pythonPath = path.join(this.pythonDir, 'python.exe');
} else {
pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
}
const isValid = await this.validateExistingEnvironment(pythonPath);

if (isValid) {
Expand Down Expand Up @@ -76,11 +81,22 @@ class EmbeddedPythonBuilder {

async downloadPythonRuntime() {
const arch = process.arch === 'arm64' ? 'aarch64' : 'x86_64';
const filename = `cpython-${this.pythonVersion}+${this.buildDate}-${arch}-apple-darwin-install_only.tar.gz`;

// 根据平台选择正确的标识符
let platformId;
if (process.platform === 'win32') {
platformId = 'pc-windows-msvc-shared';
} else if (process.platform === 'darwin') {
platformId = 'apple-darwin-install_only';
} else {
platformId = 'unknown-linux-gnu';
}

const filename = `cpython-${this.pythonVersion}+${this.buildDate}-${arch}-${platformId}.tar.gz`;
const url = `https://github.com/indygreg/python-build-standalone/releases/download/${this.buildDate}/${filename}`;
const tarPath = path.join(this.pythonDir, 'python.tar.gz');

console.log(`📥 下载Python运行时 (${arch})...`);
console.log(`📥 下载Python运行时 (${arch}, ${process.platform})...`);
console.log(`URL: ${url}`);

await this.downloadFile(url, tarPath);
Expand Down Expand Up @@ -146,8 +162,15 @@ class EmbeddedPythonBuilder {
}

async installDependencies() {
const pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
const sitePackagesPath = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
// 根据平台选择正确的Python路径
let pythonPath, sitePackagesPath;
if (process.platform === 'win32') {
pythonPath = path.join(this.pythonDir, 'python.exe');
sitePackagesPath = path.join(this.pythonDir, 'Lib', 'site-packages');
} else {
pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
sitePackagesPath = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
}

console.log('📦 安装Python依赖...');

Expand Down Expand Up @@ -253,7 +276,12 @@ class EmbeddedPythonBuilder {
console.log('🔍 验证依赖安装...');

const criticalDeps = ['numpy', 'torch', 'librosa', 'funasr'];
const sitePackagesPath = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
let sitePackagesPath;
if (process.platform === 'win32') {
sitePackagesPath = path.join(this.pythonDir, 'Lib', 'site-packages');
} else {
sitePackagesPath = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
}

for (const dep of criticalDeps) {
try {
Expand Down Expand Up @@ -301,7 +329,12 @@ class EmbeddedPythonBuilder {

// 检查关键依赖是否可用
const criticalDeps = ['numpy', 'torch', 'librosa', 'funasr'];
const sitePackagesPath = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
let sitePackagesPath;
if (process.platform === 'win32') {
sitePackagesPath = path.join(this.pythonDir, 'Lib', 'site-packages');
} else {
sitePackagesPath = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
}

// 构建环境变量
const verifyEnv = {
Expand Down Expand Up @@ -392,7 +425,13 @@ class EmbeddedPythonBuilder {
}

async getEmbeddedPythonInfo() {
const pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
// 根据平台选择正确的Python路径
let pythonPath;
if (process.platform === 'win32') {
pythonPath = path.join(this.pythonDir, 'python.exe');
} else {
pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
}

if (!fs.existsSync(pythonPath)) {
return null;
Expand Down
17 changes: 14 additions & 3 deletions scripts/test-embedded-python.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ const { spawn } = require('child_process');
class EmbeddedPythonTester {
constructor() {
this.pythonDir = path.join(__dirname, '..', 'python');
this.pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
// 根据平台选择正确的Python路径
if (process.platform === 'win32') {
this.pythonPath = path.join(this.pythonDir, 'python.exe');
} else {
this.pythonPath = path.join(this.pythonDir, 'bin', 'python3.11');
}
}

async runTests() {
Expand Down Expand Up @@ -139,15 +144,21 @@ class EmbeddedPythonTester {
async runPythonCommand(args) {
return new Promise((resolve, reject) => {
// 设置隔离环境变量
const env = {
let env = {
...process.env,
PYTHONHOME: this.pythonDir,
PYTHONPATH: path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages'),
PYTHONDONTWRITEBYTECODE: '1',
PYTHONIOENCODING: 'utf-8',
PYTHONUNBUFFERED: '1'
};

// 根据平台设置正确的PYTHONPATH
if (process.platform === 'win32') {
env.PYTHONPATH = path.join(this.pythonDir, 'Lib', 'site-packages');
} else {
env.PYTHONPATH = path.join(this.pythonDir, 'lib', 'python3.11', 'site-packages');
}

// 清除可能干扰的环境变量
delete env.PYTHONUSERBASE;
delete env.PYTHONSTARTUP;
Expand Down
85 changes: 67 additions & 18 deletions src/helpers/funasrManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -603,17 +603,47 @@ class FunASRManager {
const pythonEnv = this.buildPythonEnvironment();

return new Promise((resolve) => {
this.logger.info && this.logger.info('启动FunASR Python进程', {
command: pythonCmd,
args: [serverPath],
env: pythonEnv
});

this.serverProcess = spawn(pythonCmd, [serverPath], {
stdio: ["pipe", "pipe", "pipe"],
windowsHide: true,
env: pythonEnv // 使用完整的Python环境变量
});
// 判断是否使用 uv 环境
const projectRoot = path.join(__dirname, "..", "..");
const isUvEnvironment = pythonCmd.includes(path.join(projectRoot, ".venv")) || pythonCmd === "python3" || pythonCmd === "python";

let serverProcess;
let command, args;

if (isUvEnvironment && fs.existsSync(path.join(projectRoot, "pyproject.toml"))) {
// 使用 uv run 执行
command = "uv";
args = ["run", "python", serverPath];
this.logger.info && this.logger.info('使用 uv run 启动 FunASR 服务器', {
command,
args,
cwd: projectRoot
});

serverProcess = spawn(command, args, {
stdio: ["pipe", "pipe", "pipe"],
windowsHide: true,
cwd: projectRoot,
env: pythonEnv
});
} else {
// 使用常规方式
command = pythonCmd;
args = [serverPath];
this.logger.info && this.logger.info('启动FunASR Python进程', {
command,
args,
env: pythonEnv
});

serverProcess = spawn(command, args, {
stdio: ["pipe", "pipe", "pipe"],
windowsHide: true,
env: pythonEnv
});
}

this.serverProcess = serverProcess;

let initResponseReceived = false;

Expand Down Expand Up @@ -804,6 +834,8 @@ class FunASRManager {
path.join(projectRoot, ".venv", "bin", "python3.11"),
path.join(projectRoot, ".venv", "bin", "python3"),
path.join(projectRoot, ".venv", "bin", "python"),
path.join(projectRoot, ".venv", "Scripts", "python.exe"), // Windows uv 环境
path.join(projectRoot, ".venv", "Scripts", "python3.exe"), // Windows uv 环境
// 然后尝试系统路径
"python3.11",
"python3",
Expand Down Expand Up @@ -903,14 +935,31 @@ class FunASRManager {

const result = await new Promise((resolve) => {
// 确保使用正确的Python环境
const pythonEnv = this.buildPythonEnvironment();
let pythonEnv = this.buildPythonEnvironment();

const checkProcess = spawn(pythonCmd, [
"-c",
'import funasr; print("OK")',
], {
env: pythonEnv
});
// 如果使用的是 uv 环境,则使用 uv run
const projectRoot = path.join(__dirname, "..", "..");
const isUvEnvironment = pythonCmd.includes(path.join(projectRoot, ".venv")) || pythonCmd === "python3" || pythonCmd === "python";

let checkProcess;
if (isUvEnvironment && fs.existsSync(path.join(projectRoot, "pyproject.toml"))) {
// 使用 uv run 执行
this.logger.info && this.logger.info('使用 uv run 执行 FunASR 检查');
checkProcess = spawn("uv", [
"run", "python", "-c", 'import funasr; print("OK")'
], {
cwd: projectRoot,
env: pythonEnv
});
} else {
// 使用常规方式
checkProcess = spawn(pythonCmd, [
"-c",
'import funasr; print("OK")',
], {
env: pythonEnv
});
}

let output = "";
let errorOutput = "";
Expand Down