From c703229518b9c628a051c40bd46ba2feef7cf0dd Mon Sep 17 00:00:00 2001 From: cat-shark Date: Sat, 20 Jun 2026 18:37:23 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=89=93=E5=8C=85=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E4=BD=BF=E7=94=A8=20packaging=20=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apply-oem.cjs | 8 +-- electron-builder.json5 | 60 +++++++++------- scripts/build_optimize.cjs | 4 +- scripts/copy-playwright-browser.cjs | 2 +- scripts/download-dependencies-china.cjs | 14 ++-- scripts/download-dependencies.cjs | 14 ++-- scripts/icon_convert.sh | 4 +- scripts/init.sh | 43 ++++++------ scripts/make-oem-builder-config.cjs | 10 ++- scripts/prepare-package.cjs | 70 +++++++++++++------ scripts/resource-bundles.config.cjs | 19 ++--- .../electron-builder.oem.generated.json | 40 +++++++---- 12 files changed, 168 insertions(+), 120 deletions(-) diff --git a/apply-oem.cjs b/apply-oem.cjs index 966a5a3..2afce14 100644 --- a/apply-oem.cjs +++ b/apply-oem.cjs @@ -165,7 +165,7 @@ if (fs.existsSync(sourceQrcode)) { // ========== 3. 复制 LOGO 图片 ========== const logoDirs = [ { src: 'public', file: 'logo.svg' }, - { src: 'electron/resources/build', file: 'logo.png' }, + { src: 'packaging/build-resources', file: 'logo.png' }, ]; const sourceLogo = oemConfig.logoPath ? path.join(__dirname, oemConfig.logoPath) : null; if (sourceLogo && fs.existsSync(sourceLogo)) { @@ -174,10 +174,10 @@ if (sourceLogo && fs.existsSync(sourceLogo)) { fs.copyFileSync(sourceLogo, path.join(publicDir, 'logo.png')); console.log('[OK] Copied logo.png to public/'); } - const buildDir = path.join(__dirname, 'electron/resources/build'); + const buildDir = path.join(__dirname, 'packaging/build-resources'); if (fs.existsSync(buildDir)) { fs.copyFileSync(sourceLogo, path.join(buildDir, 'logo.png')); - console.log('[OK] Copied logo.png to electron/resources/build/'); + console.log('[OK] Copied logo.png to packaging/build-resources/'); } const allPublicDirs = fs.readdirSync(__dirname).filter(d => { const sub = path.join(__dirname, d, 'public'); @@ -195,7 +195,7 @@ if (sourceLogo && fs.existsSync(sourceLogo)) { // ========== 3.5 复制图标(ICO/ICNS) ========== const sourceIcon = oemConfig.iconPath ? path.join(__dirname, oemConfig.iconPath) : null; if (sourceIcon && fs.existsSync(sourceIcon)) { - const buildDir = path.join(__dirname, 'electron/resources/build'); + const buildDir = path.join(__dirname, 'packaging/build-resources'); if (fs.existsSync(buildDir)) { const sharp = require('sharp'); const iconBuf = fs.readFileSync(sourceIcon); diff --git a/electron-builder.json5 b/electron-builder.json5 index c0d5314..97ed5e5 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -14,7 +14,7 @@ "productName": "天英超级IP智能体", "directories": { "output": "dist-release-final", - "buildResources": "electron/resources/build" + "buildResources": "packaging/build-resources" }, "afterPack": "./scripts/build_optimize.cjs", "files": [ @@ -29,10 +29,20 @@ "*.html" ] }, - "scripts" + { + "from": "scripts", + "to": "scripts", + "filter": [ + "**/*", + "!local-auth-server.cjs" + ] + }, + "!server/**/*", + "!admin-panel/**/*", + "!local-auth-data/**/*" ], "win": { - icon: "electron/resources/build/logo.ico", + icon: "packaging/build-resources/logo.ico", "target": [ { "target": "nsis", @@ -44,7 +54,7 @@ "artifactName": "${productName}-${version}-win-${arch}.${ext}", "extraResources": [ { - "from": "electron/resources/extra", + "from": "packaging/resources", "to": "extra", "filter": [ "common/config/**/*", @@ -52,49 +62,47 @@ "common/resource-manifest.json", "common/cover-templates/**/*", "common/shuiziren/**/*", - "common/soundEffects/**/*", - "win-x86/**/*", - "win-x86-backup/**/*" + "common/soundEffects/**/*" ] }, { - "from": "electron/resources/extra/common/playwright", + "from": "packaging/vendor/playwright", "to": "resources-bundles/playwright" }, { - "from": "electron/resources/extra/common/python", + "from": "packaging/vendor/python-runtime", "to": "resources-bundles/python-runtime" }, { - "from": "electron/resources/extra/win-x86", + "from": "packaging/vendor/ffmpeg", "to": "resources-bundles/ffmpeg" }, { - "from": "electron/resources/extra/common/soundEffects", + "from": "packaging/resources/common/soundEffects", "to": "resources-bundles/soundEffects" }, { - "from": "electron/resources/extra/common/models", + "from": "packaging/vendor/models", "to": "resources-bundles/models" }, { - "from": "electron/resources/extra/common/fonts", + "from": "packaging/vendor/fonts", "to": "resources-bundles/fonts" }, { - "from": "ziti", + "from": "packaging/vendor/ziti", "to": "resources-bundles/ziti" }, { - "from": "bgm", + "from": "packaging/vendor/bgm", "to": "resources-bundles/bgm" }, { - "from": "electron/resources/voice-preview-cache", + "from": "packaging/resources/voice-preview-cache", "to": "voice-preview-cache" }, { - "from": "electron/resources/build", + "from": "packaging/build-resources", "to": "build", "filter": [ "tray.png", @@ -119,9 +127,9 @@ "warningsAsErrors": false, "runAfterFinish": true, "allowElevation": true, - "installerIcon": "electron/resources/build/logo.ico", - "uninstallerIcon": "electron/resources/build/logo.ico", - "installerHeaderIcon": "electron/resources/build/logo.ico" + "installerIcon": "packaging/build-resources/logo.ico", + "uninstallerIcon": "packaging/build-resources/logo.ico", + "installerHeaderIcon": "packaging/build-resources/logo.ico" }, "appx": { "identityName": "IP", @@ -134,7 +142,7 @@ ] }, "mac": { - "icon": "logo.icns", + "icon": "packaging/build-resources/logo.icns", "target": [ { "target": "dir", @@ -147,7 +155,7 @@ "artifactName": "${productName}-${version}-mac-${arch}.${ext}", "extraResources": [ { - "from": "electron/resources/extra", + "from": "packaging/resources", "to": "extra", "filter": [ "common", @@ -155,7 +163,7 @@ ] }, { - "from": "electron/resources/voice-preview-cache", + "from": "packaging/resources/voice-preview-cache", "to": "voice-preview-cache" } ], @@ -173,7 +181,7 @@ "identity": "Xi'an Yanyi Information Technology Co., Ltd (Q96H3H33RK)" }, "linux": { - "icon": "logo.icns", + "icon": "packaging/build-resources/logo.icns", "desktop": "logo", "maintainer": "天英超级IP智能体", "category": "Utility", @@ -196,7 +204,7 @@ "artifactName": "${productName}-${version}-linux-${arch}.${ext}", "extraResources": [ { - "from": "electron/resources/extra", + "from": "packaging/resources", "to": "extra", "filter": [ "common", @@ -204,7 +212,7 @@ ] }, { - "from": "electron/resources/voice-preview-cache", + "from": "packaging/resources/voice-preview-cache", "to": "voice-preview-cache" } ] diff --git a/scripts/build_optimize.cjs b/scripts/build_optimize.cjs index 2867cae..94172c6 100644 --- a/scripts/build_optimize.cjs +++ b/scripts/build_optimize.cjs @@ -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( diff --git a/scripts/copy-playwright-browser.cjs b/scripts/copy-playwright-browser.cjs index 1c09fcc..4021fee 100644 --- a/scripts/copy-playwright-browser.cjs +++ b/scripts/copy-playwright-browser.cjs @@ -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 的缓存位置(开发环境) diff --git a/scripts/download-dependencies-china.cjs b/scripts/download-dependencies-china.cjs index 2dcaf16..a2f8bf6 100644 --- a/scripts/download-dependencies-china.cjs +++ b/scripts/download-dependencies-china.cjs @@ -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'); diff --git a/scripts/download-dependencies.cjs b/scripts/download-dependencies.cjs index 89fc457..6c79743 100644 --- a/scripts/download-dependencies.cjs +++ b/scripts/download-dependencies.cjs @@ -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'); diff --git a/scripts/icon_convert.sh b/scripts/icon_convert.sh index a814ced..472c426 100644 --- a/scripts/icon_convert.sh +++ b/scripts/icon_convert.sh @@ -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" diff --git a/scripts/init.sh b/scripts/init.sh index 9baec8f..55a1cb5 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -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 diff --git a/scripts/make-oem-builder-config.cjs b/scripts/make-oem-builder-config.cjs index 3f8ce63..989523b 100644 --- a/scripts/make-oem-builder-config.cjs +++ b/scripts/make-oem-builder-config.cjs @@ -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'), diff --git a/scripts/prepare-package.cjs b/scripts/prepare-package.cjs index d908f7a..db1a0dc 100644 --- a/scripts/prepare-package.cjs +++ b/scripts/prepare-package.cjs @@ -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; diff --git a/scripts/resource-bundles.config.cjs b/scripts/resource-bundles.config.cjs index fc62698..556490c 100644 --- a/scripts/resource-bundles.config.cjs +++ b/scripts/resource-bundles.config.cjs @@ -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', }, diff --git a/tools/oem-batch-builder/electron-builder.oem.generated.json b/tools/oem-batch-builder/electron-builder.oem.generated.json index 5c4e73a..af37e1a 100644 --- a/tools/oem-batch-builder/electron-builder.oem.generated.json +++ b/tools/oem-batch-builder/electron-builder.oem.generated.json @@ -13,7 +13,7 @@ "productName": "天英超级IP智能体", "directories": { "output": "dist-release-final", - "buildResources": "electron/resources/build" + "buildResources": "packaging/build-resources" }, "afterPack": "./scripts/build_optimize.cjs", "files": [ @@ -27,7 +27,17 @@ "*.html" ] }, - "scripts", + { + "from": "scripts", + "to": "scripts", + "filter": [ + "**/*", + "!local-auth-server.cjs" + ] + }, + "!server/**/*", + "!admin-panel/**/*", + "!local-auth-data/**/*", "!node_modules/date-fns{,/**/*}", "!node_modules/fluent-ffmpeg{,/**/*}", "!node_modules/ttf2woff2{,/**/*}", @@ -102,7 +112,7 @@ "!node_modules/wait-on{,/**/*}" ], "win": { - "icon": "electron/resources/build/logo.ico", + "icon": "packaging/build-resources/logo.ico", "target": [ { "target": "dir", @@ -114,7 +124,7 @@ "artifactName": "${productName}-${version}-win-${arch}.${ext}", "extraResources": [ { - "from": "electron/resources/extra", + "from": "packaging/resources", "to": "extra", "filter": [ "common/default-config.json", @@ -157,11 +167,11 @@ ] }, { - "from": "electron/resources/voice-preview-cache", + "from": "packaging/resources/voice-preview-cache", "to": "voice-preview-cache" }, { - "from": "electron/resources/build", + "from": "packaging/build-resources", "to": "build", "filter": [ "tray.png", @@ -185,9 +195,9 @@ "warningsAsErrors": false, "runAfterFinish": true, "allowElevation": true, - "installerIcon": "electron/resources/build/logo.ico", - "uninstallerIcon": "electron/resources/build/logo.ico", - "installerHeaderIcon": "electron/resources/build/logo.ico" + "installerIcon": "packaging/build-resources/logo.ico", + "uninstallerIcon": "packaging/build-resources/logo.ico", + "installerHeaderIcon": "packaging/build-resources/logo.ico" }, "appx": { "identityName": "IP", @@ -200,7 +210,7 @@ ] }, "mac": { - "icon": "logo.icns", + "icon": "packaging/build-resources/logo.icns", "target": [ { "target": "dir", @@ -213,7 +223,7 @@ "artifactName": "${productName}-${version}-mac-${arch}.${ext}", "extraResources": [ { - "from": "electron/resources/extra", + "from": "packaging/resources", "to": "extra", "filter": [ "common", @@ -221,7 +231,7 @@ ] }, { - "from": "electron/resources/voice-preview-cache", + "from": "packaging/resources/voice-preview-cache", "to": "voice-preview-cache" } ], @@ -239,7 +249,7 @@ "identity": "Xi'an Yanyi Information Technology Co., Ltd (Q96H3H33RK)" }, "linux": { - "icon": "logo.icns", + "icon": "packaging/build-resources/logo.icns", "desktop": "logo", "maintainer": "天英超级IP智能体", "category": "Utility", @@ -262,7 +272,7 @@ "artifactName": "${productName}-${version}-linux-${arch}.${ext}", "extraResources": [ { - "from": "electron/resources/extra", + "from": "packaging/resources", "to": "extra", "filter": [ "common", @@ -270,7 +280,7 @@ ] }, { - "from": "electron/resources/voice-preview-cache", + "from": "packaging/resources/voice-preview-cache", "to": "voice-preview-cache" } ]