Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a180295b3b | |||
| 4d7a19dc8a | |||
| b8a6f0c649 | |||
| c703229518 | |||
| e3d4fd1aa1 | |||
| 028d407a78 |
@@ -50,13 +50,15 @@ src/lang/source-use.json
|
|||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
/share-binary/
|
/share-binary/
|
||||||
/electron/resources/extra/linux-arm64/
|
/archive/
|
||||||
/electron/resources/extra/linux-x86/
|
/packaging/resources/linux-arm64/
|
||||||
/electron/resources/extra/osx-arm64/
|
/packaging/resources/linux-x86/
|
||||||
/electron/resources/extra/osx-x86/
|
/packaging/resources/osx/
|
||||||
/electron/resources/extra/win-x86/
|
/packaging/resources/osx-arm64/
|
||||||
/electron/resources/extra/osx/
|
/packaging/resources/osx-x86/
|
||||||
/electron/resources/extra/common/playwright/chromium-1200/chrome-mac/
|
/packaging/resources/win-x86-backup/
|
||||||
|
/packaging/resources/common/chromium/
|
||||||
|
/packaging/vendor/
|
||||||
nul
|
nul
|
||||||
|
|
||||||
# Python cache
|
# Python cache
|
||||||
@@ -92,15 +94,30 @@ fix_server.py
|
|||||||
build/
|
build/
|
||||||
output/
|
output/
|
||||||
tmp/
|
tmp/
|
||||||
resources-bundles/
|
/resources-bundles/
|
||||||
/electron/resources/extra/win-x86-backup/
|
/dist/
|
||||||
/electron/resources/extra/common/playwright/
|
/dist-ssr/
|
||||||
/electron/resources/extra/common/python/
|
/dist-electron/
|
||||||
/electron/resources/extra/common/fonts/
|
/dist-release/
|
||||||
/electron/resources/extra/common/models/
|
/dist-release-final/
|
||||||
/electron/resources/extra/common/bgm/
|
/dist-release-build/
|
||||||
/electron/resources/extra/common/chromium/
|
/dist-release-new/
|
||||||
/electron/resources/extra/common/soundEffects/
|
/release-assets/
|
||||||
|
/win-unpacked/
|
||||||
|
/builder-debug.yml
|
||||||
|
/builder-effective-config.yaml
|
||||||
|
*.blockmap
|
||||||
|
*.nsis.7z
|
||||||
|
/packaging/resources/common/playwright/
|
||||||
|
/packaging/resources/common/python/
|
||||||
|
/packaging/resources/common/fonts/
|
||||||
|
/packaging/resources/common/models/
|
||||||
|
/packaging/resources/common/bgm/
|
||||||
|
/packaging/resources/common/soundEffects/
|
||||||
|
/packaging/resources/common/python-scripts/
|
||||||
|
/packaging/resources/stickers/
|
||||||
|
/packaging/resources/common/config/* - 副本*.json
|
||||||
|
/packaging/resources/common/config/*-backup-*.json
|
||||||
|
|
||||||
# Clean repository: media/content packs are not source code
|
# Clean repository: media/content packs are not source code
|
||||||
*.mp3
|
*.mp3
|
||||||
@@ -134,10 +151,20 @@ active-run.json
|
|||||||
activation-server/*.db
|
activation-server/*.db
|
||||||
user-server/*.db
|
user-server/*.db
|
||||||
user-server/_archive_old_versions/
|
user-server/_archive_old_versions/
|
||||||
fonts/
|
/fonts/
|
||||||
bgm/
|
/bgm/
|
||||||
ziti/
|
/ziti/
|
||||||
resources/
|
/resources/
|
||||||
resources-bundles/
|
/resources-bundles/
|
||||||
tools/**/runs/
|
tools/**/runs/
|
||||||
electron/resources/voice-preview-cache/
|
/packaging/resources/voice-preview-cache/*.wav
|
||||||
|
|
||||||
|
# Clean repository: local auth/admin runtime data
|
||||||
|
local-auth-data/
|
||||||
|
server/local-auth-data/
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
users.json
|
||||||
|
|
||||||
|
# Clean repository: large redistributable installers
|
||||||
|
/packaging/build-resources/vc_redist*.exe
|
||||||
|
|||||||
@@ -48,6 +48,27 @@ http://127.0.0.1:3302/admin/?api=http://127.0.0.1:3302
|
|||||||
npm.cmd run start:check
|
npm.cmd run start:check
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 打包客户端
|
||||||
|
|
||||||
|
客户端安装包不包含本地认证服务和管理后台,只会连接你指定的认证服务地址。
|
||||||
|
|
||||||
|
```bat
|
||||||
|
npm.cmd run build:client:win -- --auth-url=https://auth.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
本地测试可以先只打包目录,速度更快:
|
||||||
|
|
||||||
|
```bat
|
||||||
|
npm.cmd run build:client:win -- --auth-url=http://127.0.0.1:3302 --dir
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `--auth-url` 可以带或不带 `/api`,脚本会自动规范化。
|
||||||
|
- 打包时会把该地址注入客户端默认配置。
|
||||||
|
- 运行时仍可通过环境变量 `AUTH_SERVER_URL` 覆盖认证服务地址。
|
||||||
|
- 打包配置已排除 `server/`、`admin-panel/`、`scripts/local-auth-server.cjs` 和 `local-auth-data/`。
|
||||||
|
|
||||||
## 系统 API Key 配置
|
## 系统 API Key 配置
|
||||||
|
|
||||||
进入 `http://127.0.0.1:3302/admin/` 后,在“系统配置”里填写:
|
进入 `http://127.0.0.1:3302/admin/` 后,在“系统配置”里填写:
|
||||||
@@ -57,3 +78,14 @@ npm.cmd run start:check
|
|||||||
- 其他 OSS、RunningHub、火山引擎、云美颜配置也可以在这里维护
|
- 其他 OSS、RunningHub、火山引擎、云美颜配置也可以在这里维护
|
||||||
|
|
||||||
这些配置会通过 `/api/system/config/public` 提供给本地应用作为兜底配置。
|
这些配置会通过 `/api/system/config/public` 提供给本地应用作为兜底配置。
|
||||||
|
|
||||||
|
## 目录结构约定
|
||||||
|
|
||||||
|
clean 目录就是后续入库的项目根目录;外层 `aIzhinengti` 只作为临时整理和参考目录。
|
||||||
|
|
||||||
|
- 源码放在 `src/`、`electron/`、`server/`、`admin-panel/`。
|
||||||
|
- 打包相关资源统一放在 `packaging/`。
|
||||||
|
- 大型本地依赖放在 `packaging/vendor/`,例如 Python runtime、FFmpeg、Playwright、模型、字体、BGM;这些内容默认不入库。
|
||||||
|
- 构建输出、运行数据和历史整理残留分别由 `.gitignore` 和 `archive/` 隔离。
|
||||||
|
|
||||||
|
更完整说明见 `docs/PROJECT_LAYOUT.md` 和 `packaging/README.md`。
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ if (fs.existsSync(sourceQrcode)) {
|
|||||||
// ========== 3. 复制 LOGO 图片 ==========
|
// ========== 3. 复制 LOGO 图片 ==========
|
||||||
const logoDirs = [
|
const logoDirs = [
|
||||||
{ src: 'public', file: 'logo.svg' },
|
{ 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;
|
const sourceLogo = oemConfig.logoPath ? path.join(__dirname, oemConfig.logoPath) : null;
|
||||||
if (sourceLogo && fs.existsSync(sourceLogo)) {
|
if (sourceLogo && fs.existsSync(sourceLogo)) {
|
||||||
@@ -174,10 +174,10 @@ if (sourceLogo && fs.existsSync(sourceLogo)) {
|
|||||||
fs.copyFileSync(sourceLogo, path.join(publicDir, 'logo.png'));
|
fs.copyFileSync(sourceLogo, path.join(publicDir, 'logo.png'));
|
||||||
console.log('[OK] Copied logo.png to public/');
|
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)) {
|
if (fs.existsSync(buildDir)) {
|
||||||
fs.copyFileSync(sourceLogo, path.join(buildDir, 'logo.png'));
|
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 allPublicDirs = fs.readdirSync(__dirname).filter(d => {
|
||||||
const sub = path.join(__dirname, d, 'public');
|
const sub = path.join(__dirname, d, 'public');
|
||||||
@@ -195,7 +195,7 @@ if (sourceLogo && fs.existsSync(sourceLogo)) {
|
|||||||
// ========== 3.5 复制图标(ICO/ICNS) ==========
|
// ========== 3.5 复制图标(ICO/ICNS) ==========
|
||||||
const sourceIcon = oemConfig.iconPath ? path.join(__dirname, oemConfig.iconPath) : null;
|
const sourceIcon = oemConfig.iconPath ? path.join(__dirname, oemConfig.iconPath) : null;
|
||||||
if (sourceIcon && fs.existsSync(sourceIcon)) {
|
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)) {
|
if (fs.existsSync(buildDir)) {
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
const iconBuf = fs.readFileSync(sourceIcon);
|
const iconBuf = fs.readFileSync(sourceIcon);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Project Layout
|
||||||
|
|
||||||
|
`aIzhinengti-clean` is the repository-ready project root. The parent `aIzhinengti` directory is only a temporary workspace/reference area.
|
||||||
|
|
||||||
|
## Root
|
||||||
|
|
||||||
|
- `src/`: renderer application source.
|
||||||
|
- `electron/`: Electron main/preload code and app configuration source.
|
||||||
|
- `server/`, `admin-panel/`: local auth/admin support code.
|
||||||
|
- `scripts/`: development, packaging, release, and maintenance scripts.
|
||||||
|
- `packaging/`: build resources and local packaging dependencies.
|
||||||
|
- `docs/`: project notes and operational documentation.
|
||||||
|
- `archive/`: preserved cleanup leftovers. Do not treat this as active source.
|
||||||
|
|
||||||
|
## Packaging Split
|
||||||
|
|
||||||
|
- Track small, intentional packaging assets such as icons and JSON config.
|
||||||
|
- Keep large runtimes and media packs under `packaging/vendor` or ignored resource subdirectories.
|
||||||
|
- Do not place Python runtimes, FFmpeg builds, Playwright browsers, models, fonts, BGM, or generated release output in the project root.
|
||||||
|
|
||||||
|
## Generated Or Local-Only Directories
|
||||||
|
|
||||||
|
The following root directories are local outputs or runtime data and should stay ignored:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build/
|
||||||
|
data/
|
||||||
|
dist/
|
||||||
|
dist-electron/
|
||||||
|
dist-release-final/
|
||||||
|
resources-bundles/
|
||||||
|
node_modules/
|
||||||
|
```
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"productName": "天英超级IP智能体",
|
"productName": "天英超级IP智能体",
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "dist-release-final",
|
"output": "dist-release-final",
|
||||||
"buildResources": "electron/resources/build"
|
"buildResources": "packaging/build-resources"
|
||||||
},
|
},
|
||||||
"afterPack": "./scripts/build_optimize.cjs",
|
"afterPack": "./scripts/build_optimize.cjs",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -29,10 +29,20 @@
|
|||||||
"*.html"
|
"*.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts"
|
{
|
||||||
|
"from": "scripts",
|
||||||
|
"to": "scripts",
|
||||||
|
"filter": [
|
||||||
|
"**/*",
|
||||||
|
"!local-auth-server.cjs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"!server/**/*",
|
||||||
|
"!admin-panel/**/*",
|
||||||
|
"!local-auth-data/**/*"
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
icon: "electron/resources/build/logo.ico",
|
icon: "packaging/build-resources/logo.ico",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
@@ -44,7 +54,7 @@
|
|||||||
"artifactName": "${productName}-${version}-win-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-win-${arch}.${ext}",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra",
|
"from": "packaging/resources",
|
||||||
"to": "extra",
|
"to": "extra",
|
||||||
"filter": [
|
"filter": [
|
||||||
"common/config/**/*",
|
"common/config/**/*",
|
||||||
@@ -52,49 +62,47 @@
|
|||||||
"common/resource-manifest.json",
|
"common/resource-manifest.json",
|
||||||
"common/cover-templates/**/*",
|
"common/cover-templates/**/*",
|
||||||
"common/shuiziren/**/*",
|
"common/shuiziren/**/*",
|
||||||
"common/soundEffects/**/*",
|
"common/soundEffects/**/*"
|
||||||
"win-x86/**/*",
|
|
||||||
"win-x86-backup/**/*"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra/common/playwright",
|
"from": "packaging/vendor/playwright",
|
||||||
"to": "resources-bundles/playwright"
|
"to": "resources-bundles/playwright"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra/common/python",
|
"from": "packaging/vendor/python-runtime",
|
||||||
"to": "resources-bundles/python-runtime"
|
"to": "resources-bundles/python-runtime"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra/win-x86",
|
"from": "packaging/vendor/ffmpeg",
|
||||||
"to": "resources-bundles/ffmpeg"
|
"to": "resources-bundles/ffmpeg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra/common/soundEffects",
|
"from": "packaging/resources/common/soundEffects",
|
||||||
"to": "resources-bundles/soundEffects"
|
"to": "resources-bundles/soundEffects"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra/common/models",
|
"from": "packaging/vendor/models",
|
||||||
"to": "resources-bundles/models"
|
"to": "resources-bundles/models"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra/common/fonts",
|
"from": "packaging/vendor/fonts",
|
||||||
"to": "resources-bundles/fonts"
|
"to": "resources-bundles/fonts"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "ziti",
|
"from": "packaging/vendor/ziti",
|
||||||
"to": "resources-bundles/ziti"
|
"to": "resources-bundles/ziti"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "bgm",
|
"from": "packaging/vendor/bgm",
|
||||||
"to": "resources-bundles/bgm"
|
"to": "resources-bundles/bgm"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/voice-preview-cache",
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
"to": "voice-preview-cache"
|
"to": "voice-preview-cache"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/build",
|
"from": "packaging/build-resources",
|
||||||
"to": "build",
|
"to": "build",
|
||||||
"filter": [
|
"filter": [
|
||||||
"tray.png",
|
"tray.png",
|
||||||
@@ -119,9 +127,9 @@
|
|||||||
"warningsAsErrors": false,
|
"warningsAsErrors": false,
|
||||||
"runAfterFinish": true,
|
"runAfterFinish": true,
|
||||||
"allowElevation": true,
|
"allowElevation": true,
|
||||||
"installerIcon": "electron/resources/build/logo.ico",
|
"installerIcon": "packaging/build-resources/logo.ico",
|
||||||
"uninstallerIcon": "electron/resources/build/logo.ico",
|
"uninstallerIcon": "packaging/build-resources/logo.ico",
|
||||||
"installerHeaderIcon": "electron/resources/build/logo.ico"
|
"installerHeaderIcon": "packaging/build-resources/logo.ico"
|
||||||
},
|
},
|
||||||
"appx": {
|
"appx": {
|
||||||
"identityName": "IP",
|
"identityName": "IP",
|
||||||
@@ -134,7 +142,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"icon": "logo.icns",
|
"icon": "packaging/build-resources/logo.icns",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "dir",
|
"target": "dir",
|
||||||
@@ -147,7 +155,7 @@
|
|||||||
"artifactName": "${productName}-${version}-mac-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-mac-${arch}.${ext}",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra",
|
"from": "packaging/resources",
|
||||||
"to": "extra",
|
"to": "extra",
|
||||||
"filter": [
|
"filter": [
|
||||||
"common",
|
"common",
|
||||||
@@ -155,7 +163,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/voice-preview-cache",
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
"to": "voice-preview-cache"
|
"to": "voice-preview-cache"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -173,7 +181,7 @@
|
|||||||
"identity": "Xi'an Yanyi Information Technology Co., Ltd (Q96H3H33RK)"
|
"identity": "Xi'an Yanyi Information Technology Co., Ltd (Q96H3H33RK)"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"icon": "logo.icns",
|
"icon": "packaging/build-resources/logo.icns",
|
||||||
"desktop": "logo",
|
"desktop": "logo",
|
||||||
"maintainer": "天英超级IP智能体",
|
"maintainer": "天英超级IP智能体",
|
||||||
"category": "Utility",
|
"category": "Utility",
|
||||||
@@ -196,7 +204,7 @@
|
|||||||
"artifactName": "${productName}-${version}-linux-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-linux-${arch}.${ext}",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra",
|
"from": "packaging/resources",
|
||||||
"to": "extra",
|
"to": "extra",
|
||||||
"filter": [
|
"filter": [
|
||||||
"common",
|
"common",
|
||||||
@@ -204,7 +212,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/voice-preview-cache",
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
"to": "voice-preview-cache"
|
"to": "voice-preview-cache"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2874,5 +2874,5 @@
|
|||||||
"is_system": 1
|
"is_system": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": "2026-06-16T08:45:31.753Z"
|
"timestamp": "2026-06-20T10:27:18.332Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4294,5 +4294,5 @@
|
|||||||
"is_system": 1
|
"is_system": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": "2026-06-16T08:45:31.753Z"
|
"timestamp": "2026-06-20T10:27:18.332Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,9 +59,8 @@ export function getPlaywrightChromiumPath(): string | undefined {
|
|||||||
const runtimePath = getRuntimeBundlePath('playwright');
|
const runtimePath = getRuntimeBundlePath('playwright');
|
||||||
const basePaths = [
|
const basePaths = [
|
||||||
runtimePath,
|
runtimePath,
|
||||||
path.join(process.resourcesPath || app.getAppPath(), 'extra/common/playwright'),
|
path.join(app.getAppPath(), 'packaging/vendor/playwright'),
|
||||||
path.join(app.getAppPath(), 'resources/extra/common/playwright'),
|
path.join(process.cwd(), 'packaging/vendor/playwright'),
|
||||||
path.join(app.getAppPath(), 'electron/resources/extra/common/playwright'),
|
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
const chromiumVersions = ['chromium-1200'];
|
const chromiumVersions = ['chromium-1200'];
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ export function isDevelopment(): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 🔧 修复:不硬编码项目文件夹名,改用检查开发目录结构是否存在
|
// 🔧 修复:不硬编码项目文件夹名,改用检查开发目录结构是否存在
|
||||||
// 开发环境特征:项目根目录有 electron/resources/extra 目录
|
// 开发环境特征:项目根目录有 packaging/resources 目录
|
||||||
const devResourcePath = path.join(appRoot, 'electron', 'resources', 'extra');
|
const devResourcePath = path.join(appRoot, 'packaging', 'resources');
|
||||||
if (fs.existsSync(devResourcePath)) {
|
if (fs.existsSync(devResourcePath)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
const legacyDevResourcePath = path.join(appRoot, 'electron', 'resources', 'extra');
|
||||||
|
if (fs.existsSync(legacyDevResourcePath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// 回退:检查旧的文件夹名(兼容)
|
// 回退:检查旧的文件夹名(兼容)
|
||||||
return appRoot.includes('aigcpanel-main');
|
return appRoot.includes('aigcpanel-main');
|
||||||
}
|
}
|
||||||
@@ -35,8 +39,12 @@ export function getResourceRoot(): string {
|
|||||||
const isDev = isDevelopment();
|
const isDev = isDevelopment();
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
// 开发环境:项目根目录/electron/resources/extra
|
// 开发环境:项目根目录/packaging/resources
|
||||||
const appRoot = process.env.APP_ROOT || process.cwd();
|
const appRoot = process.env.APP_ROOT || process.cwd();
|
||||||
|
const devResourcePath = path.join(appRoot, 'packaging', 'resources');
|
||||||
|
if (fs.existsSync(devResourcePath)) {
|
||||||
|
return devResourcePath;
|
||||||
|
}
|
||||||
return path.join(appRoot, 'electron', 'resources', 'extra');
|
return path.join(appRoot, 'electron', 'resources', 'extra');
|
||||||
} else {
|
} else {
|
||||||
// 生产环境:使用 process.resourcesPath 获取 resources 目录
|
// 生产环境:使用 process.resourcesPath 获取 resources 目录
|
||||||
@@ -142,6 +150,10 @@ export function getPythonExecutablePath(): string {
|
|||||||
if (fs.existsSync(runtimePath)) {
|
if (fs.existsSync(runtimePath)) {
|
||||||
return runtimePath;
|
return runtimePath;
|
||||||
}
|
}
|
||||||
|
const devPath = path.join(process.env.APP_ROOT || process.cwd(), 'packaging', 'vendor', 'python-runtime', pythonFileName);
|
||||||
|
if (fs.existsSync(devPath)) {
|
||||||
|
return devPath;
|
||||||
|
}
|
||||||
return getCommonResourcePath(path.join('python', pythonFileName));
|
return getCommonResourcePath(path.join('python', pythonFileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +172,11 @@ export function getFFmpegExecutablePath(): string {
|
|||||||
return runtimePath;
|
return runtimePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const devPath = path.join(process.env.APP_ROOT || process.cwd(), 'packaging', 'vendor', 'ffmpeg', ffmpegFileName);
|
||||||
|
if (fs.existsSync(devPath)) {
|
||||||
|
return devPath;
|
||||||
|
}
|
||||||
|
|
||||||
return getPlatformResourcePath(ffmpegFileName);
|
return getPlatformResourcePath(ffmpegFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +195,11 @@ export function getFFprobeExecutablePath(): string {
|
|||||||
return runtimePath;
|
return runtimePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const devPath = path.join(process.env.APP_ROOT || process.cwd(), 'packaging', 'vendor', 'ffmpeg', ffprobeFileName);
|
||||||
|
if (fs.existsSync(devPath)) {
|
||||||
|
return devPath;
|
||||||
|
}
|
||||||
|
|
||||||
return getPlatformResourcePath(ffprobeFileName);
|
return getPlatformResourcePath(ffprobeFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +212,10 @@ export function getModelPath(modelFileName: string): string {
|
|||||||
if (fs.existsSync(runtimePath)) {
|
if (fs.existsSync(runtimePath)) {
|
||||||
return runtimePath;
|
return runtimePath;
|
||||||
}
|
}
|
||||||
|
const devPath = path.join(process.env.APP_ROOT || process.cwd(), 'packaging', 'vendor', 'models', modelFileName);
|
||||||
|
if (fs.existsSync(devPath)) {
|
||||||
|
return devPath;
|
||||||
|
}
|
||||||
return getCommonResourcePath(path.join('models', modelFileName));
|
return getCommonResourcePath(path.join('models', modelFileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ export async function copyPreCacheFromResources(): Promise<boolean> {
|
|||||||
path.join(app.getAppPath(), 'voice-preview-cache'),
|
path.join(app.getAppPath(), 'voice-preview-cache'),
|
||||||
path.join(app.getAppPath().replace(/\.asar$/, '.asar.unpacked'), 'voice-preview-cache'),
|
path.join(app.getAppPath().replace(/\.asar$/, '.asar.unpacked'), 'voice-preview-cache'),
|
||||||
// 开发环境
|
// 开发环境
|
||||||
path.join(process.cwd(), 'electron/resources/voice-preview-cache'),
|
path.join(process.cwd(), 'packaging/resources/voice-preview-cache'),
|
||||||
// 备选路径
|
// 备选路径
|
||||||
path.join(process.resourcesPath, 'voice-preview-cache'),
|
path.join(process.resourcesPath, 'voice-preview-cache'),
|
||||||
path.join(process.resourcesPath, '../voice-preview-cache'),
|
path.join(process.resourcesPath, '../voice-preview-cache'),
|
||||||
|
|||||||
@@ -144,8 +144,12 @@ function getZitiDir(): string {
|
|||||||
console.warn('[FontManager] 所有 ziti 路径都不存在,返回默认路径');
|
console.warn('[FontManager] 所有 ziti 路径都不存在,返回默认路径');
|
||||||
return path.join(fallbackRoot, 'resources-bundles', 'ziti');
|
return path.join(fallbackRoot, 'resources-bundles', 'ziti');
|
||||||
} else {
|
} else {
|
||||||
// 开发环境:ziti 在项目根目录
|
// 开发环境:ziti 在 packaging/vendor/ziti
|
||||||
const appRoot = getAppRoot();
|
const appRoot = getAppRoot();
|
||||||
|
const devZitiDir = path.join(appRoot, 'packaging', 'vendor', 'ziti');
|
||||||
|
if (fs.existsSync(devZitiDir)) {
|
||||||
|
return devZitiDir;
|
||||||
|
}
|
||||||
return path.join(appRoot, 'ziti');
|
return path.join(appRoot, 'ziti');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,7 +255,11 @@ function scanZitiFonts(): any[] {
|
|||||||
*/
|
*/
|
||||||
function getBundledFonts(): any[] {
|
function getBundledFonts(): any[] {
|
||||||
const appRoot = getAppRoot();
|
const appRoot = getAppRoot();
|
||||||
const metadataFile = path.join(appRoot, 'fonts', 'bundled', 'fonts_metadata.json');
|
const metadataCandidates = [
|
||||||
|
path.join(appRoot, 'packaging', 'vendor', 'fonts', 'bundled', 'fonts_metadata.json'),
|
||||||
|
path.join(appRoot, 'fonts', 'bundled', 'fonts_metadata.json'),
|
||||||
|
];
|
||||||
|
const metadataFile = metadataCandidates.find(file => fs.existsSync(file)) || metadataCandidates[0];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(metadataFile)) {
|
if (!fs.existsSync(metadataFile)) {
|
||||||
@@ -554,6 +562,9 @@ function getBundledFontDirs(): string[] {
|
|||||||
pushIfExists(path.join(bundleRoot, 'fonts', 'ziti'));
|
pushIfExists(path.join(bundleRoot, 'fonts', 'ziti'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pushIfExists(path.join(appRoot, 'packaging', 'vendor', 'ziti'));
|
||||||
|
pushIfExists(path.join(appRoot, 'packaging', 'vendor', 'fonts'));
|
||||||
|
pushIfExists(path.join(appRoot, 'packaging', 'vendor', 'fonts', 'ziti'));
|
||||||
pushIfExists(path.join(appRoot, 'ziti'));
|
pushIfExists(path.join(appRoot, 'ziti'));
|
||||||
pushIfExists(path.join(appRoot, 'resources-bundles', 'ziti'));
|
pushIfExists(path.join(appRoot, 'resources-bundles', 'ziti'));
|
||||||
pushIfExists(path.join(appRoot, 'resources-bundles', 'fonts'));
|
pushIfExists(path.join(appRoot, 'resources-bundles', 'fonts'));
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ function getBundledZitiDirs(): string[] {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
|
pushIfExists(path.join(process.env.APP_ROOT || process.cwd(), 'packaging', 'vendor', 'ziti'));
|
||||||
pushIfExists(path.join(process.env.APP_ROOT || process.cwd(), 'ziti'));
|
pushIfExists(path.join(process.env.APP_ROOT || process.cwd(), 'ziti'));
|
||||||
return dirs;
|
return dirs;
|
||||||
}
|
}
|
||||||
@@ -4875,14 +4876,17 @@ ${subtitleText}
|
|||||||
let defaultPath = "";
|
let defaultPath = "";
|
||||||
|
|
||||||
// 尝试查找bgm目录
|
// 尝试查找bgm目录
|
||||||
// 开发环境: 项目根目录/bgm
|
// 开发环境: packaging/vendor/bgm
|
||||||
// 生产环境: resources/app.asar.unpacked/bgm (由asarUnpack解压)
|
// 生产环境: resources-bundles/bgm
|
||||||
const possiblePaths = [
|
const possiblePaths = [
|
||||||
|
path.join(process.cwd(), 'packaging', 'vendor', 'bgm'),
|
||||||
|
AppEnv.resourceBundleRoot ? path.join(AppEnv.resourceBundleRoot, 'bgm') : '',
|
||||||
|
path.join(process.cwd(), 'resources-bundles', 'bgm'),
|
||||||
path.join(process.cwd(), 'bgm'),
|
path.join(process.cwd(), 'bgm'),
|
||||||
path.join(process.resourcesPath, 'app.asar.unpacked', 'bgm'),
|
path.join(process.resourcesPath, 'app.asar.unpacked', 'bgm'),
|
||||||
path.join(process.resourcesPath, 'bgm'),
|
path.join(process.resourcesPath, 'bgm'),
|
||||||
path.join(app.getAppPath(), 'bgm')
|
path.join(app.getAppPath(), 'bgm')
|
||||||
];
|
].filter(Boolean);
|
||||||
|
|
||||||
for (const p of possiblePaths) {
|
for (const p of possiblePaths) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { app } from 'electron';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 音效 ID 到文件名的映射
|
* 音效 ID 到文件名的映射
|
||||||
* 这些音效文件存储在 resources/extra/common/soundEffects/ 目录下
|
* 这些音效文件存储在 packaging/resources/common/soundEffects/ 目录下
|
||||||
*/
|
*/
|
||||||
export const SOUND_EFFECT_FILES: Record<string, string> = {
|
export const SOUND_EFFECT_FILES: Record<string, string> = {
|
||||||
// 原有音效
|
// 原有音效
|
||||||
@@ -81,7 +81,7 @@ export function getSoundEffectsDir(): string {
|
|||||||
const isDev = !app.isPackaged;
|
const isDev = !app.isPackaged;
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
// 开发模式:使用 app.getAppPath() 获取项目根目录
|
// 开发模式:使用 app.getAppPath() 获取项目根目录
|
||||||
return path.join(app.getAppPath(), 'electron/resources/extra/common/soundEffects');
|
return path.join(app.getAppPath(), 'packaging/resources/common/soundEffects');
|
||||||
} else {
|
} else {
|
||||||
// 生产模式:使用 process.resourcesPath
|
// 生产模式:使用 process.resourcesPath
|
||||||
return path.join(process.resourcesPath, 'extra/common/soundEffects');
|
return path.join(process.resourcesPath, 'extra/common/soundEffects');
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"dev:electron": "concurrently \"set AUTH_SERVER_URL=http://127.0.0.1:3302&& set VITE_DEV_SERVER_URL=http://127.0.0.1:3354&& set EXTERNAL_ELECTRON_DEV=1&& vite --host 127.0.0.1 --port 3354 --strictPort\" \"set AUTH_SERVER_URL=http://127.0.0.1:3302&& set VITE_DEV_SERVER_URL=http://127.0.0.1:3354&& node scripts/dev-electron.cjs\"",
|
"dev:electron": "concurrently \"set AUTH_SERVER_URL=http://127.0.0.1:3302&& set VITE_DEV_SERVER_URL=http://127.0.0.1:3354&& set EXTERNAL_ELECTRON_DEV=1&& vite --host 127.0.0.1 --port 3354 --strictPort\" \"set AUTH_SERVER_URL=http://127.0.0.1:3302&& set VITE_DEV_SERVER_URL=http://127.0.0.1:3354&& node scripts/dev-electron.cjs\"",
|
||||||
"prepare-package": "node apply-oem.cjs && py -3 scripts/export-runtime-system-templates.py && node scripts/prepare-package.cjs",
|
"prepare-package": "node apply-oem.cjs && py -3 scripts/export-runtime-system-templates.py && node scripts/prepare-package.cjs",
|
||||||
"build:win": "npm run prepare-package && npm run build && node scripts/build-resource-artifacts.cjs && electron-builder --win --x64 && node scripts/fix-latest-yml.cjs && node scripts/assemble-release-artifacts.cjs",
|
"build:win": "npm run prepare-package && npm run build && node scripts/build-resource-artifacts.cjs && electron-builder --win --x64 && node scripts/fix-latest-yml.cjs && node scripts/assemble-release-artifacts.cjs",
|
||||||
|
"build:client:win": "node scripts/package-client.cjs",
|
||||||
"build:win:only": "electron-builder --win --x64",
|
"build:win:only": "electron-builder --win --x64",
|
||||||
"build:mac": "npm run prepare-package && npm run typecheck && vite build && electron-builder --mac",
|
"build:mac": "npm run prepare-package && npm run typecheck && vite build && electron-builder --mac",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Packaging Layout
|
||||||
|
|
||||||
|
`packaging/` keeps build-time assets separate from application source code.
|
||||||
|
|
||||||
|
## Directories
|
||||||
|
|
||||||
|
- `build-resources/`: Electron Builder icons and installer assets. Small icon files are source-controlled. Large redistributables such as `vc_redist*.exe` stay local and are ignored.
|
||||||
|
- `resources/`: Files copied into the packaged app as `resources/extra`, such as config files, cover templates, tray assets, sound effects, and voice preview cache metadata.
|
||||||
|
- `vendor/`: Local packaging dependencies and large runtime bundles, including Playwright, Python runtime, FFmpeg, models, fonts, ziti, and BGM. This directory is intentionally ignored by Git.
|
||||||
|
|
||||||
|
## Restore Local Vendor Assets
|
||||||
|
|
||||||
|
Use the existing preparation scripts when a fresh checkout needs local packaging dependencies:
|
||||||
|
|
||||||
|
```bat
|
||||||
|
npm.cmd run prepare-package
|
||||||
|
node scripts\copy-playwright-browser.cjs
|
||||||
|
```
|
||||||
|
|
||||||
|
Dependency download helpers write into `packaging/vendor` instead of the project root.
|
||||||
|
After Width: | Height: | Size: 279 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 279 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
@@ -0,0 +1,721 @@
|
|||||||
|
{
|
||||||
|
"version": "1.2",
|
||||||
|
"timestamp": "2026-01-10T04:12:13.221Z",
|
||||||
|
"ipAgentSettings": {
|
||||||
|
"textModelKey": "builtInDoubao|doubao-seed-2-0-mini-260215",
|
||||||
|
"voiceModelKey": "",
|
||||||
|
"voicePromptId": 7,
|
||||||
|
"asrMode": "online",
|
||||||
|
"asrModelKey": "",
|
||||||
|
"voiceMode": "online",
|
||||||
|
"videoDigitalHumanModelKey": "__CLOUD__",
|
||||||
|
"scriptPrompt": "你是一个视频口播专家,擅长根据用户给的主题和关键词生成高质量的口播稿件。\n请根据以下要求生成口播稿件:\n\n1. 主题:{title}\n2. 字数:大约{count}字\n\n请确保稿件内容连贯、有吸引力,并且符合口播的风格。\n请直接输出生成的口播稿件,无需任何额外说明文字。\n\n可用变量:{title}、{count}",
|
||||||
|
"titleTagPrompt": "是短视频标题与标签助手。请基于提供的口播文案,生成一个标题和3-4个标签,输出的语言要完全是我们文案的语言格式\n- 标题不超过15个字,简洁有吸引力,不加引号,不含表情或图标字符。\n- 标签为2-6个字的中文短词,不含#、空格和表情;以主题词为主,避免冗长句。\n- 然后从文案里筛选出优秀的的词汇,必须是文案里包含的一字不差的,按照以下分类\n * 重点词/成语词:文案中的核心词汇、成语、重要概念。\n * 描述词:文案中用于描述事物特征、状态的优秀词汇。\n * 行动词:文案中表示动作、行为的优秀词汇。\n * 情感词:文案中表达情感、情绪的优秀词汇。\n\n-- 直接输出纯JSON格式,如果没有合适的词,[]格式里面可以留空,不要生凑,不要一个字的词,不要使用markdown代码块(不要用),不要附加任何说明文字。\n\n- 输出格式为:\n{\n \"title\": \"...\",\n \"tags\": [\"...\", \"...\", \"...\", \"...\"],\n \"keywords\": {\n \"重点词/成语词\": [\"...\", \"...\"],\n \"描述词\": [\"...\"],\n \"行动词\": [\"...\"],\n \"情感词\": [\"...\",]\n }\n}\n\n\n文案:<content>{{content}}</content>",
|
||||||
|
"topicRewritePrompt": "参考以下标题,帮我生成5个新的标题。\n\n参考标题:\n{{content}}\n\n要求:\n1. 保持相似的主题和风格\n2. 标题简洁有力,吸引眼球\n3. 不要添加表情符号和#标签\n4. 每个标题独立成行\n\n请直接输出5个新标题,不要编号和额外说明。\n可用变量:{{content}}",
|
||||||
|
"rewritePrompt": "# 你是一位文案改写助手,不增加表情或特殊符号,输出{{count}}字。\n原文:\n{{content}}\n\n\n ## 写作限制和特征\n -禁止输出的词:“原文”“作者”“文章”等词,攻击性强、侮辱性强的词(滚吧、找爸爸去、废物)、骂人的词(傻子、垃圾、蠢蛋),ai常用词(如“这波操作”、“666”)\n -用词口语化,句式有短句、情绪浓烈,\n -不要输出前言和介绍,也不能直白的指责。\n -写作的内容不要引用提示词的要求。\n\n ## 写作要求\n 1、第一段直接照抄原文的第一句话作为文章开头,一个字也不能改,必须控制30个字以内,绝对禁止照抄的第一句话超过30个字,超过30个字在不影响句子连贯的情况下截停。\n\n 2、后面的内容要具体叙述具体行为,每一个具体动作都不能跳过,可以直接使用原文句子,但是不能完全一致,从原文案的内容里面,用不同的角度切入叙述,确保内容完整,可以使用同义字进行替换,重组结构。\n\n 3、最后一段可以直接使用原文的最后一段,如果有身份信息的内容,就不要写出来,**例如:我是某某,后续内容主页观看。**",
|
||||||
|
"subtitleHighlightWords": "",
|
||||||
|
"bgmDirectory": "",
|
||||||
|
"enableSentimentAnalysis": false,
|
||||||
|
"sentimentModelKey": "zhipu|glm-4-flash",
|
||||||
|
"sentimentTemplate": "auto",
|
||||||
|
"sentimentEffectIntensity": 70,
|
||||||
|
"enableSentimentAnimation": true,
|
||||||
|
"sentimentColors": {
|
||||||
|
"positive": "#00FF00",
|
||||||
|
"negative": "#FF0000",
|
||||||
|
"neutral": "#FFFFFF",
|
||||||
|
"emphasis": "#FFFF00"
|
||||||
|
},
|
||||||
|
"minSubtitleDuration": 2,
|
||||||
|
"normalSubtitleX": 50,
|
||||||
|
"normalSubtitleY": 15,
|
||||||
|
"effectSubtitleX": 50,
|
||||||
|
"effectSubtitleY": 50,
|
||||||
|
"disableSmartLayout": false,
|
||||||
|
"keywordGroupPositions": [
|
||||||
|
{
|
||||||
|
"groupName": "行动词",
|
||||||
|
"effectX": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "情感词",
|
||||||
|
"effectX": 50,
|
||||||
|
"effectY": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "描述词",
|
||||||
|
"effectX": 46,
|
||||||
|
"effectY": 63
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "重点词/成语词",
|
||||||
|
"effectX": 50,
|
||||||
|
"effectY": 89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "12"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"avoidanceConfig": {
|
||||||
|
"safeDistance": 20,
|
||||||
|
"preferDirection": "right"
|
||||||
|
},
|
||||||
|
"silenceThreshold": -40,
|
||||||
|
"silenceDuration": 1,
|
||||||
|
"minPauseDuration": 0.15,
|
||||||
|
"videoDigitalHumanMode": "local"
|
||||||
|
},
|
||||||
|
"prompts": {
|
||||||
|
"scriptPrompt": "你是一个视频口播专家,擅长根据用户给的主题和关键词生成高质量的口播稿件。\n请根据以下要求生成口播稿件:\n\n1. 主题:{title}\n2. 字数:大约{count}字\n\n请确保稿件内容连贯、有吸引力,并且符合口播的风格。\n请直接输出生成的口播稿件,无需任何额外说明文字。\n\n可用变量:{title}、{count}",
|
||||||
|
"titleTagPrompt": "是短视频标题与标签助手。请基于提供的口播文案,生成一个标题和3-4个标签,输出的语言要完全是我们文案的语言格式\n- 标题不超过15个字,简洁有吸引力,不加引号,不含表情或图标字符。\n- 标签为2-6个字的中文短词,不含#、空格和表情;以主题词为主,避免冗长句。\n- 然后从文案里筛选出优秀的的词汇,必须是文案里包含的一字不差的,按照以下分类\n * 重点词/成语词:文案中的核心词汇、成语、重要概念。\n * 描述词:文案中用于描述事物特征、状态的优秀词汇。\n * 行动词:文案中表示动作、行为的优秀词汇。\n * 情感词:文案中表达情感、情绪的优秀词汇。\n\n-- 直接输出纯JSON格式,如果没有合适的词,[]格式里面可以留空,不要生凑,不要一个字的词,不要使用markdown代码块(不要用),不要附加任何说明文字。\n\n- 输出格式为:\n{\n \"title\": \"...\",\n \"tags\": [\"...\", \"...\", \"...\", \"...\"],\n \"keywords\": {\n \"重点词/成语词\": [\"...\", \"...\"],\n \"描述词\": [\"...\"],\n \"行动词\": [\"...\"],\n \"情感词\": [\"...\",]\n }\n}\n\n\n文案:<content>{{content}}</content>",
|
||||||
|
"topicRewritePrompt": "参考以下标题,帮我生成5个新的标题。\n\n参考标题:\n{{content}}\n\n要求:\n1. 保持相似的主题和风格\n2. 标题简洁有力,吸引眼球\n3. 不要添加表情符号和#标签\n4. 每个标题独立成行\n\n请直接输出5个新标题,不要编号和额外说明。\n可用变量:{{content}}",
|
||||||
|
"rewritePrompt": "# 你是一位文案改写助手,不增加表情或特殊符号,输出{{count}}字。\n原文:\n{{content}}\n\n\n ## 写作限制和特征\n -禁止输出的词:“原文”“作者”“文章”等词,攻击性强、侮辱性强的词(滚吧、找爸爸去、废物)、骂人的词(傻子、垃圾、蠢蛋),ai常用词(如“这波操作”、“666”)\n -用词口语化,句式有短句、情绪浓烈,\n -不要输出前言和介绍,也不能直白的指责。\n -写作的内容不要引用提示词的要求。\n\n ## 写作要求\n 1、第一段直接照抄原文的第一句话作为文章开头,一个字也不能改,必须控制30个字以内,绝对禁止照抄的第一句话超过30个字,超过30个字在不影响句子连贯的情况下截停。\n\n 2、后面的内容要具体叙述具体行为,每一个具体动作都不能跳过,可以直接使用原文句子,但是不能完全一致,从原文案的内容里面,用不同的角度切入叙述,确保内容完整,可以使用同义字进行替换,重组结构。\n\n 3、最后一段可以直接使用原文的最后一段,如果有身份信息的内容,就不要写出来,**例如:我是某某,后续内容主页观看。**",
|
||||||
|
"voicePromptId": 7,
|
||||||
|
"audioVoicePromptId": 7,
|
||||||
|
"audioLanguage": "中文"
|
||||||
|
},
|
||||||
|
"videoMixCutSettings": {
|
||||||
|
"enabled": true,
|
||||||
|
"mode": "fixed",
|
||||||
|
"fixedModeConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"opening": {
|
||||||
|
"enabled": false,
|
||||||
|
"timeRange": {
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5
|
||||||
|
},
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "top-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"middle": {
|
||||||
|
"enabled": false,
|
||||||
|
"timeRange": {
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5
|
||||||
|
},
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-left",
|
||||||
|
"pipSizePercent": 40,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ending": {
|
||||||
|
"enabled": false,
|
||||||
|
"timeRange": {
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5
|
||||||
|
},
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keywordModeConfig": {
|
||||||
|
"enabled": false,
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"groupId": "group_action",
|
||||||
|
"groupName": "行动词",
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5,
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"enabled": false,
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupId": "group_emotion",
|
||||||
|
"groupName": "情感词",
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5,
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"enabled": false,
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupId": "group_description",
|
||||||
|
"groupName": "描述词",
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5,
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"enabled": false,
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupId": "group_keyemphasis",
|
||||||
|
"groupName": "重点词/成语词",
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5,
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"enabled": false,
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupId": "group_12",
|
||||||
|
"groupName": "12",
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5,
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"enabled": false,
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subtitleTemplates": [
|
||||||
|
{
|
||||||
|
"id": "template_system_11",
|
||||||
|
"name": "11",
|
||||||
|
"description": "自定义字幕模板 - 黑底白字,支持关键词特效",
|
||||||
|
"createdAt": 1767950393391,
|
||||||
|
"config": {
|
||||||
|
"subtitleStyleId": "system-subtitle-11",
|
||||||
|
"subtitleStyle": {
|
||||||
|
"id": "custom",
|
||||||
|
"name": "自定义样式",
|
||||||
|
"description": "完全自定义的字幕样式",
|
||||||
|
"preview": "示例文字",
|
||||||
|
"fontName": "Noto Serif CJK",
|
||||||
|
"fontSize": 48,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 3,
|
||||||
|
"shadowOffset": 2,
|
||||||
|
"shadowColor": "#000000",
|
||||||
|
"shadowBlur": 4,
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"backgroundOpacity": 0,
|
||||||
|
"position": "bottom",
|
||||||
|
"alignment": "center",
|
||||||
|
"fontSource": "bundled"
|
||||||
|
},
|
||||||
|
"subtitleFontSizeScale": 100,
|
||||||
|
"subtitlePosition": "bottom",
|
||||||
|
"enableKeywordEffects": true,
|
||||||
|
"keywordGroups": [
|
||||||
|
{
|
||||||
|
"name": "12",
|
||||||
|
"description": "模板分组12",
|
||||||
|
"color": "#FF6B6B",
|
||||||
|
"effectId": "none",
|
||||||
|
"keywords": [],
|
||||||
|
"styleOverride": {},
|
||||||
|
"effectConfig": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "重点词/成语词",
|
||||||
|
"description": "视频中的重点词汇和成语",
|
||||||
|
"color": "#EF0707EC",
|
||||||
|
"effectId": "fly-in",
|
||||||
|
"soundEffectId": "chain-pull",
|
||||||
|
"keywords": [],
|
||||||
|
"styleOverride": {
|
||||||
|
"fontName": "字玩哥特黑白无常 免费 超黑",
|
||||||
|
"fontSize": 80,
|
||||||
|
"fontColor": "#EF0707EC",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 10
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 1.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "描述词",
|
||||||
|
"description": "描述性的形容词和副词",
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"effectId": "intense-flash",
|
||||||
|
"soundEffectId": "pop-1",
|
||||||
|
"keywords": [],
|
||||||
|
"styleOverride": {
|
||||||
|
"fontName": "USMCCyuanjiantecu",
|
||||||
|
"fontSize": 64,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 1
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "情感词",
|
||||||
|
"description": "表达情感的词汇",
|
||||||
|
"color": "#FEFEFE",
|
||||||
|
"effectId": "danmaku",
|
||||||
|
"soundEffectId": "cash-register",
|
||||||
|
"keywords": [],
|
||||||
|
"styleOverride": {
|
||||||
|
"fontName": "心朵儿体",
|
||||||
|
"fontSize": 68,
|
||||||
|
"fontColor": "#FEFEFE",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 0
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行动词",
|
||||||
|
"description": "需要强调的行动动词",
|
||||||
|
"color": "#6CC4F6",
|
||||||
|
"effectId": "ARC_TEXT",
|
||||||
|
"soundEffectId": "knife-scrape",
|
||||||
|
"keywords": [],
|
||||||
|
"styleOverride": {
|
||||||
|
"fontName": "USMCCyuanjiantecu",
|
||||||
|
"fontSize": 200,
|
||||||
|
"fontColor": "#6CC4F6",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 3
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"screenWidth": 1920,
|
||||||
|
"screenHeight": 1080,
|
||||||
|
"fontSize": 48,
|
||||||
|
"fontFamily": "Arial, sans-serif",
|
||||||
|
"keywordGroupsStyles": [
|
||||||
|
{
|
||||||
|
"groupName": "行动词",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 3,
|
||||||
|
"fontSize": 68,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectId": "intense-flash",
|
||||||
|
"effectDuration": 2,
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"soundEffectId": "magic-sfx",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "情感词",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#1D69DE",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 0,
|
||||||
|
"fontSize": 192,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectId": "ARC_TEXT",
|
||||||
|
"effectDuration": 2,
|
||||||
|
"color": "#1D69DE",
|
||||||
|
"soundEffectId": "swoosh",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "描述词",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#9ACAF9",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 1,
|
||||||
|
"fontSize": 48,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectId": "danmaku",
|
||||||
|
"effectDuration": 2,
|
||||||
|
"color": "#9ACAF9",
|
||||||
|
"soundEffectId": "cash-register",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "重点词/成语词",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#EF0707EC",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 10,
|
||||||
|
"fontSize": 104,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectId": "fly-in",
|
||||||
|
"effectDuration": 2,
|
||||||
|
"color": "#EF0707EC",
|
||||||
|
"soundEffectId": "blow-impact",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "12",
|
||||||
|
"styleOverride": null,
|
||||||
|
"effectId": null,
|
||||||
|
"effectDuration": null,
|
||||||
|
"color": null,
|
||||||
|
"soundEffectId": null,
|
||||||
|
"soundEffectConfig": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"isSystem": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frontendState": {
|
||||||
|
"selectedSubtitleTemplate": "template_system_11",
|
||||||
|
"keywordGroups": [
|
||||||
|
{
|
||||||
|
"id": "group_action",
|
||||||
|
"name": "行动词",
|
||||||
|
"description": "用于表达行动和进度",
|
||||||
|
"keywords": [
|
||||||
|
{
|
||||||
|
"keyword": "重仓",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "预判",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "认准",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "聚焦",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "兑现",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "离场",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"effectId": "intense-flash",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 3,
|
||||||
|
"fontSize": 68,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 2
|
||||||
|
},
|
||||||
|
"styleConfig": {
|
||||||
|
"fontSize": 64,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 2
|
||||||
|
},
|
||||||
|
"createdAt": 1767519506523,
|
||||||
|
"updatedAt": 1768017351366,
|
||||||
|
"sound_effect_id": "magic-sfx",
|
||||||
|
"sound_effect_config": "{\"volume\":0.7,\"pitch\":1,\"fadeIn\":0,\"fadeOut\":50,\"triggerTime\":\"start\"}",
|
||||||
|
"stickerId": null,
|
||||||
|
"effectDuration": 2,
|
||||||
|
"soundEffectId": "magic-sfx",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group_emotion",
|
||||||
|
"name": "情感词",
|
||||||
|
"description": "用于表达情感",
|
||||||
|
"keywords": [
|
||||||
|
{
|
||||||
|
"keyword": "慌了",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "狂揽",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "不舒服",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color": "#1D69DE",
|
||||||
|
"effectId": "ARC_TEXT",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#1D69DE",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 0,
|
||||||
|
"fontSize": 192,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 3
|
||||||
|
},
|
||||||
|
"styleConfig": {
|
||||||
|
"fontSize": 64,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 2
|
||||||
|
},
|
||||||
|
"createdAt": 1767519506517,
|
||||||
|
"updatedAt": 1768017351438,
|
||||||
|
"sound_effect_id": "swoosh",
|
||||||
|
"sound_effect_config": "{\"volume\":0.7,\"pitch\":1,\"fadeIn\":0,\"fadeOut\":50,\"triggerTime\":\"start\"}",
|
||||||
|
"stickerId": null,
|
||||||
|
"effectDuration": 2,
|
||||||
|
"soundEffectId": "swoosh",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group_description",
|
||||||
|
"name": "描述词",
|
||||||
|
"description": "用于描述场景和事物",
|
||||||
|
"keywords": [
|
||||||
|
{
|
||||||
|
"keyword": "果断",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "极致",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "无敌",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color": "#9ACAF9",
|
||||||
|
"effectId": "danmaku",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#9ACAF9",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 1,
|
||||||
|
"fontSize": 48,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 2
|
||||||
|
},
|
||||||
|
"styleConfig": {
|
||||||
|
"fontSize": 64,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 2
|
||||||
|
},
|
||||||
|
"createdAt": 1767519506509,
|
||||||
|
"updatedAt": 1768017351477,
|
||||||
|
"sound_effect_id": "cash-register",
|
||||||
|
"sound_effect_config": "{\"volume\":0.7,\"pitch\":1,\"fadeIn\":0,\"fadeOut\":50,\"triggerTime\":\"start\"}",
|
||||||
|
"stickerId": null,
|
||||||
|
"effectDuration": 2,
|
||||||
|
"soundEffectId": "cash-register",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group_keyemphasis",
|
||||||
|
"name": "重点词/成语词",
|
||||||
|
"description": "用于强调关键词汇和成语",
|
||||||
|
"keywords": [
|
||||||
|
{
|
||||||
|
"keyword": "核心优势",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "护城河",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "投资逻辑",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyword": "价值标的",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color": "#EF0707EC",
|
||||||
|
"effectId": "fly-in",
|
||||||
|
"styleOverride": {
|
||||||
|
"fontColor": "#EF0707EC",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 10,
|
||||||
|
"fontSize": 104,
|
||||||
|
"fontName": "Noto Serif CJK"
|
||||||
|
},
|
||||||
|
"effectConfig": {
|
||||||
|
"duration": 1.5
|
||||||
|
},
|
||||||
|
"styleConfig": {
|
||||||
|
"fontSize": 64,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 2
|
||||||
|
},
|
||||||
|
"createdAt": 1767519506503,
|
||||||
|
"updatedAt": 1768017351515,
|
||||||
|
"sound_effect_id": "blow-impact",
|
||||||
|
"sound_effect_config": "{\"volume\":0.7,\"pitch\":1,\"fadeIn\":0,\"fadeOut\":50,\"triggerTime\":\"start\"}",
|
||||||
|
"stickerId": null,
|
||||||
|
"effectDuration": 2,
|
||||||
|
"soundEffectId": "blow-impact",
|
||||||
|
"soundEffectConfig": {
|
||||||
|
"volume": 0.7,
|
||||||
|
"pitch": 1,
|
||||||
|
"fadeIn": 0,
|
||||||
|
"fadeOut": 50,
|
||||||
|
"triggerTime": "start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group_12",
|
||||||
|
"name": "12",
|
||||||
|
"description": "模板分组12",
|
||||||
|
"keywords": [],
|
||||||
|
"color": null,
|
||||||
|
"effectId": null,
|
||||||
|
"effectConfig": {},
|
||||||
|
"styleConfig": {
|
||||||
|
"fontSize": 64,
|
||||||
|
"fontColor": "#FFFFFF",
|
||||||
|
"outlineColor": "#000000",
|
||||||
|
"outlineWidth": 2
|
||||||
|
},
|
||||||
|
"createdAt": 1767519506497,
|
||||||
|
"updatedAt": 1768017351556,
|
||||||
|
"sound_effect_id": null,
|
||||||
|
"sound_effect_config": null,
|
||||||
|
"stickerId": null,
|
||||||
|
"effectDuration": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"enableKeywordEffects": true,
|
||||||
|
"enableSmartSubtitle": true,
|
||||||
|
"enableSilenceRemoval": false,
|
||||||
|
"subtitleFontSizeScale": 100,
|
||||||
|
"subtitlePosition": "bottom",
|
||||||
|
"enableBGM": true,
|
||||||
|
"bgmVolume": 30,
|
||||||
|
"videoTemplateId": 7,
|
||||||
|
"enableTopTitle": false,
|
||||||
|
"topTitleStyle": {
|
||||||
|
"fontSize": 48,
|
||||||
|
"fontColor": "#FFD700",
|
||||||
|
"backgroundColor": "#000000",
|
||||||
|
"backgroundOpacity": 0.7,
|
||||||
|
"paddingHorizontal": 20,
|
||||||
|
"paddingVertical": 10,
|
||||||
|
"borderRadius": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
|
||||||
|
<!--
|
||||||
|
Minimal fontconfig configuration for FFmpeg
|
||||||
|
This file prevents "Cannot load default config file" errors
|
||||||
|
and allows drawtext filter to work with fontfile parameter
|
||||||
|
-->
|
||||||
|
<fontconfig>
|
||||||
|
<!-- Font directories - use prefix="cwd" to avoid ambiguous path warning -->
|
||||||
|
<dir prefix="cwd">.</dir>
|
||||||
|
|
||||||
|
<!-- Disable font matching since we use explicit fontfile paths -->
|
||||||
|
<match target="pattern">
|
||||||
|
<edit name="family" mode="append" binding="weak">
|
||||||
|
<string>sans-serif</string>
|
||||||
|
</edit>
|
||||||
|
</match>
|
||||||
|
|
||||||
|
<!-- Cache configuration - resolved from XDG_CACHE_HOME at runtime -->
|
||||||
|
<cachedir prefix="xdg">fontconfig-cache</cachedir>
|
||||||
|
|
||||||
|
<!-- Minimal config to satisfy fontconfig initialization -->
|
||||||
|
<config>
|
||||||
|
<rescan>
|
||||||
|
<int>0</int>
|
||||||
|
</rescan>
|
||||||
|
</config>
|
||||||
|
</fontconfig>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"name": "简约风格",
|
||||||
|
"description": "简洁大方的黑色半透明背景,适合各类视频",
|
||||||
|
"config": {
|
||||||
|
"style": "default",
|
||||||
|
"fontSize": 80,
|
||||||
|
"fontColor": [255, 255, 255],
|
||||||
|
"fontName": "NotoSerifCJK-VF", "fontPath": "",
|
||||||
|
"position": "top",
|
||||||
|
"maxWidth": 1920
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "描边风格",
|
||||||
|
"description": "白色文字配黑色描边,清晰醒目",
|
||||||
|
"config": {
|
||||||
|
"style": "outline",
|
||||||
|
"fontSize": 90,
|
||||||
|
"fontColor": [255, 255, 255],
|
||||||
|
"outlineColor": [0, 0, 0],
|
||||||
|
"fontName": "NotoSerifCJK-VF", "fontPath": "",
|
||||||
|
"position": "top",
|
||||||
|
"maxWidth": 1920
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "模糊背景",
|
||||||
|
"description": "背景模糊效果,突出标题文字",
|
||||||
|
"config": {
|
||||||
|
"style": "blur_bg",
|
||||||
|
"fontSize": 85,
|
||||||
|
"fontColor": [255, 255, 255],
|
||||||
|
"fontName": "NotoSerifCJK-VF", "fontPath": "",
|
||||||
|
"position": "center",
|
||||||
|
"maxWidth": 1920
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "渐变遮罩",
|
||||||
|
"description": "优雅的渐变半透明遮罩效果",
|
||||||
|
"config": {
|
||||||
|
"style": "gradient",
|
||||||
|
"fontSize": 75,
|
||||||
|
"fontColor": [255, 255, 255],
|
||||||
|
"fontName": "NotoSerifCJK-VF", "fontPath": "",
|
||||||
|
"position": "top",
|
||||||
|
"maxWidth": 1920
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "分栏设计",
|
||||||
|
"description": "左侧标题配右侧装饰,设计感十足",
|
||||||
|
"config": {
|
||||||
|
"style": "split",
|
||||||
|
"fontSize": 70,
|
||||||
|
"fontColor": [255, 255, 255],
|
||||||
|
"fontName": "NotoSerifCJK-VF", "fontPath": "",
|
||||||
|
"position": "top",
|
||||||
|
"accentText": "✨",
|
||||||
|
"maxWidth": 1920
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 944 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 800 KiB |
|
After Width: | Height: | Size: 726 KiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.3 MiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 4.0 MiB |
|
After Width: | Height: | Size: 3.6 MiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.3 MiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 4.0 MiB |
|
After Width: | Height: | Size: 3.6 MiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"prompts": {
|
||||||
|
"scriptPrompt": "你是一个视频口播专家,擅长根据用户给的主题和关键词生成高质量的口播稿件。\n请根据以下要求生成口播稿件:\n\n1. 主题:{title}\n2. 字数:大约{count}字\n\n请确保稿件内容连贯、有吸引力,并且符合口播的风格。\n请直接输出生成的口播稿件,无需任何额外说明文字。\n不要带任何表情和符号。\n禁止输出的词:\"原文\"\"作者\"\"文章\"等词,攻击性强、侮辱性强的词(滚吧、找爸爸去、废物)、骂人的词(傻子、垃圾、蠢蛋),ai常用词(如\"这波操作\"、\"666\"),极限词、违反法律的词汇\n\n可用变量:{title}、{count}",
|
||||||
|
"titleTagPrompt": "是短视频标题与标签助手。请基于提供的口播文案,生成一个标题和3-4个标签,输出的语言要完全是我们文案的语言格式\n- 标题不超过15个字,简洁有吸引力,不加引号,不含表情或图标字符。\n- 标签为2-6个字的中文短词,不含#、空格和表情;以主题词为主,避免冗长句。\n- 然后从文案里筛选出优秀的的词汇,必须是文案里包含的一字不差的,按照以下分类\n * 重点词/成语词:文案中的核心词汇、成语、重要概念。\n * 描述词:文案中用于描述事物特征、状态的优秀词汇。\n * 行动词:文案中表示动作、行为的优秀词汇。\n * 情感词:文案中表达情感、情绪的优秀词汇。\n\n-- 直接输出纯JSON格式,如果没有合适的词,[]格式里面可以留空,不要生凑,不要一个字的词,不要使用markdown代码块(不要用),不要附加任何说明文字。\n\n- 输出格式为:\n{\n \"title\": \"...\",\n \"tags\": [\"...\", \"...\", \"...\", \"...\"],\n \"keywords\": {\n \"重点词/成语词\": [\"...\", \"...\"],\n \"描述词\": [\"...\"],\n \"行动词\": [\"...\"],\n \"情感词\": [\"...\",]\n }\n}\n\n\n文案:<content>{{content}}</content>\n可用变量:{{content}}",
|
||||||
|
"topicRewritePrompt": "参考以下标题,帮我生成5个新的标题。\n\n参考标题:\n{{content}}\n\n要求:\n1. 保持相似的主题和风格\n2. 标题简洁有力,吸引眼球\n3. 不要添加表情符号和#标签\n4. 每个标题独立成行\n\n请直接输出5个新标题,不要编号和额外说明。\n可用变量:{{content}}",
|
||||||
|
"rewritePrompt": "你是一位文案改写助手,不增加表情或特殊符号,输出{{count}}字。\n原文:\n{{content}}\n\n\n ## 写作限制和特征\n -禁止输出的词:\"原文\"\"作者\"\"文章\"等词,攻击性强、侮辱性强的词(滚吧、找爸爸去、废物)、骂人的词(傻子、垃圾、蠢蛋),ai常用词(如\"这波操作\"、\"666\"),极限词、违反法律的词汇\n\n -用词口语化,句式有短句、情绪浓烈,\n -不要输出前言和介绍,也不能直白的指责。\n -写作的内容不要引用提示词的要求。\n\n ## 写作要求\n 1、第一段直接照抄原文的第一句话作为文章开头,一个字也不能改,必须控制30个字以内,绝对禁止照抄的第一句话超过30个字,超过30个字在不影响句子连贯的情况下截停。\n\n 2、后面的内容要具体叙述具体行为,每一个具体动作都不能跳过,可以直接使用原文句子,但是不能完全一致,从原文案的内容里面,用不同的角度切入叙述,确保内容完整,可以使用同义字进行替换,重组结构。\n\n 3、最后一段可以直接使用原文的最后一段,如果有身份信息的内容,就不要写出来,**例如:我是某某,后续内容主页观看。**\n可用变量:{{content}}、{{count}}"
|
||||||
|
},
|
||||||
|
"videoMixCutSettings": {
|
||||||
|
"enabled": false,
|
||||||
|
"mode": "fixed",
|
||||||
|
"fixedModeConfig": {
|
||||||
|
"enabled": false,
|
||||||
|
"opening": {
|
||||||
|
"enabled": false,
|
||||||
|
"timeRange": {
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5
|
||||||
|
},
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "top-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"middle": {
|
||||||
|
"enabled": false,
|
||||||
|
"timeRange": {
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5
|
||||||
|
},
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-left",
|
||||||
|
"pipSizePercent": 40,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ending": {
|
||||||
|
"enabled": false,
|
||||||
|
"timeRange": {
|
||||||
|
"durationMin": 2,
|
||||||
|
"durationMax": 5
|
||||||
|
},
|
||||||
|
"materialFolderPath": "",
|
||||||
|
"selectionMode": "random",
|
||||||
|
"displayMode": "fullscreen",
|
||||||
|
"pipPosition": "bottom-right",
|
||||||
|
"pipSizePercent": 30,
|
||||||
|
"pipScaleMode": "fit",
|
||||||
|
"originalVideoMinimized": {
|
||||||
|
"enabled": false,
|
||||||
|
"shape": "square",
|
||||||
|
"position": "bottom-right",
|
||||||
|
"sizePercent": 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keywordModeConfig": {
|
||||||
|
"enabled": false,
|
||||||
|
"groups": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subtitleSettings": {
|
||||||
|
"defaultFont": "1.程荣光刻楷",
|
||||||
|
"defaultFontSize": 48,
|
||||||
|
"defaultPosition": "bottom",
|
||||||
|
"enableKeywordEffects": true
|
||||||
|
},
|
||||||
|
"modelSettings": {
|
||||||
|
"defaultModel": "gpt-4",
|
||||||
|
"temperature": 0.7,
|
||||||
|
"maxTokens": 2000
|
||||||
|
},
|
||||||
|
"otherSettings": {
|
||||||
|
"darkMode": "auto",
|
||||||
|
"guideWatched": false,
|
||||||
|
"updaterCheckAtLaunch": "yes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
document.addEventListener(
|
||||||
|
"click",
|
||||||
|
function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"manifestVersion": 1,
|
||||||
|
"generatedAt": "2026-06-20T06:40:16.667Z",
|
||||||
|
"appVersion": "9.9.8",
|
||||||
|
"bundles": {
|
||||||
|
"playwright": {
|
||||||
|
"version": "b2c0ba43945b43d5",
|
||||||
|
"archive": "playwright-b2c0ba43945b43d5.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/playwright-b2c0ba43945b43d5.zip",
|
||||||
|
"sha256": "5febfa8992005fedba49d6448d04ea7e1dfdfe4d1a64e12dacc9439bcb6fd3df",
|
||||||
|
"size": 342061468,
|
||||||
|
"required": true,
|
||||||
|
"extractTo": "resources-bundles/playwright",
|
||||||
|
"source": "packaging/vendor/playwright"
|
||||||
|
},
|
||||||
|
"python-runtime": {
|
||||||
|
"version": "43b1bc61b300f429",
|
||||||
|
"archive": "python-runtime-43b1bc61b300f429.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/python-runtime-43b1bc61b300f429.zip",
|
||||||
|
"sha256": "4d6a0872953d779f4b18a5b45279437c11c21ef057512cf82602b0aa307391fb",
|
||||||
|
"size": 242930983,
|
||||||
|
"required": true,
|
||||||
|
"extractTo": "resources-bundles/python-runtime",
|
||||||
|
"source": "packaging/vendor/python-runtime"
|
||||||
|
},
|
||||||
|
"ffmpeg": {
|
||||||
|
"version": "b3ee3c78959a363a",
|
||||||
|
"archive": "ffmpeg-b3ee3c78959a363a.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/ffmpeg-b3ee3c78959a363a.zip",
|
||||||
|
"sha256": "5648084968b1e4228a8b00cd48fb74e5c4daef09a3b3620e5ba9b1081cf6e051",
|
||||||
|
"size": 148417067,
|
||||||
|
"required": true,
|
||||||
|
"extractTo": "resources-bundles/ffmpeg",
|
||||||
|
"source": "packaging/vendor/ffmpeg"
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"version": "2ba4d03d6459eace",
|
||||||
|
"archive": "models-2ba4d03d6459eace.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/models-2ba4d03d6459eace.zip",
|
||||||
|
"sha256": "02b9e0f71a7822143c2f4122ce8cdd39f23569705bb0032717655230896e7a3d",
|
||||||
|
"size": 164653874,
|
||||||
|
"required": true,
|
||||||
|
"extractTo": "resources-bundles/models",
|
||||||
|
"source": "packaging/vendor/models"
|
||||||
|
},
|
||||||
|
"fonts": {
|
||||||
|
"version": "03a3d7c54491326b",
|
||||||
|
"archive": "fonts-03a3d7c54491326b.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/fonts-03a3d7c54491326b.zip",
|
||||||
|
"sha256": "7520ae93e5b1b7ddb63157ca98e6e6ae0dd8b7f5cd94999a24aee99a7f99dfa3",
|
||||||
|
"size": 241090999,
|
||||||
|
"required": false,
|
||||||
|
"extractTo": "resources-bundles/fonts",
|
||||||
|
"source": "packaging/vendor/fonts"
|
||||||
|
},
|
||||||
|
"ziti": {
|
||||||
|
"version": "431cd09bfeb5140f",
|
||||||
|
"archive": "ziti-431cd09bfeb5140f.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/ziti-431cd09bfeb5140f.zip",
|
||||||
|
"sha256": "b6bb315b308d96c8621965342f01dfc74b1e61be513ca543d2d53fccd80bbc8a",
|
||||||
|
"size": 199724902,
|
||||||
|
"required": true,
|
||||||
|
"extractTo": "resources-bundles/ziti",
|
||||||
|
"source": "packaging/vendor/ziti"
|
||||||
|
},
|
||||||
|
"bgm": {
|
||||||
|
"version": "39c0cca46011e3fd",
|
||||||
|
"archive": "bgm-39c0cca46011e3fd.zip",
|
||||||
|
"url": "https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates/bgm-39c0cca46011e3fd.zip",
|
||||||
|
"sha256": "7902156de9b99908901e4ad50d19bfeba9dd2bea15e6fc87be8e2f0a7972e836",
|
||||||
|
"size": 83505038,
|
||||||
|
"required": false,
|
||||||
|
"extractTo": "resources-bundles/bgm",
|
||||||
|
"source": "packaging/vendor/bgm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 279 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
@@ -0,0 +1,99 @@
|
|||||||
|
# 音色预缓存目录
|
||||||
|
|
||||||
|
此目录用于存放系统默认音色的预缓存文件,在应用打包时包含这些文件可以让用户首次使用时无需等待音色生成。
|
||||||
|
|
||||||
|
## 目录用途
|
||||||
|
|
||||||
|
- **开发环境**: 用于测试预缓存功能
|
||||||
|
- **生产环境**: 打包时将此目录包含在应用中,首次启动时会自动复制到用户数据目录
|
||||||
|
|
||||||
|
## 如何生成预缓存文件
|
||||||
|
|
||||||
|
### 方式一:使用应用自动生成(推荐)
|
||||||
|
|
||||||
|
1. 启动应用(开发模式或打包后的应用)
|
||||||
|
2. 确保已配置 DashScope / 千问 API Key(在模型设置中)
|
||||||
|
3. 打开音色管理对话框,逐个试听所有默认音色
|
||||||
|
4. 试听后的音色会自动保存到用户数据目录的 `voice-preview-cache/` 文件夹
|
||||||
|
5. 找到用户数据目录:
|
||||||
|
- **Windows**: `C:\Users\<用户名>\AppData\Roaming\zhenqianba-v2\voice-preview-cache`
|
||||||
|
- **macOS**: `~/Library/Application Support/zhenqianba-v2/voice-preview-cache`
|
||||||
|
- **Linux**: `~/.config/zhenqianba-v2/voice-preview-cache`
|
||||||
|
6. 将生成的 `.wav` 文件复制到此目录(`packaging/resources/voice-preview-cache/`)
|
||||||
|
|
||||||
|
### 方式二:手动触发预缓存
|
||||||
|
|
||||||
|
在应用的开发者控制台中执行:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
await window.$mapi.aliyun.cosyvoice.preCacheSystemVoices('<DashScope API Key>', true);
|
||||||
|
```
|
||||||
|
|
||||||
|
然后从用户数据目录复制生成的文件到此目录。
|
||||||
|
|
||||||
|
## 预缓存文件列表
|
||||||
|
|
||||||
|
当前系统音色应以 `sys_*.wav` 形式存放,例如:
|
||||||
|
|
||||||
|
- `sys_Cherry.wav`
|
||||||
|
- `sys_Serena.wav`
|
||||||
|
- `sys_Ethan.wav`
|
||||||
|
- `sys_Chelsie.wav`
|
||||||
|
- `sys_Momo.wav`
|
||||||
|
- `sys_Vivian.wav`
|
||||||
|
- `sys_Moon.wav`
|
||||||
|
- `sys_Maia.wav`
|
||||||
|
- `sys_Kai.wav`
|
||||||
|
- `sys_Nofish.wav`
|
||||||
|
- `sys_Bella.wav`
|
||||||
|
- `sys_Jennifer.wav`
|
||||||
|
- `sys_Ryan.wav`
|
||||||
|
- `sys_Katerina.wav`
|
||||||
|
- `sys_Aiden.wav`
|
||||||
|
- `sys_Jada.wav`
|
||||||
|
- `sys_Dylan.wav`
|
||||||
|
- `sys_Li.wav`
|
||||||
|
- `sys_Marcus.wav`
|
||||||
|
- `sys_Roy.wav`
|
||||||
|
- `sys_Peter.wav`
|
||||||
|
- `sys_Sunny.wav`
|
||||||
|
- `sys_Eric.wav`
|
||||||
|
- `sys_Rocky.wav`
|
||||||
|
- `sys_Kiki.wav`
|
||||||
|
- `sys_Bodega.wav`
|
||||||
|
- `sys_Sonrisa.wav`
|
||||||
|
- `sys_Alek.wav`
|
||||||
|
- `sys_Dolce.wav`
|
||||||
|
- `sys_Sohee.wav`
|
||||||
|
- `sys_Ono_Anna.wav`
|
||||||
|
- `sys_Lenn.wav`
|
||||||
|
- `sys_Emilien.wav`
|
||||||
|
- `sys_Andre.wav`
|
||||||
|
|
||||||
|
## 打包配置
|
||||||
|
|
||||||
|
确保在 `electron-builder.json5` 中包含此目录:
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"extraResources": [
|
||||||
|
{
|
||||||
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
|
"to": "voice-preview-cache"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 工作原理
|
||||||
|
|
||||||
|
1. **首次启动**: 应用检测到用户数据目录没有预缓存文件
|
||||||
|
2. **资源复制**: 从应用资源目录 (`resources/voice-preview-cache/`) 复制到用户数据目录
|
||||||
|
3. **即时可用**: 用户试听时直接使用本地缓存,无需等待 API 生成
|
||||||
|
4. **自动补全**: 如果某些音色未包含在打包中,用户试听时会自动生成并保存
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 当前试听缓存已改为 `.wav` 文件
|
||||||
|
- 用户后续试听新音色时,仍会自动保存到本地缓存
|
||||||
|
- 此功能不影响用户自定义音色的使用
|
||||||
@@ -7,7 +7,7 @@ console.log('=== 全面分析所有配置源和数据库中的模板差异 ===\n
|
|||||||
// 1. 收集所有配置文件
|
// 1. 收集所有配置文件
|
||||||
const configFiles = [
|
const configFiles = [
|
||||||
{ name: 'C盘根目录', path: 'C:\\system-templates.json' },
|
{ name: 'C盘根目录', path: 'C:\\system-templates.json' },
|
||||||
{ name: '项目打包配置', path: 'electron/resources/extra/common/config/system-templates.json' },
|
{ name: '项目打包配置', path: 'packaging/resources/common/config/system-templates.json' },
|
||||||
{ name: '项目开发配置', path: 'electron/config/system-templates.json' },
|
{ name: '项目开发配置', path: 'electron/config/system-templates.json' },
|
||||||
{ name: '历史配置v2', path: 'electron/config/default-subtitle-templates-v2.json' },
|
{ name: '历史配置v2', path: 'electron/config/default-subtitle-templates-v2.json' },
|
||||||
{ name: 'TypeScript源文件', path: 'src/config/systemSubtitleTemplates.ts' },
|
{ name: 'TypeScript源文件', path: 'src/config/systemSubtitleTemplates.ts' },
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ exports.default = async function (context) {
|
|||||||
context: context,
|
context: context,
|
||||||
root: context.appOutDir,
|
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 platformName = common.platformName();
|
||||||
const platformArch = common.platformArch();
|
const platformArch = common.platformArch();
|
||||||
const name = platformName + "-" + platformArch;
|
const name = platformName + "-" + platformArch;
|
||||||
|
|
||||||
const srcDir = `electron/resources/extra/${name}`;
|
const srcDir = `packaging/resources/${name}`;
|
||||||
let destDir = null;
|
let destDir = null;
|
||||||
if (platformName === 'osx') {
|
if (platformName === 'osx') {
|
||||||
destDir = common.pathResolve(
|
destDir = common.pathResolve(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const content = fs.readFileSync('electron/resources/extra/common/config/system-templates.json', 'utf-8');
|
const content = fs.readFileSync('packaging/resources/common/config/system-templates.json', 'utf-8');
|
||||||
const data = JSON.parse(content);
|
const data = JSON.parse(content);
|
||||||
|
|
||||||
console.log('模板数量:', data.subtitleTemplates.length);
|
console.log('模板数量:', data.subtitleTemplates.length);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const path = require('path');
|
|||||||
|
|
||||||
// 读取 C 盘根目录的配置文件
|
// 读取 C 盘根目录的配置文件
|
||||||
const cDriveFile = 'C:\\system-templates.json';
|
const cDriveFile = 'C:\\system-templates.json';
|
||||||
const projectFile = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const projectFile = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
|
|
||||||
console.log('=== 对比 C 盘配置 vs 项目配置 ===\n');
|
console.log('=== 对比 C 盘配置 vs 项目配置 ===\n');
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const cDriveFile = 'C:\\system-templates.json';
|
const cDriveFile = 'C:\\system-templates.json';
|
||||||
const projectFile = 'electron/resources/extra/common/config/system-templates.json';
|
const projectFile = 'packaging/resources/common/config/system-templates.json';
|
||||||
|
|
||||||
const cConfig = JSON.parse(fs.readFileSync(cDriveFile, 'utf-8'));
|
const cConfig = JSON.parse(fs.readFileSync(cDriveFile, 'utf-8'));
|
||||||
const pConfig = JSON.parse(fs.readFileSync(projectFile, 'utf-8'));
|
const pConfig = JSON.parse(fs.readFileSync(projectFile, 'utf-8'));
|
||||||
@@ -71,4 +71,3 @@ Object.keys(pGroups).forEach(key => {
|
|||||||
console.log(`模板 ${pGroups[key].join(', ')} 配置相同`);
|
console.log(`模板 ${pGroups[key].join(', ')} 配置相同`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const historicalFile = path.join(__dirname, '../electron/config/default-subtitle
|
|||||||
const historical = JSON.parse(fs.readFileSync(historicalFile, 'utf-8'));
|
const historical = JSON.parse(fs.readFileSync(historicalFile, 'utf-8'));
|
||||||
|
|
||||||
// 读取当前配置
|
// 读取当前配置
|
||||||
const currentFile = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const currentFile = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
const current = JSON.parse(fs.readFileSync(currentFile, 'utf-8'));
|
const current = JSON.parse(fs.readFileSync(currentFile, 'utf-8'));
|
||||||
|
|
||||||
console.log('=== 历史配置 vs 当前配置对比 ===\n');
|
console.log('=== 历史配置 vs 当前配置对比 ===\n');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
// 读取 JSON 配置文件
|
// 读取 JSON 配置文件
|
||||||
const jsonFile = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const jsonFile = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
const data = JSON.parse(fs.readFileSync(jsonFile, 'utf-8'));
|
const data = JSON.parse(fs.readFileSync(jsonFile, 'utf-8'));
|
||||||
|
|
||||||
console.log('=== 模板标题字幕配置对比 ===\n');
|
console.log('=== 模板标题字幕配置对比 ===\n');
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ async function copyPlaywrightBrowser() {
|
|||||||
console.log('='.repeat(60));
|
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);
|
await fs.ensureDir(targetDir);
|
||||||
|
|
||||||
// 源目录:Playwright 的缓存位置(开发环境)
|
// 源目录:Playwright 的缓存位置(开发环境)
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ const https = require('https');
|
|||||||
const http = require('http');
|
const http = require('http');
|
||||||
const { execSync } = require('child_process');
|
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) {
|
function downloadFile(url, destPath, description) {
|
||||||
@@ -80,11 +82,11 @@ async function main() {
|
|||||||
|
|
||||||
// 1. 下载 Python 3.12 嵌入式版本(使用淘宝镜像)
|
// 1. 下载 Python 3.12 嵌入式版本(使用淘宝镜像)
|
||||||
console.log('\n📦 步骤 1/5: 下载 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);
|
await fs.ensureDir(pythonDir);
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(pythonDir, 'python.exe'))) {
|
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 = [
|
const mirrors = [
|
||||||
@@ -206,7 +208,7 @@ trusted-host = pypi.tuna.tsinghua.edu.cn
|
|||||||
|
|
||||||
// 4. FFmpeg 手动下载提示
|
// 4. FFmpeg 手动下载提示
|
||||||
console.log('\n📦 步骤 4/5: 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);
|
await fs.ensureDir(ffmpegDir);
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(ffmpegDir, 'ffmpeg.exe'))) {
|
if (!fs.existsSync(path.join(ffmpegDir, 'ffmpeg.exe'))) {
|
||||||
@@ -226,7 +228,7 @@ trusted-host = pypi.tuna.tsinghua.edu.cn
|
|||||||
|
|
||||||
// 5. Chromium
|
// 5. Chromium
|
||||||
console.log('\n📦 步骤 5/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);
|
await fs.ensureDir(chromiumDir);
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(chromiumDir, 'chrome-win'))) {
|
if (!fs.existsSync(path.join(chromiumDir, 'chrome-win'))) {
|
||||||
@@ -268,7 +270,7 @@ trusted-host = pypi.tuna.tsinghua.edu.cn
|
|||||||
|
|
||||||
// 6. AI 模型
|
// 6. AI 模型
|
||||||
console.log('\n📦 步骤 6/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);
|
await fs.ensureDir(modelsDir);
|
||||||
|
|
||||||
const u2netPath = path.join(modelsDir, 'u2net.onnx');
|
const u2netPath = path.join(modelsDir, 'u2net.onnx');
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ const https = require('https');
|
|||||||
const http = require('http');
|
const http = require('http');
|
||||||
const { execSync } = require('child_process');
|
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) {
|
function downloadFile(url, destPath, description) {
|
||||||
@@ -81,11 +83,11 @@ async function main() {
|
|||||||
|
|
||||||
// 1. 下载 Python 3.12 嵌入式版本
|
// 1. 下载 Python 3.12 嵌入式版本
|
||||||
console.log('\n📦 步骤 1/4: 下载 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);
|
await fs.ensureDir(pythonDir);
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(pythonDir, 'python.exe'))) {
|
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(
|
await downloadFile(
|
||||||
'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
'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
|
// 3. 下载 FFmpeg
|
||||||
console.log('\n📦 步骤 3/4: 下载 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);
|
await fs.ensureDir(ffmpegDir);
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(ffmpegDir, 'ffmpeg.exe'))) {
|
if (!fs.existsSync(path.join(ffmpegDir, 'ffmpeg.exe'))) {
|
||||||
@@ -178,7 +180,7 @@ async function main() {
|
|||||||
|
|
||||||
// 4. 下载 Chromium (通过 puppeteer)
|
// 4. 下载 Chromium (通过 puppeteer)
|
||||||
console.log('\n📦 步骤 4/4: 准备 Chromium 浏览器');
|
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);
|
await fs.ensureDir(chromiumDir);
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(chromiumDir, 'chrome-win'))) {
|
if (!fs.existsSync(path.join(chromiumDir, 'chrome-win'))) {
|
||||||
@@ -220,7 +222,7 @@ async function main() {
|
|||||||
|
|
||||||
// 5. 下载 AI 模型
|
// 5. 下载 AI 模型
|
||||||
console.log('\n📦 步骤 5/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);
|
await fs.ensureDir(modelsDir);
|
||||||
|
|
||||||
const u2netPath = path.join(modelsDir, 'u2net.onnx');
|
const u2netPath = path.join(modelsDir, 'u2net.onnx');
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ console.log('=== 查找8个模板的不同配置(普通字幕字体、特效
|
|||||||
// 检查所有可能的配置文件
|
// 检查所有可能的配置文件
|
||||||
const filesToCheck = [
|
const filesToCheck = [
|
||||||
{ name: 'default-ipagent-config.json', path: 'electron/config/default-ipagent-config.json' },
|
{ name: 'default-ipagent-config.json', path: 'electron/config/default-ipagent-config.json' },
|
||||||
{ name: 'default-ipagent-config.json (resources)', path: 'electron/resources/extra/common/config/default-ipagent-config.json' },
|
{ name: 'default-ipagent-config.json (resources)', path: 'packaging/resources/common/config/default-ipagent-config.json' },
|
||||||
{ name: 'default-subtitle-templates.json', path: 'electron/config/default-subtitle-templates.json' },
|
{ name: 'default-subtitle-templates.json', path: 'electron/config/default-subtitle-templates.json' },
|
||||||
{ name: 'C盘配置', path: 'C:\\system-templates.json' },
|
{ name: 'C盘配置', path: 'C:\\system-templates.json' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ console.log('=== 查找8个模板的不同配置 ===\n');
|
|||||||
|
|
||||||
// 检查所有可能的配置文件
|
// 检查所有可能的配置文件
|
||||||
const filesToCheck = [
|
const filesToCheck = [
|
||||||
{ name: 'default-templates.json (subtitleStyles)', path: 'electron/resources/extra/common/config/default-templates.json' },
|
{ name: 'default-templates.json (subtitleStyles)', path: 'packaging/resources/common/config/default-templates.json' },
|
||||||
{ name: 'default-subtitle-templates.json', path: 'electron/config/default-subtitle-templates.json' },
|
{ name: 'default-subtitle-templates.json', path: 'electron/config/default-subtitle-templates.json' },
|
||||||
{ name: 'C盘配置', path: 'C:\\system-templates.json' },
|
{ name: 'C盘配置', path: 'C:\\system-templates.json' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ console.log('=== 8个模板配置差异全面分析报告 ===\n');
|
|||||||
// 读取所有配置源
|
// 读取所有配置源
|
||||||
const sources = {
|
const sources = {
|
||||||
'C盘根目录': 'C:\\system-templates.json',
|
'C盘根目录': 'C:\\system-templates.json',
|
||||||
'项目打包配置': 'electron/resources/extra/common/config/system-templates.json',
|
'项目打包配置': 'packaging/resources/common/config/system-templates.json',
|
||||||
'项目开发配置': 'electron/config/system-templates.json',
|
'项目开发配置': 'electron/config/system-templates.json',
|
||||||
'历史配置v2': 'electron/config/default-subtitle-templates-v2.json',
|
'历史配置v2': 'electron/config/default-subtitle-templates-v2.json',
|
||||||
'TypeScript源': 'src/config/systemSubtitleTemplates.ts',
|
'TypeScript源': 'src/config/systemSubtitleTemplates.ts',
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ echo "PROJECT_ROOT: ${PROJECT_ROOT}"
|
|||||||
|
|
||||||
path_svg="${PROJECT_ROOT}/public/logo.svg"
|
path_svg="${PROJECT_ROOT}/public/logo.svg"
|
||||||
path_white_svg="${PROJECT_ROOT}/public/logo-white.svg"
|
path_white_svg="${PROJECT_ROOT}/public/logo-white.svg"
|
||||||
path_build="${PROJECT_ROOT}/electron/resources/build"
|
path_build="${PROJECT_ROOT}/packaging/build-resources"
|
||||||
path_extra="${PROJECT_ROOT}/electron/resources/extra"
|
path_extra="${PROJECT_ROOT}/packaging/resources"
|
||||||
path_source_png="${path_build}/logo_1024x1024.png"
|
path_source_png="${path_build}/logo_1024x1024.png"
|
||||||
|
|
||||||
cp -a "${path_svg}" "${PROJECT_ROOT}/src/assets/image/logo.svg"
|
cp -a "${path_svg}" "${PROJECT_ROOT}/src/assets/image/logo.svg"
|
||||||
|
|||||||
@@ -15,29 +15,30 @@ else
|
|||||||
cd ..
|
cd ..
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rfv electron/resources/extra/osx-arm64
|
rm -rfv packaging/resources/osx-arm64
|
||||||
mkdir -p electron/resources/extra/osx-arm64
|
mkdir -p packaging/resources/osx-arm64
|
||||||
cp -a share-binary/osx-arm64/ffmpeg electron/resources/extra/osx-arm64/ffmpeg
|
cp -a share-binary/osx-arm64/ffmpeg packaging/resources/osx-arm64/ffmpeg
|
||||||
cp -a share-binary/osx-arm64/ffprobe electron/resources/extra/osx-arm64/ffprobe
|
cp -a share-binary/osx-arm64/ffprobe packaging/resources/osx-arm64/ffprobe
|
||||||
|
|
||||||
rm -rfv electron/resources/extra/osx-x86
|
rm -rfv packaging/resources/osx-x86
|
||||||
mkdir -p electron/resources/extra/osx-x86
|
mkdir -p packaging/resources/osx-x86
|
||||||
cp -a share-binary/osx-x86/ffmpeg electron/resources/extra/osx-x86/ffmpeg
|
cp -a share-binary/osx-x86/ffmpeg packaging/resources/osx-x86/ffmpeg
|
||||||
cp -a share-binary/osx-x86/ffprobe electron/resources/extra/osx-x86/ffprobe
|
cp -a share-binary/osx-x86/ffprobe packaging/resources/osx-x86/ffprobe
|
||||||
|
|
||||||
rm -rfv electron/resources/extra/linux-arm64
|
rm -rfv packaging/resources/linux-arm64
|
||||||
mkdir -p electron/resources/extra/linux-arm64
|
mkdir -p packaging/resources/linux-arm64
|
||||||
cp -a share-binary/linux-arm64/ffmpeg electron/resources/extra/linux-arm64/ffmpeg
|
cp -a share-binary/linux-arm64/ffmpeg packaging/resources/linux-arm64/ffmpeg
|
||||||
cp -a share-binary/linux-arm64/ffprobe electron/resources/extra/linux-arm64/ffprobe
|
cp -a share-binary/linux-arm64/ffprobe packaging/resources/linux-arm64/ffprobe
|
||||||
|
|
||||||
rm -rfv electron/resources/extra/linux-x86
|
rm -rfv packaging/resources/linux-x86
|
||||||
mkdir -p electron/resources/extra/linux-x86
|
mkdir -p packaging/resources/linux-x86
|
||||||
cp -a share-binary/linux-x86/ffmpeg electron/resources/extra/linux-x86/ffmpeg
|
cp -a share-binary/linux-x86/ffmpeg packaging/resources/linux-x86/ffmpeg
|
||||||
cp -a share-binary/linux-x86/ffprobe electron/resources/extra/linux-x86/ffprobe
|
cp -a share-binary/linux-x86/ffprobe packaging/resources/linux-x86/ffprobe
|
||||||
|
|
||||||
rm -rfv electron/resources/extra/win-x86
|
rm -rfv packaging/vendor/ffmpeg
|
||||||
mkdir -p electron/resources/extra/win-x86
|
mkdir -p packaging/vendor/ffmpeg
|
||||||
cp -a share-binary/win-x86/ffmpeg.exe electron/resources/extra/win-x86/ffmpeg.exe
|
cp -a share-binary/win-x86/ffmpeg.exe packaging/vendor/ffmpeg/ffmpeg.exe
|
||||||
cp -a share-binary/win-x86/ffprobe.exe electron/resources/extra/win-x86/ffprobe.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
|
||||||
|
|||||||
@@ -12,10 +12,8 @@ function isLargeBundledResource(entry) {
|
|||||||
return to.startsWith('resources-bundles/')
|
return to.startsWith('resources-bundles/')
|
||||||
|| from === 'ziti'
|
|| from === 'ziti'
|
||||||
|| from === 'bgm'
|
|| from === 'bgm'
|
||||||
|| from.includes('/common/playwright')
|
|| from.startsWith('packaging/vendor/')
|
||||||
|| from.includes('/common/python')
|
|| from.includes('/packaging/vendor/');
|
||||||
|| from.includes('/common/models')
|
|
||||||
|| from.includes('/common/fonts');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = JSON5.parse(fs.readFileSync(sourcePath, 'utf8'));
|
const config = JSON5.parse(fs.readFileSync(sourcePath, 'utf8'));
|
||||||
@@ -114,7 +112,7 @@ if (config.win && Array.isArray(config.win.extraResources)) {
|
|||||||
.filter(entry => !isLargeBundledResource(entry))
|
.filter(entry => !isLargeBundledResource(entry))
|
||||||
.map(entry => {
|
.map(entry => {
|
||||||
const from = String(entry && entry.from || '').replace(/\\/g, '/');
|
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 {
|
return {
|
||||||
...entry,
|
...entry,
|
||||||
filter: entry.filter
|
filter: entry.filter
|
||||||
@@ -134,7 +132,7 @@ if (config.win && Array.isArray(config.win.extraResources)) {
|
|||||||
.concat(oemCoverTemplateFiles)
|
.concat(oemCoverTemplateFiles)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (from.endsWith('electron/resources/build') && Array.isArray(entry.filter)) {
|
if (from.endsWith('packaging/build-resources') && Array.isArray(entry.filter)) {
|
||||||
return {
|
return {
|
||||||
...entry,
|
...entry,
|
||||||
filter: entry.filter.filter(pattern => String(pattern).replace(/\\/g, '/') !== 'vc_redist.x64.exe'),
|
filter: entry.filter.filter(pattern => String(pattern).replace(/\\/g, '/') !== 'vc_redist.x64.exe'),
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
const path = require("node:path");
|
||||||
|
const { spawnSync } = require("node:child_process");
|
||||||
|
|
||||||
|
const projectRoot = path.resolve(__dirname, "..");
|
||||||
|
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
||||||
|
const electronBuilder = path.join(
|
||||||
|
projectRoot,
|
||||||
|
"node_modules",
|
||||||
|
".bin",
|
||||||
|
process.platform === "win32" ? "electron-builder.cmd" : "electron-builder",
|
||||||
|
);
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
console.log(`
|
||||||
|
客户端打包脚本(不包含认证服务/管理后台)
|
||||||
|
|
||||||
|
用法:
|
||||||
|
npm.cmd run build:client:win -- --auth-url=https://auth.example.com
|
||||||
|
npm.cmd run build:client:win -- --auth-url=http://127.0.0.1:3302 --dry-run
|
||||||
|
|
||||||
|
参数:
|
||||||
|
--auth-url <url> 认证服务地址,可带或不带 /api
|
||||||
|
--dir 只打包目录,不生成安装包
|
||||||
|
--dry-run 只打印将执行的命令,不真正打包
|
||||||
|
--help 显示帮助
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readArg(name) {
|
||||||
|
const prefix = `${name}=`;
|
||||||
|
const inline = process.argv.find(arg => arg.startsWith(prefix));
|
||||||
|
if (inline) return inline.slice(prefix.length);
|
||||||
|
|
||||||
|
const index = process.argv.indexOf(name);
|
||||||
|
if (index >= 0) return process.argv[index + 1];
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasArg(name) {
|
||||||
|
return process.argv.includes(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAuthUrl(value) {
|
||||||
|
const raw = String(value || "").trim();
|
||||||
|
if (!raw) return "";
|
||||||
|
if (/^https?\/\//i.test(raw)) {
|
||||||
|
throw new Error(`认证服务地址无效,协议后缺少冒号:${raw}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = new URL(raw);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`认证服务地址无效:${raw}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!["http:", "https:"].includes(parsed.protocol)) {
|
||||||
|
throw new Error(`认证服务地址只支持 http 或 https:${raw}`);
|
||||||
|
}
|
||||||
|
if (["http", "https"].includes(parsed.hostname) && parsed.pathname.startsWith("//")) {
|
||||||
|
throw new Error(`认证服务地址疑似多写了协议,请检查:${raw}`);
|
||||||
|
}
|
||||||
|
if (!parsed.hostname || parsed.hostname.includes("/")) {
|
||||||
|
throw new Error(`认证服务地址主机名无效:${raw}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed.pathname = parsed.pathname.replace(/\/api\/?$/i, "").replace(/\/+$/, "");
|
||||||
|
parsed.search = "";
|
||||||
|
parsed.hash = "";
|
||||||
|
return parsed.toString().replace(/\/+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function quoteDisplay(value) {
|
||||||
|
const text = String(value);
|
||||||
|
return /\s/.test(text) ? `"${text}"` : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function quoteCmdArg(value) {
|
||||||
|
const text = String(value);
|
||||||
|
if (!/[ \t"&|<>^]/.test(text)) return text;
|
||||||
|
return `"${text.replace(/"/g, '""')}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWindowsCmd(command) {
|
||||||
|
return process.platform === "win32" && /\.(cmd|bat)$/i.test(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command, args, env, dryRun) {
|
||||||
|
const display = [command, ...args].map(quoteDisplay).join(" ");
|
||||||
|
console.log(`[package-client] ${display}`);
|
||||||
|
if (dryRun) return;
|
||||||
|
|
||||||
|
const spawnCommand = isWindowsCmd(command)
|
||||||
|
? (process.env.ComSpec || "cmd.exe")
|
||||||
|
: command;
|
||||||
|
const spawnArgs = isWindowsCmd(command)
|
||||||
|
? ["/d", "/s", "/c", [quoteCmdArg(command), ...args.map(quoteCmdArg)].join(" ")]
|
||||||
|
: args;
|
||||||
|
|
||||||
|
const result = spawnSync(spawnCommand, spawnArgs, {
|
||||||
|
cwd: projectRoot,
|
||||||
|
stdio: "inherit",
|
||||||
|
env,
|
||||||
|
shell: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status !== 0) {
|
||||||
|
throw new Error(`命令执行失败:${display}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
if (hasArg("--help") || hasArg("-h")) {
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authUrl = normalizeAuthUrl(
|
||||||
|
readArg("--auth-url") ||
|
||||||
|
process.env.PACKAGE_AUTH_SERVER_URL ||
|
||||||
|
process.env.AUTH_SERVER_URL ||
|
||||||
|
process.env.VITE_AUTH_SERVER_URL,
|
||||||
|
);
|
||||||
|
const dryRun = hasArg("--dry-run");
|
||||||
|
const dirOnly = hasArg("--dir");
|
||||||
|
|
||||||
|
if (!authUrl) {
|
||||||
|
usage();
|
||||||
|
throw new Error("打包客户端必须指定认证服务地址:--auth-url=https://auth.example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
AUTH_SERVER_URL: authUrl,
|
||||||
|
VITE_AUTH_SERVER_URL: authUrl,
|
||||||
|
PACKAGE_CLIENT_ONLY: "1",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`[package-client] 认证服务地址:${authUrl}`);
|
||||||
|
console.log("[package-client] 本脚本只打包客户端,不会启动或打包本地认证服务。");
|
||||||
|
|
||||||
|
run(npmCommand, ["run", "prepare-package"], env, dryRun);
|
||||||
|
run(npmCommand, ["run", "build"], env, dryRun);
|
||||||
|
run(process.execPath, ["scripts/build-resource-artifacts.cjs"], env, dryRun);
|
||||||
|
run(electronBuilder, ["--win", "--x64", ...(dirOnly ? ["--dir"] : [])], env, dryRun);
|
||||||
|
|
||||||
|
if (!dirOnly) {
|
||||||
|
run(process.execPath, ["scripts/fix-latest-yml.cjs"], env, dryRun);
|
||||||
|
run(process.execPath, ["scripts/assemble-release-artifacts.cjs"], env, dryRun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
main();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[package-client] ${error.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -6,8 +6,14 @@ async function preparePackage() {
|
|||||||
console.log('📦 开始准备打包资源...\n');
|
console.log('📦 开始准备打包资源...\n');
|
||||||
console.log('='.repeat(60));
|
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 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. 发布前同步系统字幕模板,避免安装包带入旧候选模板或旧音效配置
|
// 0. 发布前同步系统字幕模板,避免安装包带入旧候选模板或旧音效配置
|
||||||
console.log('\n0锔忊儯 鍚屾绯荤粺瀛楀箷妯℃澘...');
|
console.log('\n0锔忊儯 鍚屾绯荤粺瀛楀箷妯℃澘...');
|
||||||
@@ -16,16 +22,16 @@ async function preparePackage() {
|
|||||||
|
|
||||||
// 1. 确保目录存在
|
// 1. 确保目录存在
|
||||||
console.log('\n1️⃣ 确保目录结构存在...');
|
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, 'chromium'));
|
||||||
await fs.ensureDir(path.join(commonDir, 'python-scripts'));
|
await fs.ensureDir(path.join(commonDir, 'python-scripts'));
|
||||||
await fs.ensureDir(path.join(commonDir, 'models'));
|
await fs.ensureDir(modelDir);
|
||||||
await fs.ensureDir(path.join(commonDir, 'fonts'));
|
await fs.ensureDir(fontBundleDir);
|
||||||
await fs.ensureDir(path.join(commonDir, 'bgm'));
|
await fs.ensureDir(vendorDir);
|
||||||
await fs.ensureDir(path.join(commonDir, 'demo'));
|
await fs.ensureDir(path.join(commonDir, 'demo'));
|
||||||
await fs.ensureDir(path.join(commonDir, 'config'));
|
await fs.ensureDir(path.join(commonDir, 'config'));
|
||||||
await fs.ensureDir(path.join(commonDir, 'cover-templates'));
|
await fs.ensureDir(path.join(commonDir, 'cover-templates'));
|
||||||
await fs.ensureDir(path.join(extraDir, 'win-x86'));
|
await fs.ensureDir(ffmpegDir);
|
||||||
console.log(' ✅ 目录结构准备完成\n');
|
console.log(' ✅ 目录结构准备完成\n');
|
||||||
|
|
||||||
// 2. 复制 Python 脚本
|
// 2. 复制 Python 脚本
|
||||||
@@ -60,7 +66,6 @@ async function preparePackage() {
|
|||||||
const fileCount = await countFiles(pythonDest);
|
const fileCount = await countFiles(pythonDest);
|
||||||
console.log(` ✅ Python 脚本复制到 python-scripts 完成 (${fileCount} 个文件)`);
|
console.log(` ✅ Python 脚本复制到 python-scripts 完成 (${fileCount} 个文件)`);
|
||||||
|
|
||||||
const pythonRuntimeDir = path.join(commonDir, 'python');
|
|
||||||
await copyWithLockTolerance(pythonSrc, pythonRuntimeDir, {
|
await copyWithLockTolerance(pythonSrc, pythonRuntimeDir, {
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
filter: pythonFilter
|
filter: pythonFilter
|
||||||
@@ -72,10 +77,13 @@ async function preparePackage() {
|
|||||||
|
|
||||||
// 3. 复制字体
|
// 3. 复制字体
|
||||||
console.log('3️⃣ 复制字体文件...');
|
console.log('3️⃣ 复制字体文件...');
|
||||||
const fontSrc = path.join(__dirname, '../ziti');
|
const fontSrc = await firstExistingPath([
|
||||||
const fontDest = path.join(commonDir, 'fonts/ziti');
|
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)) {
|
if (process.env.OEM_BATCH_BUILD === '1' && await fs.pathExists(fontDest)) {
|
||||||
const existingFileCount = await countFiles(fontDest);
|
const existingFileCount = await countFiles(fontDest);
|
||||||
if (existingFileCount > 0) {
|
if (existingFileCount > 0) {
|
||||||
@@ -105,20 +113,27 @@ async function preparePackage() {
|
|||||||
|
|
||||||
// 4. 复制 BGM
|
// 4. 复制 BGM
|
||||||
console.log('4️⃣ 复制背景音乐...');
|
console.log('4️⃣ 复制背景音乐...');
|
||||||
const bgmSrc = path.join(__dirname, '../bgm');
|
const bgmSrc = await firstExistingPath([
|
||||||
const bgmDest = path.join(commonDir, 'bgm');
|
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)) {
|
if (process.env.OEM_BATCH_BUILD === '1' && await fs.pathExists(bgmDest)) {
|
||||||
const existingFileCount = await countFiles(bgmDest);
|
const existingFileCount = await countFiles(bgmDest);
|
||||||
if (existingFileCount > 0) {
|
if (existingFileCount > 0) {
|
||||||
console.log(` [SKIP] OEM batch build reuses prepared BGM (${existingFileCount} files)\n`);
|
console.log(` [SKIP] OEM batch build reuses prepared BGM (${existingFileCount} files)\n`);
|
||||||
} else {
|
} else {
|
||||||
|
if (path.resolve(bgmSrc) !== path.resolve(bgmDest)) {
|
||||||
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
|
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (path.resolve(bgmSrc) !== path.resolve(bgmDest)) {
|
||||||
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
|
await copyWithLockTolerance(bgmSrc, bgmDest, { overwrite: true });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const fileCount = await countFiles(bgmDest);
|
const fileCount = await countFiles(bgmDest);
|
||||||
const totalSize = await getDirSize(bgmDest);
|
const totalSize = await getDirSize(bgmDest);
|
||||||
console.log(` ✅ BGM 复制完成 (${fileCount} 个文件, ${(totalSize / 1024 / 1024).toFixed(2)} MB)\n`);
|
console.log(` ✅ BGM 复制完成 (${fileCount} 个文件, ${(totalSize / 1024 / 1024).toFixed(2)} MB)\n`);
|
||||||
@@ -175,7 +190,7 @@ async function preparePackage() {
|
|||||||
name: 'default-ipagent-config.json'
|
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'),
|
dest: path.join(configDest, 'system-templates.json'),
|
||||||
name: 'system-templates.json (系统模板配置 - 8套字幕+8套封面)'
|
name: 'system-templates.json (系统模板配置 - 8套字幕+8套封面)'
|
||||||
}
|
}
|
||||||
@@ -217,7 +232,7 @@ async function preparePackage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// 动态查找最新的 Playwright Chromium 版本
|
// 动态查找最新的 Playwright Chromium 版本
|
||||||
const playwrightDir = path.join(commonDir, 'playwright');
|
const playwrightDir = path.join(vendorDir, 'playwright');
|
||||||
let playwrightChromiumCheck = null;
|
let playwrightChromiumCheck = null;
|
||||||
if (await fs.pathExists(playwrightDir)) {
|
if (await fs.pathExists(playwrightDir)) {
|
||||||
const chromiumDirs = (await fs.readdir(playwrightDir))
|
const chromiumDirs = (await fs.readdir(playwrightDir))
|
||||||
@@ -236,13 +251,13 @@ async function preparePackage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checks = [
|
const checks = [
|
||||||
{ name: 'Python 环境', path: path.join(commonDir, 'python/python.exe') },
|
{ name: 'Python 环境', path: path.join(pythonRuntimeDir, 'python.exe') },
|
||||||
{ name: 'Python 依赖 (opencv)', path: path.join(commonDir, 'python/Lib/site-packages/cv2') },
|
{ name: 'Python 依赖 (opencv)', path: path.join(pythonRuntimeDir, 'Lib/site-packages/cv2') },
|
||||||
{ name: 'FFmpeg (win-x86)', path: path.join(extraDir, 'win-x86/ffmpeg.exe') },
|
{ name: 'FFmpeg (win-x86)', path: path.join(ffmpegDir, 'ffmpeg.exe') },
|
||||||
{ name: 'FFprobe (win-x86)', path: path.join(extraDir, 'win-x86/ffprobe.exe') },
|
{ name: 'FFprobe (win-x86)', path: path.join(ffmpegDir, 'ffprobe.exe') },
|
||||||
...ffmpegDlls.map(dll => ({ name: `FFmpeg DLL (${dll})`, path: path.join(extraDir, `win-x86/${dll}`) })),
|
...ffmpegDlls.map(dll => ({ name: `FFmpeg DLL (${dll})`, path: path.join(ffmpegDir, dll) })),
|
||||||
...(playwrightChromiumCheck ? [playwrightChromiumCheck] : []),
|
...(playwrightChromiumCheck ? [playwrightChromiumCheck] : []),
|
||||||
{ name: 'U2-Net 模型', path: path.join(commonDir, 'models/u2net.onnx') },
|
{ name: 'U2-Net 模型', path: path.join(modelDir, 'u2net.onnx') },
|
||||||
];
|
];
|
||||||
|
|
||||||
let allOk = true;
|
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) {
|
async function countFiles(dir) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const configPath = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const configPath = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
|
|
||||||
console.log('📖 读取配置文件:', configPath);
|
console.log('📖 读取配置文件:', configPath);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const configPath = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const configPath = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
|
|
||||||
console.log('📖 读取配置文件:', configPath);
|
console.log('📖 读取配置文件:', configPath);
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +1,57 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const root = path.join(__dirname, '..');
|
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 = {
|
module.exports = {
|
||||||
releaseBaseUrlEnv: 'RESOURCE_BASE_URL',
|
releaseBaseUrlEnv: 'RESOURCE_BASE_URL',
|
||||||
releaseBaseUrl: 'https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates',
|
releaseBaseUrl: 'https://xiazaigengxin-1417293730.cos.ap-guangzhou.myqcloud.com/updates',
|
||||||
outputDir: path.join(root, 'dist-release-final', 'resource-bundles'),
|
outputDir: path.join(root, 'dist-release-final', 'resource-bundles'),
|
||||||
releaseManifestPath: path.join(root, 'dist-release-final', 'resource-manifest.json'),
|
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'),
|
buildStatePath: path.join(root, 'build', 'resource-build-state.json'),
|
||||||
bundles: [
|
bundles: [
|
||||||
{
|
{
|
||||||
name: 'playwright',
|
name: 'playwright',
|
||||||
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'playwright'),
|
source: path.join(vendorRoot, 'playwright'),
|
||||||
required: true,
|
required: true,
|
||||||
extractTo: 'resources-bundles/playwright',
|
extractTo: 'resources-bundles/playwright',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'python-runtime',
|
name: 'python-runtime',
|
||||||
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'python'),
|
source: path.join(vendorRoot, 'python-runtime'),
|
||||||
required: true,
|
required: true,
|
||||||
extractTo: 'resources-bundles/python-runtime',
|
extractTo: 'resources-bundles/python-runtime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ffmpeg',
|
name: 'ffmpeg',
|
||||||
source: path.join(root, 'electron', 'resources', 'extra', 'win-x86'),
|
source: path.join(vendorRoot, 'ffmpeg'),
|
||||||
required: true,
|
required: true,
|
||||||
extractTo: 'resources-bundles/ffmpeg',
|
extractTo: 'resources-bundles/ffmpeg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'models',
|
name: 'models',
|
||||||
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'models'),
|
source: path.join(vendorRoot, 'models'),
|
||||||
required: true,
|
required: true,
|
||||||
extractTo: 'resources-bundles/models',
|
extractTo: 'resources-bundles/models',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'fonts',
|
name: 'fonts',
|
||||||
source: path.join(root, 'electron', 'resources', 'extra', 'common', 'fonts'),
|
source: path.join(vendorRoot, 'fonts'),
|
||||||
required: false,
|
required: false,
|
||||||
extractTo: 'resources-bundles/fonts',
|
extractTo: 'resources-bundles/fonts',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ziti',
|
name: 'ziti',
|
||||||
source: path.join(root, 'ziti'),
|
source: path.join(vendorRoot, 'ziti'),
|
||||||
required: true,
|
required: true,
|
||||||
extractTo: 'resources-bundles/ziti',
|
extractTo: 'resources-bundles/ziti',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'bgm',
|
name: 'bgm',
|
||||||
source: path.join(root, 'bgm'),
|
source: path.join(vendorRoot, 'bgm'),
|
||||||
required: false,
|
required: false,
|
||||||
extractTo: 'resources-bundles/bgm',
|
extractTo: 'resources-bundles/bgm',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ const path = require('path');
|
|||||||
|
|
||||||
const repoRoot = path.join(__dirname, '..');
|
const repoRoot = path.join(__dirname, '..');
|
||||||
const tsSourcePath = path.join(repoRoot, 'src', 'config', 'systemSubtitleTemplates.ts');
|
const tsSourcePath = path.join(repoRoot, 'src', 'config', 'systemSubtitleTemplates.ts');
|
||||||
|
const packagingResourceRoot = path.join(repoRoot, 'packaging', 'resources');
|
||||||
const runtimeDatabasePath = path.join(repoRoot, 'data', 'database.db');
|
const runtimeDatabasePath = path.join(repoRoot, 'data', 'database.db');
|
||||||
const runtimeExportPath = path.join(repoRoot, 'build', 'runtime-system-subtitle-templates.json');
|
const runtimeExportPath = path.join(repoRoot, 'build', 'runtime-system-subtitle-templates.json');
|
||||||
const previewAssetDir = path.join(repoRoot, 'electron', 'resources', 'extra', 'common', 'cover-templates');
|
const previewAssetDir = path.join(packagingResourceRoot, 'common', 'cover-templates');
|
||||||
const packagedPreviewPrefix = 'extra/common/cover-templates';
|
const packagedPreviewPrefix = 'extra/common/cover-templates';
|
||||||
const inlineTemplateIds = new Set([
|
const inlineTemplateIds = new Set([
|
||||||
'template_system_11',
|
'template_system_11',
|
||||||
@@ -27,12 +28,12 @@ const defaultTriggeredSoundConfig = {
|
|||||||
|
|
||||||
const systemTemplateJsonPaths = [
|
const systemTemplateJsonPaths = [
|
||||||
path.join(repoRoot, 'electron', 'config', 'system-templates.json'),
|
path.join(repoRoot, 'electron', 'config', 'system-templates.json'),
|
||||||
path.join(repoRoot, 'electron', 'resources', 'extra', 'common', 'config', 'system-templates.json'),
|
path.join(packagingResourceRoot, 'common', 'config', 'system-templates.json'),
|
||||||
];
|
];
|
||||||
|
|
||||||
const defaultSubtitleTemplateJsonPaths = [
|
const defaultSubtitleTemplateJsonPaths = [
|
||||||
path.join(repoRoot, 'electron', 'config', 'default-subtitle-templates.json'),
|
path.join(repoRoot, 'electron', 'config', 'default-subtitle-templates.json'),
|
||||||
path.join(repoRoot, 'electron', 'resources', 'extra', 'common', 'config', 'default-subtitle-templates.json'),
|
path.join(packagingResourceRoot, 'common', 'config', 'default-subtitle-templates.json'),
|
||||||
];
|
];
|
||||||
|
|
||||||
const isAbsoluteLocalPath = (value) => /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith('\\\\');
|
const isAbsoluteLocalPath = (value) => /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith('\\\\');
|
||||||
@@ -148,6 +149,19 @@ const readTemplatesFromTsSource = () => {
|
|||||||
return templates;
|
return templates;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const readTemplatesFromJsonFile = (filePath) => {
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||||
|
const templates = json.subtitleTemplates;
|
||||||
|
if (!Array.isArray(templates) || templates.length !== 16) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return templates;
|
||||||
|
};
|
||||||
|
|
||||||
const readCanonicalTemplates = () => {
|
const readCanonicalTemplates = () => {
|
||||||
const dbTemplates = readTemplatesFromRuntimeDatabase();
|
const dbTemplates = readTemplatesFromRuntimeDatabase();
|
||||||
if (dbTemplates) {
|
if (dbTemplates) {
|
||||||
@@ -157,6 +171,16 @@ const readCanonicalTemplates = () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const filePath of systemTemplateJsonPaths) {
|
||||||
|
const jsonTemplates = readTemplatesFromJsonFile(filePath);
|
||||||
|
if (jsonTemplates) {
|
||||||
|
return {
|
||||||
|
source: filePath,
|
||||||
|
templates: jsonTemplates.map(normalizeTemplateForPackaging),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
source: 'ts-source',
|
source: 'ts-source',
|
||||||
templates: readTemplatesFromTsSource().map(normalizeTemplateForPackaging),
|
templates: readTemplatesFromTsSource().map(normalizeTemplateForPackaging),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const path = require('path');
|
|||||||
|
|
||||||
// 读取 TypeScript 配置文件
|
// 读取 TypeScript 配置文件
|
||||||
const tsFilePath = path.join(__dirname, '../src/config/systemSubtitleTemplates.ts');
|
const tsFilePath = path.join(__dirname, '../src/config/systemSubtitleTemplates.ts');
|
||||||
const jsonFilePath = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const jsonFilePath = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
|
|
||||||
console.log('读取 TypeScript 配置文件:', tsFilePath);
|
console.log('读取 TypeScript 配置文件:', tsFilePath);
|
||||||
const tsContent = fs.readFileSync(tsFilePath, 'utf-8');
|
const tsContent = fs.readFileSync(tsFilePath, 'utf-8');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const jsonFilePath = path.join(__dirname, '../electron/resources/extra/common/config/system-templates.json');
|
const jsonFilePath = path.join(__dirname, '../packaging/resources/common/config/system-templates.json');
|
||||||
const data = JSON.parse(fs.readFileSync(jsonFilePath, 'utf-8'));
|
const data = JSON.parse(fs.readFileSync(jsonFilePath, 'utf-8'));
|
||||||
|
|
||||||
console.log('验证字幕模板配置:\n');
|
console.log('验证字幕模板配置:\n');
|
||||||
|
|||||||
@@ -161,10 +161,10 @@
|
|||||||
<a-card :title="t('字体目录')" :bordered="false">
|
<a-card :title="t('字体目录')" :bordered="false">
|
||||||
<a-descriptions :column="1" bordered>
|
<a-descriptions :column="1" bordered>
|
||||||
<a-descriptions-item :label="t('预置字体目录')">
|
<a-descriptions-item :label="t('预置字体目录')">
|
||||||
<code class="text-xs">fonts/bundled/</code>
|
<code class="text-xs">packaging/vendor/fonts/bundled/</code>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item :label="t('ziti字体目录')">
|
<a-descriptions-item :label="t('ziti字体目录')">
|
||||||
<code class="text-xs">ziti/</code>
|
<code class="text-xs">packaging/vendor/ziti/</code>
|
||||||
<a-button size="mini" type="text" @click="openZitiDir" class="ml-2">
|
<a-button size="mini" type="text" @click="openZitiDir" class="ml-2">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-folder />
|
<icon-folder />
|
||||||
@@ -429,7 +429,7 @@ const openFontDir = async () => {
|
|||||||
const openZitiDir = async () => {
|
const openZitiDir = async () => {
|
||||||
try {
|
try {
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const zitiPath = path.join(process.cwd(), 'ziti');
|
const zitiPath = path.join(process.cwd(), 'packaging', 'vendor', 'ziti');
|
||||||
console.log('打开 ziti 目录:', zitiPath);
|
console.log('打开 ziti 目录:', zitiPath);
|
||||||
|
|
||||||
// 🔧 使用 $mapi 代替直接 require('electron')
|
// 🔧 使用 $mapi 代替直接 require('electron')
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const handleOpenConfigPath = async () => {
|
|||||||
<div class="bg-gray-50 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
|
<div class="bg-gray-50 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
|
||||||
<p class="font-semibold text-sm mb-2">{{ t('配置文件位置') }}</p>
|
<p class="font-semibold text-sm mb-2">{{ t('配置文件位置') }}</p>
|
||||||
<div class="text-xs bg-gray-200 dark:bg-gray-800 rounded p-2 font-mono break-all">
|
<div class="text-xs bg-gray-200 dark:bg-gray-800 rounded p-2 font-mono break-all">
|
||||||
electron/resources/extra/common/config/system-templates.json
|
packaging/resources/common/config/system-templates.json
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs text-gray-600 dark:text-gray-400 mt-2">
|
<p class="text-xs text-gray-600 dark:text-gray-400 mt-2">
|
||||||
此文件在应用打包时会被包含,所有系统模板都会被初始化。
|
此文件在应用打包时会被包含,所有系统模板都会被初始化。
|
||||||
|
|||||||
@@ -3,6 +3,15 @@ import packageJson from "../package.json";
|
|||||||
// import {TimeUtil} from "../electron/lib/util"; // ❌ 这是 Node.js 版本,会被打包到渲染进程
|
// import {TimeUtil} from "../electron/lib/util"; // ❌ 这是 Node.js 版本,会被打包到渲染进程
|
||||||
|
|
||||||
const BASE_URL = "";
|
const BASE_URL = "";
|
||||||
|
const viteEnv = import.meta.env || {};
|
||||||
|
const processEnv = (typeof process !== "undefined" ? process.env : {}) as Record<string, string | undefined>;
|
||||||
|
const authServerUrl = viteEnv.VITE_AUTH_SERVER_URL || processEnv.AUTH_SERVER_URL || "http://127.0.0.1:3302";
|
||||||
|
|
||||||
|
function normalizeApiBaseUrl(url: string): string {
|
||||||
|
const trimmed = String(url || "").trim().replace(/\/+$/, "");
|
||||||
|
if (!trimmed) return "http://127.0.0.1:3302/api";
|
||||||
|
return /\/api$/i.test(trimmed) ? trimmed : `${trimmed}/api`;
|
||||||
|
}
|
||||||
|
|
||||||
export const AppConfig = {
|
export const AppConfig = {
|
||||||
name: "天英超级IP智能体",
|
name: "天英超级IP智能体",
|
||||||
@@ -12,7 +21,7 @@ export const AppConfig = {
|
|||||||
website: ``,
|
website: ``,
|
||||||
websiteGithub: "",
|
websiteGithub: "",
|
||||||
websiteGitee: "",
|
websiteGitee: "",
|
||||||
apiBaseUrl: `http://127.0.0.1:3302/api`,
|
apiBaseUrl: normalizeApiBaseUrl(authServerUrl),
|
||||||
updaterUrl: ``,
|
updaterUrl: ``,
|
||||||
downloadUrl: ``,
|
downloadUrl: ``,
|
||||||
feedbackUrl: ``,
|
feedbackUrl: ``,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import systemTemplatesConfig from '../../electron/resources/extra/common/config/system-templates.json';
|
import systemTemplatesConfig from '../../packaging/resources/common/config/system-templates.json';
|
||||||
|
|
||||||
const rawSystemTemplates = Array.isArray(systemTemplatesConfig?.subtitleTemplates)
|
const rawSystemTemplates = Array.isArray(systemTemplatesConfig?.subtitleTemplates)
|
||||||
? systemTemplatesConfig.subtitleTemplates
|
? systemTemplatesConfig.subtitleTemplates
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ export class ZimuShengcheng {
|
|||||||
|
|
||||||
// 鑾峰彇璧勬簮璺緞
|
// 鑾峰彇璧勬簮璺緞
|
||||||
const path = await import('path');
|
const path = await import('path');
|
||||||
let resourcesPath = path.join(process.cwd(), 'electron/resources/extra');
|
let resourcesPath = path.join(process.cwd(), 'packaging/resources');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"productName": "天英超级IP智能体",
|
"productName": "天英超级IP智能体",
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "dist-release-final",
|
"output": "dist-release-final",
|
||||||
"buildResources": "electron/resources/build"
|
"buildResources": "packaging/build-resources"
|
||||||
},
|
},
|
||||||
"afterPack": "./scripts/build_optimize.cjs",
|
"afterPack": "./scripts/build_optimize.cjs",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -27,7 +27,17 @@
|
|||||||
"*.html"
|
"*.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts",
|
{
|
||||||
|
"from": "scripts",
|
||||||
|
"to": "scripts",
|
||||||
|
"filter": [
|
||||||
|
"**/*",
|
||||||
|
"!local-auth-server.cjs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"!server/**/*",
|
||||||
|
"!admin-panel/**/*",
|
||||||
|
"!local-auth-data/**/*",
|
||||||
"!node_modules/date-fns{,/**/*}",
|
"!node_modules/date-fns{,/**/*}",
|
||||||
"!node_modules/fluent-ffmpeg{,/**/*}",
|
"!node_modules/fluent-ffmpeg{,/**/*}",
|
||||||
"!node_modules/ttf2woff2{,/**/*}",
|
"!node_modules/ttf2woff2{,/**/*}",
|
||||||
@@ -102,7 +112,7 @@
|
|||||||
"!node_modules/wait-on{,/**/*}"
|
"!node_modules/wait-on{,/**/*}"
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "electron/resources/build/logo.ico",
|
"icon": "packaging/build-resources/logo.ico",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "dir",
|
"target": "dir",
|
||||||
@@ -114,7 +124,7 @@
|
|||||||
"artifactName": "${productName}-${version}-win-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-win-${arch}.${ext}",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra",
|
"from": "packaging/resources",
|
||||||
"to": "extra",
|
"to": "extra",
|
||||||
"filter": [
|
"filter": [
|
||||||
"common/default-config.json",
|
"common/default-config.json",
|
||||||
@@ -157,11 +167,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/voice-preview-cache",
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
"to": "voice-preview-cache"
|
"to": "voice-preview-cache"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/build",
|
"from": "packaging/build-resources",
|
||||||
"to": "build",
|
"to": "build",
|
||||||
"filter": [
|
"filter": [
|
||||||
"tray.png",
|
"tray.png",
|
||||||
@@ -185,9 +195,9 @@
|
|||||||
"warningsAsErrors": false,
|
"warningsAsErrors": false,
|
||||||
"runAfterFinish": true,
|
"runAfterFinish": true,
|
||||||
"allowElevation": true,
|
"allowElevation": true,
|
||||||
"installerIcon": "electron/resources/build/logo.ico",
|
"installerIcon": "packaging/build-resources/logo.ico",
|
||||||
"uninstallerIcon": "electron/resources/build/logo.ico",
|
"uninstallerIcon": "packaging/build-resources/logo.ico",
|
||||||
"installerHeaderIcon": "electron/resources/build/logo.ico"
|
"installerHeaderIcon": "packaging/build-resources/logo.ico"
|
||||||
},
|
},
|
||||||
"appx": {
|
"appx": {
|
||||||
"identityName": "IP",
|
"identityName": "IP",
|
||||||
@@ -200,7 +210,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"icon": "logo.icns",
|
"icon": "packaging/build-resources/logo.icns",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "dir",
|
"target": "dir",
|
||||||
@@ -213,7 +223,7 @@
|
|||||||
"artifactName": "${productName}-${version}-mac-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-mac-${arch}.${ext}",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra",
|
"from": "packaging/resources",
|
||||||
"to": "extra",
|
"to": "extra",
|
||||||
"filter": [
|
"filter": [
|
||||||
"common",
|
"common",
|
||||||
@@ -221,7 +231,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/voice-preview-cache",
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
"to": "voice-preview-cache"
|
"to": "voice-preview-cache"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -239,7 +249,7 @@
|
|||||||
"identity": "Xi'an Yanyi Information Technology Co., Ltd (Q96H3H33RK)"
|
"identity": "Xi'an Yanyi Information Technology Co., Ltd (Q96H3H33RK)"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"icon": "logo.icns",
|
"icon": "packaging/build-resources/logo.icns",
|
||||||
"desktop": "logo",
|
"desktop": "logo",
|
||||||
"maintainer": "天英超级IP智能体",
|
"maintainer": "天英超级IP智能体",
|
||||||
"category": "Utility",
|
"category": "Utility",
|
||||||
@@ -262,7 +272,7 @@
|
|||||||
"artifactName": "${productName}-${version}-linux-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-linux-${arch}.${ext}",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "electron/resources/extra",
|
"from": "packaging/resources",
|
||||||
"to": "extra",
|
"to": "extra",
|
||||||
"filter": [
|
"filter": [
|
||||||
"common",
|
"common",
|
||||||
@@ -270,7 +280,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "electron/resources/voice-preview-cache",
|
"from": "packaging/resources/voice-preview-cache",
|
||||||
"to": "voice-preview-cache"
|
"to": "voice-preview-cache"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||