调整打包脚本使用 packaging 资源路径

This commit is contained in:
cat-shark
2026-06-20 18:37:23 +08:00
parent e3d4fd1aa1
commit c703229518
12 changed files with 168 additions and 120 deletions
+2 -2
View File
@@ -10,12 +10,12 @@ exports.default = async function (context) {
context: context,
root: context.appOutDir,
});
// copy extra electron/resources/extra/[name]-[arch] to extra
// copy extra packaging/resources/[name]-[arch] to extra
const platformName = common.platformName();
const platformArch = common.platformArch();
const name = platformName + "-" + platformArch;
const srcDir = `electron/resources/extra/${name}`;
const srcDir = `packaging/resources/${name}`;
let destDir = null;
if (platformName === 'osx') {
destDir = common.pathResolve(
+1 -1
View File
@@ -11,7 +11,7 @@ async function copyPlaywrightBrowser() {
console.log('='.repeat(60));
// 目标目录:项目打包资源
const targetDir = path.join(__dirname, '../electron/resources/extra/common/playwright');
const targetDir = path.join(__dirname, '../packaging/vendor/playwright');
await fs.ensureDir(targetDir);
// 源目录:Playwright 的缓存位置(开发环境)
+8 -6
View File
@@ -4,7 +4,9 @@ const https = require('https');
const http = require('http');
const { execSync } = require('child_process');
const BASE_DIR = path.join(__dirname, '../electron/resources/extra');
const PACKAGING_DIR = path.join(__dirname, '../packaging');
const RESOURCE_DIR = path.join(PACKAGING_DIR, 'resources');
const VENDOR_DIR = path.join(PACKAGING_DIR, 'vendor');
// 下载进度显示
function downloadFile(url, destPath, description) {
@@ -80,11 +82,11 @@ async function main() {
// 1. 下载 Python 3.12 嵌入式版本(使用淘宝镜像)
console.log('\n📦 步骤 1/5: 下载 Python 3.12 嵌入式版本(国内镜像)');
const pythonDir = path.join(BASE_DIR, 'common/python');
const pythonDir = path.join(VENDOR_DIR, 'python-runtime');
await fs.ensureDir(pythonDir);
if (!fs.existsSync(path.join(pythonDir, 'python.exe'))) {
const pythonZip = path.join(BASE_DIR, 'python-3.12.0-embed-amd64.zip');
const pythonZip = path.join(VENDOR_DIR, 'python-3.12.0-embed-amd64.zip');
// 尝试多个镜像源
const mirrors = [
@@ -206,7 +208,7 @@ trusted-host = pypi.tuna.tsinghua.edu.cn
// 4. FFmpeg 手动下载提示
console.log('\n📦 步骤 4/5: FFmpeg(需要手动下载)');
const ffmpegDir = path.join(BASE_DIR, 'win-x86');
const ffmpegDir = path.join(VENDOR_DIR, 'ffmpeg');
await fs.ensureDir(ffmpegDir);
if (!fs.existsSync(path.join(ffmpegDir, 'ffmpeg.exe'))) {
@@ -226,7 +228,7 @@ trusted-host = pypi.tuna.tsinghua.edu.cn
// 5. Chromium
console.log('\n📦 步骤 5/5: Chromium 浏览器');
const chromiumDir = path.join(BASE_DIR, 'common/chromium');
const chromiumDir = path.join(RESOURCE_DIR, 'common/chromium');
await fs.ensureDir(chromiumDir);
if (!fs.existsSync(path.join(chromiumDir, 'chrome-win'))) {
@@ -268,7 +270,7 @@ trusted-host = pypi.tuna.tsinghua.edu.cn
// 6. AI 模型
console.log('\n📦 步骤 6/6: AI 模型');
const modelsDir = path.join(BASE_DIR, 'common/models');
const modelsDir = path.join(VENDOR_DIR, 'models');
await fs.ensureDir(modelsDir);
const u2netPath = path.join(modelsDir, 'u2net.onnx');
+8 -6
View File
@@ -4,7 +4,9 @@ const https = require('https');
const http = require('http');
const { execSync } = require('child_process');
const BASE_DIR = path.join(__dirname, '../electron/resources/extra');
const PACKAGING_DIR = path.join(__dirname, '../packaging');
const RESOURCE_DIR = path.join(PACKAGING_DIR, 'resources');
const VENDOR_DIR = path.join(PACKAGING_DIR, 'vendor');
// 下载进度显示
function downloadFile(url, destPath, description) {
@@ -81,11 +83,11 @@ async function main() {
// 1. 下载 Python 3.12 嵌入式版本
console.log('\n📦 步骤 1/4: 下载 Python 3.12 嵌入式版本');
const pythonDir = path.join(BASE_DIR, 'common/python');
const pythonDir = path.join(VENDOR_DIR, 'python-runtime');
await fs.ensureDir(pythonDir);
if (!fs.existsSync(path.join(pythonDir, 'python.exe'))) {
const pythonZip = path.join(BASE_DIR, 'python-3.12.0-embed-amd64.zip');
const pythonZip = path.join(VENDOR_DIR, 'python-3.12.0-embed-amd64.zip');
await downloadFile(
'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip',
@@ -160,7 +162,7 @@ async function main() {
// 3. 下载 FFmpeg
console.log('\n📦 步骤 3/4: 下载 FFmpeg');
const ffmpegDir = path.join(BASE_DIR, 'win-x86');
const ffmpegDir = path.join(VENDOR_DIR, 'ffmpeg');
await fs.ensureDir(ffmpegDir);
if (!fs.existsSync(path.join(ffmpegDir, 'ffmpeg.exe'))) {
@@ -178,7 +180,7 @@ async function main() {
// 4. 下载 Chromium (通过 puppeteer)
console.log('\n📦 步骤 4/4: 准备 Chromium 浏览器');
const chromiumDir = path.join(BASE_DIR, 'common/chromium');
const chromiumDir = path.join(RESOURCE_DIR, 'common/chromium');
await fs.ensureDir(chromiumDir);
if (!fs.existsSync(path.join(chromiumDir, 'chrome-win'))) {
@@ -220,7 +222,7 @@ async function main() {
// 5. 下载 AI 模型
console.log('\n📦 步骤 5/5: 下载 AI 模型');
const modelsDir = path.join(BASE_DIR, 'common/models');
const modelsDir = path.join(VENDOR_DIR, 'models');
await fs.ensureDir(modelsDir);
const u2netPath = path.join(modelsDir, 'u2net.onnx');
+2 -2
View File
@@ -11,8 +11,8 @@ echo "PROJECT_ROOT: ${PROJECT_ROOT}"
path_svg="${PROJECT_ROOT}/public/logo.svg"
path_white_svg="${PROJECT_ROOT}/public/logo-white.svg"
path_build="${PROJECT_ROOT}/electron/resources/build"
path_extra="${PROJECT_ROOT}/electron/resources/extra"
path_build="${PROJECT_ROOT}/packaging/build-resources"
path_extra="${PROJECT_ROOT}/packaging/resources"
path_source_png="${path_build}/logo_1024x1024.png"
cp -a "${path_svg}" "${PROJECT_ROOT}/src/assets/image/logo.svg"
+22 -21
View File
@@ -15,29 +15,30 @@ else
cd ..
fi
rm -rfv electron/resources/extra/osx-arm64
mkdir -p electron/resources/extra/osx-arm64
cp -a share-binary/osx-arm64/ffmpeg electron/resources/extra/osx-arm64/ffmpeg
cp -a share-binary/osx-arm64/ffprobe electron/resources/extra/osx-arm64/ffprobe
rm -rfv packaging/resources/osx-arm64
mkdir -p packaging/resources/osx-arm64
cp -a share-binary/osx-arm64/ffmpeg packaging/resources/osx-arm64/ffmpeg
cp -a share-binary/osx-arm64/ffprobe packaging/resources/osx-arm64/ffprobe
rm -rfv electron/resources/extra/osx-x86
mkdir -p electron/resources/extra/osx-x86
cp -a share-binary/osx-x86/ffmpeg electron/resources/extra/osx-x86/ffmpeg
cp -a share-binary/osx-x86/ffprobe electron/resources/extra/osx-x86/ffprobe
rm -rfv packaging/resources/osx-x86
mkdir -p packaging/resources/osx-x86
cp -a share-binary/osx-x86/ffmpeg packaging/resources/osx-x86/ffmpeg
cp -a share-binary/osx-x86/ffprobe packaging/resources/osx-x86/ffprobe
rm -rfv electron/resources/extra/linux-arm64
mkdir -p electron/resources/extra/linux-arm64
cp -a share-binary/linux-arm64/ffmpeg electron/resources/extra/linux-arm64/ffmpeg
cp -a share-binary/linux-arm64/ffprobe electron/resources/extra/linux-arm64/ffprobe
rm -rfv packaging/resources/linux-arm64
mkdir -p packaging/resources/linux-arm64
cp -a share-binary/linux-arm64/ffmpeg packaging/resources/linux-arm64/ffmpeg
cp -a share-binary/linux-arm64/ffprobe packaging/resources/linux-arm64/ffprobe
rm -rfv electron/resources/extra/linux-x86
mkdir -p electron/resources/extra/linux-x86
cp -a share-binary/linux-x86/ffmpeg electron/resources/extra/linux-x86/ffmpeg
cp -a share-binary/linux-x86/ffprobe electron/resources/extra/linux-x86/ffprobe
rm -rfv packaging/resources/linux-x86
mkdir -p packaging/resources/linux-x86
cp -a share-binary/linux-x86/ffmpeg packaging/resources/linux-x86/ffmpeg
cp -a share-binary/linux-x86/ffprobe packaging/resources/linux-x86/ffprobe
rm -rfv electron/resources/extra/win-x86
mkdir -p electron/resources/extra/win-x86
cp -a share-binary/win-x86/ffmpeg.exe electron/resources/extra/win-x86/ffmpeg.exe
cp -a share-binary/win-x86/ffprobe.exe electron/resources/extra/win-x86/ffprobe.exe
rm -rfv packaging/vendor/ffmpeg
mkdir -p packaging/vendor/ffmpeg
cp -a share-binary/win-x86/ffmpeg.exe packaging/vendor/ffmpeg/ffmpeg.exe
cp -a share-binary/win-x86/ffprobe.exe packaging/vendor/ffmpeg/ffprobe.exe
ls -R electron/resources/extra
ls -R packaging/resources
ls -R packaging/vendor/ffmpeg
+4 -6
View File
@@ -12,10 +12,8 @@ function isLargeBundledResource(entry) {
return to.startsWith('resources-bundles/')
|| from === 'ziti'
|| from === 'bgm'
|| from.includes('/common/playwright')
|| from.includes('/common/python')
|| from.includes('/common/models')
|| from.includes('/common/fonts');
|| from.startsWith('packaging/vendor/')
|| from.includes('/packaging/vendor/');
}
const config = JSON5.parse(fs.readFileSync(sourcePath, 'utf8'));
@@ -114,7 +112,7 @@ if (config.win && Array.isArray(config.win.extraResources)) {
.filter(entry => !isLargeBundledResource(entry))
.map(entry => {
const from = String(entry && entry.from || '').replace(/\\/g, '/');
if (from.endsWith('electron/resources/extra') && Array.isArray(entry.filter)) {
if (from.endsWith('packaging/resources') && Array.isArray(entry.filter)) {
return {
...entry,
filter: entry.filter
@@ -134,7 +132,7 @@ if (config.win && Array.isArray(config.win.extraResources)) {
.concat(oemCoverTemplateFiles)
};
}
if (from.endsWith('electron/resources/build') && Array.isArray(entry.filter)) {
if (from.endsWith('packaging/build-resources') && Array.isArray(entry.filter)) {
return {
...entry,
filter: entry.filter.filter(pattern => String(pattern).replace(/\\/g, '/') !== 'vc_redist.x64.exe'),
+47 -23
View File
@@ -6,8 +6,14 @@ async function preparePackage() {
console.log('📦 开始准备打包资源...\n');
console.log('='.repeat(60));
const extraDir = path.join(__dirname, '../electron/resources/extra');
const packagingDir = path.join(__dirname, '../packaging');
const extraDir = path.join(packagingDir, 'resources');
const vendorDir = path.join(packagingDir, 'vendor');
const commonDir = path.join(extraDir, 'common');
const pythonRuntimeDir = path.join(vendorDir, 'python-runtime');
const ffmpegDir = path.join(vendorDir, 'ffmpeg');
const modelDir = path.join(vendorDir, 'models');
const fontBundleDir = path.join(vendorDir, 'fonts');
// 0. 发布前同步系统字幕模板,避免安装包带入旧候选模板或旧音效配置
console.log('\n0锔忊儯 鍚屾绯荤粺瀛楀箷妯℃澘...');
@@ -16,16 +22,16 @@ async function preparePackage() {
// 1. 确保目录存在
console.log('\n1️⃣ 确保目录结构存在...');
await fs.ensureDir(path.join(commonDir, 'python'));
await fs.ensureDir(pythonRuntimeDir);
await fs.ensureDir(path.join(commonDir, 'chromium'));
await fs.ensureDir(path.join(commonDir, 'python-scripts'));
await fs.ensureDir(path.join(commonDir, 'models'));
await fs.ensureDir(path.join(commonDir, 'fonts'));
await fs.ensureDir(path.join(commonDir, 'bgm'));
await fs.ensureDir(modelDir);
await fs.ensureDir(fontBundleDir);
await fs.ensureDir(vendorDir);
await fs.ensureDir(path.join(commonDir, 'demo'));
await fs.ensureDir(path.join(commonDir, 'config'));
await fs.ensureDir(path.join(commonDir, 'cover-templates'));
await fs.ensureDir(path.join(extraDir, 'win-x86'));
await fs.ensureDir(ffmpegDir);
console.log(' ✅ 目录结构准备完成\n');
// 2. 复制 Python 脚本
@@ -60,7 +66,6 @@ async function preparePackage() {
const fileCount = await countFiles(pythonDest);
console.log(` ✅ Python 脚本复制到 python-scripts 完成 (${fileCount} 个文件)`);
const pythonRuntimeDir = path.join(commonDir, 'python');
await copyWithLockTolerance(pythonSrc, pythonRuntimeDir, {
overwrite: true,
filter: pythonFilter
@@ -72,10 +77,13 @@ async function preparePackage() {
// 3. 复制字体
console.log('3️⃣ 复制字体文件...');
const fontSrc = path.join(__dirname, '../ziti');
const fontDest = path.join(commonDir, 'fonts/ziti');
const fontSrc = await firstExistingPath([
path.join(vendorDir, 'ziti'),
path.join(__dirname, '../ziti')
]);
const fontDest = path.join(fontBundleDir, 'ziti');
if (fs.existsSync(fontSrc)) {
if (fontSrc && fs.existsSync(fontSrc)) {
if (process.env.OEM_BATCH_BUILD === '1' && await fs.pathExists(fontDest)) {
const existingFileCount = await countFiles(fontDest);
if (existingFileCount > 0) {
@@ -105,19 +113,26 @@ async function preparePackage() {
// 4. 复制 BGM
console.log('4️⃣ 复制背景音乐...');
const bgmSrc = path.join(__dirname, '../bgm');
const bgmDest = path.join(commonDir, 'bgm');
const bgmSrc = await firstExistingPath([
path.join(vendorDir, 'bgm'),
path.join(__dirname, '../bgm')
]);
const bgmDest = path.join(vendorDir, 'bgm');
if (fs.existsSync(bgmSrc)) {
if (bgmSrc && fs.existsSync(bgmSrc)) {
if (process.env.OEM_BATCH_BUILD === '1' && await fs.pathExists(bgmDest)) {
const existingFileCount = await countFiles(bgmDest);
if (existingFileCount > 0) {
console.log(` [SKIP] OEM batch build reuses prepared BGM (${existingFileCount} files)\n`);
} else {
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
if (path.resolve(bgmSrc) !== path.resolve(bgmDest)) {
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
}
}
} else {
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
if (path.resolve(bgmSrc) !== path.resolve(bgmDest)) {
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
}
}
const fileCount = await countFiles(bgmDest);
const totalSize = await getDirSize(bgmDest);
@@ -175,7 +190,7 @@ async function preparePackage() {
name: 'default-ipagent-config.json'
},
{
src: path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json'),
src: path.join(commonDir, 'config/system-templates.json'),
dest: path.join(configDest, 'system-templates.json'),
name: 'system-templates.json (系统模板配置 - 8套字幕+8套封面)'
}
@@ -217,7 +232,7 @@ async function preparePackage() {
];
// 动态查找最新的 Playwright Chromium 版本
const playwrightDir = path.join(commonDir, 'playwright');
const playwrightDir = path.join(vendorDir, 'playwright');
let playwrightChromiumCheck = null;
if (await fs.pathExists(playwrightDir)) {
const chromiumDirs = (await fs.readdir(playwrightDir))
@@ -236,13 +251,13 @@ async function preparePackage() {
}
const checks = [
{ name: 'Python 环境', path: path.join(commonDir, 'python/python.exe') },
{ name: 'Python 依赖 (opencv)', path: path.join(commonDir, 'python/Lib/site-packages/cv2') },
{ name: 'FFmpeg (win-x86)', path: path.join(extraDir, 'win-x86/ffmpeg.exe') },
{ name: 'FFprobe (win-x86)', path: path.join(extraDir, 'win-x86/ffprobe.exe') },
...ffmpegDlls.map(dll => ({ name: `FFmpeg DLL (${dll})`, path: path.join(extraDir, `win-x86/${dll}`) })),
{ name: 'Python 环境', path: path.join(pythonRuntimeDir, 'python.exe') },
{ name: 'Python 依赖 (opencv)', path: path.join(pythonRuntimeDir, 'Lib/site-packages/cv2') },
{ name: 'FFmpeg (win-x86)', path: path.join(ffmpegDir, 'ffmpeg.exe') },
{ name: 'FFprobe (win-x86)', path: path.join(ffmpegDir, 'ffprobe.exe') },
...ffmpegDlls.map(dll => ({ name: `FFmpeg DLL (${dll})`, path: path.join(ffmpegDir, dll) })),
...(playwrightChromiumCheck ? [playwrightChromiumCheck] : []),
{ name: 'U2-Net 模型', path: path.join(commonDir, 'models/u2net.onnx') },
{ name: 'U2-Net 模型', path: path.join(modelDir, 'u2net.onnx') },
];
let allOk = true;
@@ -295,6 +310,15 @@ async function copyWithLockTolerance(src, dest, options = {}) {
}
}
async function firstExistingPath(paths) {
for (const candidate of paths) {
if (candidate && await fs.pathExists(candidate)) {
return candidate;
}
}
return null;
}
// 辅助函数:统计文件数量
async function countFiles(dir) {
let count = 0;
+11 -8
View File
@@ -1,54 +1,57 @@
const path = require('path');
const root = path.join(__dirname, '..');
const packagingRoot = path.join(root, 'packaging');
const resourceRoot = path.join(packagingRoot, 'resources');
const vendorRoot = path.join(packagingRoot, 'vendor');
module.exports = {
releaseBaseUrlEnv: 'RESOURCE_BASE_URL',
releaseBaseUrl: 'https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates',
outputDir: path.join(root, 'dist-release-final', 'resource-bundles'),
releaseManifestPath: path.join(root, 'dist-release-final', 'resource-manifest.json'),
embeddedManifestPath: path.join(root, 'electron', 'resources', 'extra', 'common', 'resource-manifest.json'),
embeddedManifestPath: path.join(resourceRoot, 'common', 'resource-manifest.json'),
buildStatePath: path.join(root, 'build', 'resource-build-state.json'),
bundles: [
{
name: 'playwright',
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'playwright'),
source: path.join(vendorRoot, 'playwright'),
required: true,
extractTo: 'resources-bundles/playwright',
},
{
name: 'python-runtime',
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'python'),
source: path.join(vendorRoot, 'python-runtime'),
required: true,
extractTo: 'resources-bundles/python-runtime',
},
{
name: 'ffmpeg',
source: path.join(root, 'electron', 'resources', 'extra', 'win-x86'),
source: path.join(vendorRoot, 'ffmpeg'),
required: true,
extractTo: 'resources-bundles/ffmpeg',
},
{
name: 'models',
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'models'),
source: path.join(vendorRoot, 'models'),
required: true,
extractTo: 'resources-bundles/models',
},
{
name: 'fonts',
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'fonts'),
source: path.join(vendorRoot, 'fonts'),
required: false,
extractTo: 'resources-bundles/fonts',
},
{
name: 'ziti',
source: path.join(root, 'ziti'),
source: path.join(vendorRoot, 'ziti'),
required: true,
extractTo: 'resources-bundles/ziti',
},
{
name: 'bgm',
source: path.join(root, 'bgm'),
source: path.join(vendorRoot, 'bgm'),
required: false,
extractTo: 'resources-bundles/bgm',
},