Files
WYF-koubo/electron/lib/resource-path.ts
T
2026-06-19 18:45:55 +08:00

238 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 资源路径工具模块
* 统一管理开发环境和生产环境的资源路径
* ⚠️ 确保所有依赖都能在不同环境下正确识别
*/
import * as fs from 'fs';
import * as path from 'path';
import { app } from 'electron';
import { AppEnv } from '../mapi/env';
/**
* 判断是否为开发环境
*/
export function isDevelopment(): boolean {
const appRoot = process.env.APP_ROOT || process.env.ELECTRON_RESOURCES_PATH || process.cwd();
// 修复:dist-release目录不是开发环境
if (appRoot.includes('dist-release') || appRoot.includes('dist-electron')) {
return false;
}
// 🔧 修复:不硬编码项目文件夹名,改用检查开发目录结构是否存在
// 开发环境特征:项目根目录有 electron/resources/extra 目录
const devResourcePath = path.join(appRoot, 'electron', 'resources', 'extra');
if (fs.existsSync(devResourcePath)) {
return true;
}
// 回退:检查旧的文件夹名(兼容)
return appRoot.includes('aigcpanel-main');
}
/**
* 获取资源根目录
*/
export function getResourceRoot(): string {
const isDev = isDevelopment();
if (isDev) {
// 开发环境:项目根目录/electron/resources/extra
const appRoot = process.env.APP_ROOT || process.cwd();
return path.join(appRoot, 'electron', 'resources', 'extra');
} else {
// 生产环境:使用 process.resourcesPath 获取 resources 目录
// process.resourcesPath 指向 E:\xxx\resources
// 资源文件在 E:\xxx\resources\extra
// 关键修复:直接使用 process.resourcesPath 而不是 APP_ROOT
// 因为 APP_ROOT 被设置为 resources 的父目录,会导致路径错误
const resourcesPath = process.resourcesPath || process.env.ELECTRON_RESOURCES_PATH;
if (resourcesPath) {
return path.join(resourcesPath, 'extra');
}
// 回退:使用 APP_ROOT + resources/extra(仅当 process.resourcesPath 不可用时)
const appRoot = process.env.APP_ROOT || process.cwd();
return path.join(appRoot, 'resources', 'extra');
}
}
/**
* 获取通用资源路径(common 目录下的资源)
* @param subPath 子路径(相对于 extra/common
*/
export function getCommonResourcePath(subPath: string): string {
const resourceRoot = getResourceRoot();
return path.join(resourceRoot, 'common', subPath);
}
export function getRuntimeRoot(...segments: string[]): string {
const baseRoot = AppEnv.installRoot || process.env.APP_ROOT || process.cwd();
return path.join(baseRoot, ...segments);
}
export function getRuntimeDataPath(...segments: string[]): string {
const dataRoot = AppEnv.dataRoot || getRuntimeRoot('data');
return path.join(dataRoot, ...segments);
}
export function getRuntimeBundleRoot(): string {
return AppEnv.resourceBundleRoot || getRuntimeRoot('resources-bundles');
}
export function getRuntimeBundlePath(bundleName: string, ...segments: string[]): string {
return path.join(getRuntimeBundleRoot(), bundleName, ...segments);
}
export function getResourceStatePath(...segments: string[]): string {
const root = AppEnv.resourceStateRoot || getRuntimeDataPath('resource-state');
return path.join(root, ...segments);
}
/**
* 获取平台特定资源路径
* @param subPath 子路径(相对于 extra/{platform}
*/
export function getPlatformResourcePath(subPath: string): string {
const resourceRoot = getResourceRoot();
const platform = getPlatformDir();
return path.join(resourceRoot, platform, subPath);
}
/**
* 获取平台目录名
*/
export function getPlatformDir(): string {
const isWin = process.platform === 'win32';
const isMac = process.platform === 'darwin';
if (isWin) return 'win-x86';
if (isMac) return 'osx';
return 'linux';
}
/**
* 检查资源文件是否存在
* @param resourcePath 资源路径
* @returns 文件是否存在
*/
export function resourceExists(resourcePath: string): boolean {
return fs.existsSync(resourcePath);
}
/**
* 获取并验证资源路径
* @param resourcePath 资源路径
* @param errorMessage 文件不存在时的错误消息
* @returns 验证后的资源路径
* @throws 如果资源不存在则抛出错误
*/
export function getAndVerifyResource(resourcePath: string, errorMessage?: string): string {
if (!fs.existsSync(resourcePath)) {
const error = errorMessage || `资源文件不存在: ${resourcePath}`;
throw new Error(error);
}
return resourcePath;
}
/**
* 快捷方法:获取 Python 可执行文件路径
*/
export function getPythonExecutablePath(): string {
const isWin = process.platform === 'win32';
const pythonFileName = isWin ? 'python.exe' : 'python3';
const runtimePath = getRuntimeBundlePath('python-runtime', pythonFileName);
if (fs.existsSync(runtimePath)) {
return runtimePath;
}
return getCommonResourcePath(path.join('python', pythonFileName));
}
/**
* 快捷方法:获取 FFmpeg 可执行文件路径
*/
export function getFFmpegExecutablePath(): string {
const isWin = process.platform === 'win32';
const isMac = process.platform === 'darwin';
let ffmpegFileName = 'ffmpeg';
if (isWin) ffmpegFileName = 'ffmpeg.exe';
const runtimePath = getRuntimeBundlePath('ffmpeg', ffmpegFileName);
if (fs.existsSync(runtimePath)) {
return runtimePath;
}
return getPlatformResourcePath(ffmpegFileName);
}
/**
* 快捷方法:获取 FFprobe 可执行文件路径
*/
export function getFFprobeExecutablePath(): string {
const isWin = process.platform === 'win32';
const isMac = process.platform === 'darwin';
let ffprobeFileName = 'ffprobe';
if (isWin) ffprobeFileName = 'ffprobe.exe';
const runtimePath = getRuntimeBundlePath('ffmpeg', ffprobeFileName);
if (fs.existsSync(runtimePath)) {
return runtimePath;
}
return getPlatformResourcePath(ffprobeFileName);
}
/**
* 快捷方法:获取模型文件路径
* @param modelFileName 模型文件名(如 'u2net.onnx'
*/
export function getModelPath(modelFileName: string): string {
const runtimePath = getRuntimeBundlePath('models', modelFileName);
if (fs.existsSync(runtimePath)) {
return runtimePath;
}
return getCommonResourcePath(path.join('models', modelFileName));
}
/**
* 快捷方法:获取 Python 脚本路径
* @param scriptFileName Python 脚本文件名
*/
export function getPythonScriptPath(scriptFileName: string): string {
const runtimePath = getRuntimeBundlePath('python-runtime', scriptFileName);
if (fs.existsSync(runtimePath)) {
return runtimePath;
}
const appRoot = process.env.APP_ROOT || process.env.ELECTRON_RESOURCES_PATH || process.cwd();
const isDev = isDevelopment();
if (isDev) {
return path.join(appRoot, 'python', scriptFileName);
} else {
const asarUnpackedPath = path.join(appRoot, 'app.asar.unpacked', 'python', scriptFileName);
if (fs.existsSync(asarUnpackedPath)) {
return asarUnpackedPath;
}
return path.join(appRoot, 'python', scriptFileName);
}
}
/**
* 诊断信息:打印所有资源路径
*/
export function printResourceDiagnostics(): void {
const isDev = isDevelopment();
const resourceRoot = getResourceRoot();
console.log('\n========== 资源路径诊断 ==========');
console.log('环境模式:', isDev ? '开发环境' : '生产环境');
console.log('App Root:', process.env.APP_ROOT || process.cwd());
console.log('资源根目录:', resourceRoot);
console.log('\n关键资源路径:');
console.log(' Python:', getPythonExecutablePath(), '存在:', resourceExists(getPythonExecutablePath()));
console.log(' FFmpeg:', getFFmpegExecutablePath(), '存在:', resourceExists(getFFmpegExecutablePath()));
console.log(' FFprobe:', getFFprobeExecutablePath(), '存在:', resourceExists(getFFprobeExecutablePath()));
console.log(' U2Net模型:', getModelPath('u2net.onnx'), '存在:', resourceExists(getModelPath('u2net.onnx')));
console.log('=====================================\n');
}