diff --git a/.vscode/settings.json b/.vscode/settings.json index f9a9510..d051038 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "vscode.typescript-language-features" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" diff --git a/package-lock.json b/package-lock.json index 6b5a760..89e17f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "laitool", - "version": "2.2.12", + "version": "3.0.1-preview.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "laitool", - "version": "2.2.11", + "version": "3.0.1-preview.1", "hasInstallScript": true, "dependencies": { "@alicloud/alimt20181012": "^1.2.0", "@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/utils": "^3.0.0", - "@skit/x.naive-ui": "^0.15.0", "@vicons/ionicons5": "^0.12.0", "@vitejs/plugin-vue-jsx": "^3.1.0", "@volcengine/openapi": "^1.16.0", @@ -20,21 +19,24 @@ "artplayer": "^5.1.6", "awesome-js": "^2.0.0", "axios": "^1.6.5", - "baidu-aip-sdk": "^4.16.16", "blob-to-buffer": "^1.2.9", "compressing": "^1.10.0", + "compressorjs": "^1.2.1", "crypto-js": "^4.2.0", "electron-store": "^9.0.0", "electron-updater": "^6.1.7", "fluent-ffmpeg": "^2.1.3", "highlight.js": "^11.9.0", "install": "^0.13.0", - "jimp": "^0.22.10", + "jieba-js": "^1.0.2", "jsdom": "^24.0.0", "lodash": "^4.17.21", + "moment-timezone": "^0.5.45", "music-metadata": "^7.14.0", + "node-edge-tts": "^1.2.3", + "node-gyp": "^10.2.0", "node-machine-id": "^1.1.12", - "node-reg": "^0.2.4", + "node-pre-gyp": "^0.17.0", "npm": "^10.7.0", "paddle": "^1.0.0", "pinia": "^2.1.7", @@ -45,7 +47,6 @@ "uuid": "^9.0.1", "vue-router": "^4.2.5", "wav-file-info": "^0.0.10", - "winreg": "^1.2.5", "winston": "^3.13.0", "winston-daily-rotate-file": "^5.0.0", "ws": "^8.18.0" @@ -55,17 +56,16 @@ "@rushstack/eslint-patch": "^1.6.1", "@types/fluent-ffmpeg": "^2.1.24", "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^7.16.0", "@vitejs/plugin-vue": "^5.0.2", - "@vue/eslint-config-prettier": "^9.0.0", + "cross-env": "^7.0.3", "electron": "^28.1.1", "electron-builder": "^24.13.3", "electron-vite": "^2.0.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-plugin-vue": "^9.19.2", - "less": "^4.2.0", "naive-ui": "^2.38.2", "prettier": "^3.1.1", - "vfonts": "^0.0.3", "vite": "^5.0.11", "vue": "^3.4.5" } @@ -809,6 +809,7 @@ }, "node_modules/@babel/runtime": { "version": "7.23.8", + "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -870,6 +871,7 @@ }, "node_modules/@css-render/plugin-bem": { "version": "0.15.12", + "dev": true, "license": "MIT", "peerDependencies": { "css-render": "~0.15.12" @@ -877,6 +879,7 @@ }, "node_modules/@css-render/vue3-ssr": { "version": "0.15.12", + "dev": true, "license": "MIT", "peerDependencies": { "vue": "^3.0.11" @@ -1174,6 +1177,7 @@ }, "node_modules/@emotion/hash": { "version": "0.8.0", + "dev": true, "license": "MIT" }, "node_modules/@esbuild/win32-x64": { @@ -1280,9 +1284,10 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", + "version": "8.57.0", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -1359,7 +1364,6 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -1375,7 +1379,6 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1386,7 +1389,6 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1397,12 +1399,10 @@ }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -1418,7 +1418,6 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -1432,7 +1431,6 @@ }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -1446,367 +1444,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jimp/bmp": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "bmp-js": "^0.1.0" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/core": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "any-base": "^1.1.0", - "buffer": "^5.2.0", - "exif-parser": "^0.1.12", - "file-type": "^16.5.4", - "isomorphic-fetch": "^3.0.0", - "pixelmatch": "^4.0.2", - "tinycolor2": "^1.6.0" - } - }, - "node_modules/@jimp/custom": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/core": "^0.22.10" - } - }, - "node_modules/@jimp/gif": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "gifwrap": "^0.10.1", - "omggif": "^1.0.9" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/jpeg": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "jpeg-js": "^0.4.4" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-blit": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-blur": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-circle": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-color": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "tinycolor2": "^1.6.0" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-contain": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blit": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5", - "@jimp/plugin-scale": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-cover": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-crop": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5", - "@jimp/plugin-scale": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-crop": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-displace": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-dither": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-fisheye": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-flip": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-rotate": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-gaussian": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-invert": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-mask": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-normalize": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-print": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "load-bmfont": "^1.4.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blit": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-resize": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-rotate": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blit": ">=0.3.5", - "@jimp/plugin-crop": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-scale": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-shadow": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blur": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-threshold": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-color": ">=0.8.0", - "@jimp/plugin-resize": ">=0.8.0" - } - }, - "node_modules/@jimp/plugins": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/plugin-blit": "^0.22.10", - "@jimp/plugin-blur": "^0.22.10", - "@jimp/plugin-circle": "^0.22.10", - "@jimp/plugin-color": "^0.22.10", - "@jimp/plugin-contain": "^0.22.10", - "@jimp/plugin-cover": "^0.22.10", - "@jimp/plugin-crop": "^0.22.10", - "@jimp/plugin-displace": "^0.22.10", - "@jimp/plugin-dither": "^0.22.10", - "@jimp/plugin-fisheye": "^0.22.10", - "@jimp/plugin-flip": "^0.22.10", - "@jimp/plugin-gaussian": "^0.22.10", - "@jimp/plugin-invert": "^0.22.10", - "@jimp/plugin-mask": "^0.22.10", - "@jimp/plugin-normalize": "^0.22.10", - "@jimp/plugin-print": "^0.22.10", - "@jimp/plugin-resize": "^0.22.10", - "@jimp/plugin-rotate": "^0.22.10", - "@jimp/plugin-scale": "^0.22.10", - "@jimp/plugin-shadow": "^0.22.10", - "@jimp/plugin-threshold": "^0.22.10", - "timm": "^1.6.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/png": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/utils": "^0.22.10", - "pngjs": "^6.0.0" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/tiff": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "utif2": "^4.0.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/types": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "@jimp/bmp": "^0.22.10", - "@jimp/gif": "^0.22.10", - "@jimp/jpeg": "^0.22.10", - "@jimp/png": "^0.22.10", - "@jimp/tiff": "^0.22.10", - "timm": "^1.6.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/utils": { - "version": "0.22.10", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.3" - } - }, - "node_modules/@jimp/utils/node_modules/regenerator-runtime": { - "version": "0.13.11", - "license": "MIT" - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "license": "MIT", @@ -1847,6 +1484,7 @@ }, "node_modules/@juggle/resize-observer": { "version": "3.4.0", + "dev": true, "license": "Apache-2.0" }, "node_modules/@malept/cross-spawn-promise": { @@ -2026,26 +1664,91 @@ "node": ">= 8" } }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "dev": true, "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, - "node_modules/@pkgr/core": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "license": "BSD-3-Clause" @@ -2124,19 +1827,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@skit/x.naive-ui": { - "version": "0.15.0", - "license": "MIT", - "engines": { - "node": ">=18.12", - "npm": ">=8", - "pnpm": ">=8" - }, - "peerDependencies": { - "naive-ui": "^2.37.0", - "vue": "^3.2.0" - } - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "license": "MIT", @@ -2204,6 +1894,7 @@ }, "node_modules/@types/katex": { "version": "0.16.7", + "dev": true, "license": "MIT" }, "node_modules/@types/keyv": { @@ -2215,10 +1906,12 @@ }, "node_modules/@types/lodash": { "version": "4.14.202", + "dev": true, "license": "MIT" }, "node_modules/@types/lodash-es": { "version": "4.17.12", + "dev": true, "license": "MIT", "dependencies": { "@types/lodash": "*" @@ -2234,8 +1927,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.6", - "license": "MIT", + "version": "20.14.11", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dependencies": { "undici-types": "~5.26.4" } @@ -2276,6 +1970,219 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", + "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/type-utils": "7.16.0", + "@typescript-eslint/utils": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-7.16.0.tgz", + "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", + "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/utils": "7.16.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-7.16.0.tgz", + "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "dev": true, @@ -2532,19 +2439,6 @@ "version": "6.5.1", "license": "MIT" }, - "node_modules/@vue/eslint-config-prettier": { - "version": "9.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0" - }, - "peerDependencies": { - "eslint": ">= 8.0.0", - "prettier": ">= 3.0.0" - } - }, "node_modules/@vue/reactivity": { "version": "3.4.10", "license": "MIT", @@ -2610,9 +2504,7 @@ }, "node_modules/abbrev": { "version": "1.1.1", - "license": "ISC", - "optional": true, - "peer": true + "license": "ISC" }, "node_modules/acorn": { "version": "8.11.3", @@ -2643,8 +2535,21 @@ "node": ">= 6.0.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -2703,7 +2608,6 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -2711,7 +2615,6 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2723,10 +2626,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-base": { - "version": "1.1.0", - "license": "MIT" - }, "node_modules/app-builder-bin": { "version": "4.0.0", "dev": true, @@ -2966,6 +2865,15 @@ "version": "2.0.1", "license": "Python-2.0" }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/artplayer": { "version": "5.1.6", "resolved": "https://registry.npmmirror.com/artplayer/-/artplayer-5.1.6.tgz", @@ -2974,22 +2882,6 @@ "option-validator": "^2.0.6" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/async": { "version": "3.2.5", "license": "MIT" @@ -3004,6 +2896,7 @@ }, "node_modules/async-validator": { "version": "4.2.5", + "dev": true, "license": "MIT" }, "node_modules/asynckit": { @@ -3036,19 +2929,6 @@ "lodash.throttle": "^4.1.1" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmmirror.com/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.0", - "resolved": "https://registry.npmmirror.com/aws4/-/aws4-1.13.0.tgz", - "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==" - }, "node_modules/axios": { "version": "1.6.5", "license": "MIT", @@ -3058,48 +2938,8 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/baidu-aip-sdk": { - "version": "4.16.16", - "resolved": "https://registry.npmmirror.com/baidu-aip-sdk/-/baidu-aip-sdk-4.16.16.tgz", - "integrity": "sha512-dXjeQrd/eJIXDzBXNKArZZyFxf2boUI+XKEqb7yLUwnNiHJT3xDmHz30Oobe1LxOFFsvdH5pm72+pzi6MbXgmw==", - "dependencies": { - "debug": "^2.6.9", - "iconv-lite": "^0.4.24", - "request": "^2.88.2", - "underscore": "^1.12.1" - }, - "optionalDependencies": { - "mocha": "^4.0.1", - "should": "^13.2.0" - } - }, - "node_modules/baidu-aip-sdk/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/baidu-aip-sdk/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/baidu-aip-sdk/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/balanced-match": { "version": "1.0.2", - "devOptional": true, "license": "MIT" }, "node_modules/base64-js": { @@ -3120,14 +2960,6 @@ ], "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/big.js": { "version": "6.2.1", "resolved": "https://registry.npmmirror.com/big.js/-/big.js-6.2.1.tgz", @@ -3211,9 +3043,10 @@ "bluebird": "^3.5.5" } }, - "node_modules/bmp-js": { - "version": "0.1.0", - "license": "MIT" + "node_modules/blueimp-canvas-to-blob": { + "version": "3.29.0", + "resolved": "https://registry.npmmirror.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz", + "integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==" }, "node_modules/boolbase": { "version": "1.0.0", @@ -3227,17 +3060,22 @@ }, "node_modules/brace-expansion": { "version": "2.0.1", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha512-7Rfk377tpSM9TWBEeHs0FlDZGoAIei2V/4MdZJoFMBFAK6BqLpxAIUepGRHGdPFgGsLb02PXovC4qddyHvQqTg==", - "optional": true + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/browserslist": { "version": "4.22.2", @@ -3427,6 +3265,99 @@ "node": ">=8" } }, + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmmirror.com/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "license": "MIT", @@ -3502,11 +3433,6 @@ "node": ">=6" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmmirror.com/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, "node_modules/chalk": { "version": "4.1.2", "dev": true, @@ -3524,7 +3450,6 @@ }, "node_modules/chownr": { "version": "2.0.0", - "devOptional": true, "license": "ISC", "engines": { "node": ">=10" @@ -3549,9 +3474,16 @@ "node": ">=8" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, "node_modules/cliui": { "version": "8.0.1", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -3572,6 +3504,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color": { "version": "4.2.3", "license": "MIT", @@ -3724,9 +3664,17 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/compressorjs": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/compressorjs/-/compressorjs-1.2.1.tgz", + "integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==", + "dependencies": { + "blueimp-canvas-to-blob": "^3.29.0", + "is-blob": "^2.1.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", - "devOptional": true, "license": "MIT" }, "node_modules/conf": { @@ -3839,9 +3787,7 @@ }, "node_modules/console-control-strings": { "version": "1.1.0", - "license": "ISC", - "optional": true, - "peer": true + "license": "ISC" }, "node_modules/content-type": { "version": "1.0.5", @@ -3856,8 +3802,9 @@ }, "node_modules/copy-anything": { "version": "2.0.6", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-what": "^3.14.1" }, @@ -3865,6 +3812,11 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-decorators": { + "version": "0.20.0", + "resolved": "https://registry.npmmirror.com/core-decorators/-/core-decorators-0.20.0.tgz", + "integrity": "sha512-7cp/Pz3AmQXjRwhAsFN+8ndRiBNyLxtZgC/fhKvrwQTf2ZlZma6LnimoJPrOqgxZ0tIeI9VvSs+QKe0OPJ0SuA==" + }, "node_modules/core-util-is": { "version": "1.0.2", "license": "MIT" @@ -3894,9 +3846,34 @@ "node": ">= 10" } }, + "node_modules/crlf-normalize": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/crlf-normalize/-/crlf-normalize-1.0.20.tgz", + "integrity": "sha512-h/rBerTd3YHQGfv7tNT25mfhWvRq2BBLCZZ80GFarFxf6HQGbpW6iqDL3N+HBLpjLfAdcBXfWAzVlLfHkRUQBQ==", + "dependencies": { + "ts-type": ">=2" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3913,6 +3890,7 @@ }, "node_modules/css-render": { "version": "0.15.12", + "dev": true, "license": "MIT", "dependencies": { "@emotion/hash": "~0.8.0", @@ -3921,6 +3899,7 @@ }, "node_modules/css-render/node_modules/csstype": { "version": "3.0.11", + "dev": true, "license": "MIT" }, "node_modules/cssesc": { @@ -3948,17 +3927,6 @@ "version": "3.1.3", "license": "MIT" }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmmirror.com/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-urls": { "version": "5.0.0", "license": "MIT", @@ -4000,6 +3968,7 @@ }, "node_modules/date-fns": { "version": "2.30.0", + "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" @@ -4014,6 +3983,7 @@ }, "node_modules/date-fns-tz": { "version": "2.0.0", + "dev": true, "license": "MIT", "peerDependencies": { "date-fns": ">=2.0.0" @@ -4100,16 +4070,20 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "license": "MIT", + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "optional": true, "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -4137,9 +4111,7 @@ }, "node_modules/delegates": { "version": "1.0.0", - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/detect-libc": { "version": "2.0.2", @@ -4153,15 +4125,6 @@ "license": "MIT", "optional": true }, - "node_modules/diff": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", - "optional": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-compare": { "version": "3.3.0", "dev": true, @@ -4191,6 +4154,18 @@ "node": "*" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dmg-builder": { "version": "24.13.3", "dev": true, @@ -4262,9 +4237,6 @@ "node": ">=6.0.0" } }, - "node_modules/dom-walk": { - "version": "0.1.2" - }, "node_modules/dot-prop": { "version": "8.0.2", "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-8.0.2.tgz", @@ -4305,18 +4277,8 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "dev": true, "license": "MIT" }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/ejs": { "version": "3.1.9", "dev": true, @@ -4649,9 +4611,16 @@ } } }, + "node_modules/electron/node_modules/@types/node": { + "version": "18.19.41", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.41.tgz", + "integrity": "sha512-LX84pRJ+evD2e2nrgYCHObGWkiQJ1mL+meAgbvnwk/US6vmMY7S2ygBTGV2Jw91s9vUsLSXeDEkUHZIJGLrhsg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", - "devOptional": true, "license": "MIT" }, "node_modules/enabled": { @@ -4659,6 +4628,15 @@ "resolved": "https://registry.npmmirror.com/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "license": "MIT", @@ -4685,13 +4663,13 @@ }, "node_modules/err-code": { "version": "2.0.3", - "dev": true, "license": "MIT" }, "node_modules/errno": { "version": "0.1.8", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "prr": "~1.0.1" }, @@ -4699,6 +4677,27 @@ "errno": "cli.js" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "optional": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es6-error": { "version": "4.1.1", "license": "MIT", @@ -4759,15 +4758,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", + "version": "8.57.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -4812,46 +4812,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, "node_modules/eslint-plugin-vue": { "version": "9.20.0", "dev": true, @@ -5033,11 +4993,9 @@ }, "node_modules/evtd": { "version": "0.2.4", + "dev": true, "license": "MIT" }, - "node_modules/exif-parser": { - "version": "0.1.12" - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", @@ -5046,10 +5004,10 @@ "node": ">=6" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "node_modules/extract-zip": { "version": "2.0.1", @@ -5069,25 +5027,41 @@ "@types/yauzl": "^2.9.1" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "license": "MIT" }, - "node_modules/fast-diff": { - "version": "1.3.0", + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, - "license": "Apache-2.0" + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", + "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -5164,6 +5138,18 @@ "minimatch": "^5.0.1" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -5254,7 +5240,6 @@ }, "node_modules/foreground-child": { "version": "3.1.1", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -5267,14 +5252,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmmirror.com/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, "node_modules/form-data": { "version": "4.0.0", "license": "MIT", @@ -5305,7 +5282,6 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", - "devOptional": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -5316,7 +5292,6 @@ }, "node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", - "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -5327,12 +5302,10 @@ }, "node_modules/fs-minipass/node_modules/yallist": { "version": "4.0.0", - "devOptional": true, "license": "ISC" }, "node_modules/fs.realpath": { "version": "1.0.0", - "devOptional": true, "license": "ISC" }, "node_modules/function-bind": { @@ -5378,22 +5351,26 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "license": "MIT", + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "optional": true, "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5415,22 +5392,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmmirror.com/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/gifwrap": { - "version": "0.10.1", - "license": "MIT", - "dependencies": { - "image-q": "^4.0.0", - "omggif": "^1.0.10" - } - }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz", @@ -5438,7 +5399,6 @@ }, "node_modules/glob": { "version": "7.2.3", - "devOptional": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -5468,7 +5428,6 @@ }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", - "devOptional": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -5477,7 +5436,6 @@ }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", - "devOptional": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -5486,14 +5444,6 @@ "node": "*" } }, - "node_modules/global": { - "version": "4.4.0", - "license": "MIT", - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, "node_modules/global-agent": { "version": "3.0.0", "license": "BSD-3-Clause", @@ -5561,6 +5511,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "license": "MIT", @@ -5604,36 +5574,6 @@ "dev": true, "license": "MIT" }, - "node_modules/growl": { - "version": "1.10.3", - "resolved": "https://registry.npmmirror.com/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "optional": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmmirror.com/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -5643,11 +5583,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "license": "MIT", + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "optional": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5677,9 +5618,7 @@ }, "node_modules/has-unicode": { "version": "2.0.1", - "license": "ISC", - "optional": true, - "peer": true + "license": "ISC" }, "node_modules/hasown": { "version": "2.0.0", @@ -5692,15 +5631,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/he/-/he-1.1.1.tgz", - "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==", - "optional": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/highlight.js": { "version": "11.9.0", "license": "BSD-3-Clause", @@ -5772,20 +5702,6 @@ "node": ">= 6" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/http2-wrapper": { "version": "1.0.3", "license": "MIT", @@ -5816,13 +5732,6 @@ "debug": "^4.1.1" } }, - "node_modules/httpx/node_modules/@types/node": { - "version": "20.11.30", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "license": "MIT", @@ -5852,28 +5761,47 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.0", + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/image-q": { - "version": "4.0.0", - "license": "MIT", + "node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", "dependencies": { - "@types/node": "16.9.1" + "minimatch": "^3.0.4" } }, - "node_modules/image-q/node_modules/@types/node": { - "version": "16.9.1", - "license": "MIT" + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, "node_modules/image-size": { "version": "0.5.5", "license": "MIT", "optional": true, + "peer": true, "bin": { "image-size": "bin/image-size.js" }, @@ -5898,15 +5826,21 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", - "devOptional": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -5936,10 +5870,33 @@ "node": ">= 4.5.0" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "license": "MIT" }, + "node_modules/is-blob": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-blob/-/is-blob-2.1.0.tgz", + "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-ci": { "version": "3.0.1", "dev": true, @@ -5961,16 +5918,11 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/is-function": { - "version": "1.0.2", - "license": "MIT" - }, "node_modules/is-glob": { "version": "4.0.3", "dev": true, @@ -5982,6 +5934,20 @@ "node": ">=0.10.0" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "dev": true, @@ -6004,15 +5970,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, "node_modules/is-what": { "version": "3.14.1", - "devOptional": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/isarray": { "version": "1.0.0", @@ -6033,19 +5995,6 @@ "version": "2.0.0", "license": "ISC" }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, "node_modules/jackspeak": { "version": "2.3.6", "dev": true, @@ -6100,24 +6049,15 @@ "node": "*" } }, - "node_modules/jimp": { - "version": "0.22.10", - "license": "MIT", + "node_modules/jieba-js": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/jieba-js/-/jieba-js-1.0.2.tgz", + "integrity": "sha512-kLKw1P7bAuFRq5xoBqddx0S8NO0rW7M90GEbYCZ/Xuok4Y8ncRdOrdeZbOZIC4OTGXUZVkPTqw3AGlFvJmf9ww==", "dependencies": { - "@jimp/custom": "^0.22.10", - "@jimp/plugins": "^0.22.10", - "@jimp/types": "^0.22.10", - "regenerator-runtime": "^0.13.3" + "core-decorators": "^0.20.0", + "crlf-normalize": "^1.0.1" } }, - "node_modules/jimp/node_modules/regenerator-runtime": { - "version": "0.13.11", - "license": "MIT" - }, - "node_modules/jpeg-js": { - "version": "0.4.4", - "license": "BSD-3-Clause" - }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -6133,9 +6073,9 @@ } }, "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, "node_modules/jsdom": { "version": "24.0.0", @@ -6263,13 +6203,9 @@ "version": "3.0.1", "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", + "dev": true, "license": "MIT" }, "node_modules/json-schema-typed": { @@ -6284,7 +6220,8 @@ }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/json5": { "version": "2.2.3", @@ -6303,20 +6240,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "license": "MIT", @@ -6396,8 +6319,9 @@ }, "node_modules/less": { "version": "4.2.0", - "devOptional": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -6423,6 +6347,7 @@ "version": "1.6.0", "license": "MIT", "optional": true, + "peer": true, "bin": { "mime": "cli.js" }, @@ -6442,37 +6367,6 @@ "node": ">= 0.8.0" } }, - "node_modules/load-bmfont": { - "version": "1.4.1", - "license": "MIT", - "dependencies": { - "buffer-equal": "0.0.1", - "mime": "^1.3.4", - "parse-bmfont-ascii": "^1.0.3", - "parse-bmfont-binary": "^1.0.5", - "parse-bmfont-xml": "^1.1.4", - "phin": "^2.9.1", - "xhr": "^2.0.1", - "xtend": "^4.0.0" - } - }, - "node_modules/load-bmfont/node_modules/buffer-equal": { - "version": "0.0.1", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/load-bmfont/node_modules/mime": { - "version": "1.6.0", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/locate-path": { "version": "6.0.0", "dev": true, @@ -6493,6 +6387,7 @@ }, "node_modules/lodash-es": { "version": "4.17.21", + "dev": true, "license": "MIT" }, "node_modules/lodash.defaults": { @@ -6595,6 +6490,7 @@ "version": "2.1.0", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -6607,10 +6503,41 @@ "version": "5.7.2", "license": "ISC", "optional": true, + "peer": true, "bin": { "semver": "bin/semver" } }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/matcher": { "version": "3.0.0", "license": "MIT", @@ -6629,6 +6556,28 @@ "node": ">= 0.8" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "2.6.0", "license": "MIT", @@ -6674,12 +6623,6 @@ "node": ">=4" } }, - "node_modules/min-document": { - "version": "2.19.0", - "dependencies": { - "dom-walk": "^0.1.0" - } - }, "node_modules/minimatch": { "version": "5.1.6", "dev": true, @@ -6700,15 +6643,137 @@ }, "node_modules/minipass": { "version": "5.0.0", - "devOptional": true, "license": "ISC", "engines": { "node": ">=8" } }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/minizlib": { "version": "2.1.2", - "devOptional": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -6720,7 +6785,6 @@ }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", - "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -6731,12 +6795,10 @@ }, "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", - "devOptional": true, "license": "ISC" }, "node_modules/mkdirp": { "version": "1.0.4", - "devOptional": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -6750,141 +6812,6 @@ "resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, - "node_modules/mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", - "optional": true, - "dependencies": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/mocha/node_modules/commander": { - "version": "2.11.0", - "resolved": "https://registry.npmmirror.com/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "optional": true - }, - "node_modules/mocha/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "optional": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "optional": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmmirror.com/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", - "optional": true - }, - "node_modules/mocha/node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "optional": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "optional": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "optional": true, - "dependencies": { - "has-flag": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz", @@ -6893,6 +6820,17 @@ "node": "*" } }, + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "license": "MIT" @@ -6921,6 +6859,7 @@ "version": "2.38.2", "resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.38.2.tgz", "integrity": "sha512-WhZ+6DW61aYSmFyfH7evcSGFmd2xR68Yq1mNRrVdJwBhZsnNdAUsMN9IeNCVEPMCND/jzYZghkStoNoR5Xa09g==", + "dev": true, "dependencies": { "@css-render/plugin-bem": "^0.15.12", "@css-render/vue3-ssr": "^0.15.12", @@ -6982,6 +6921,7 @@ "version": "3.3.1", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -6993,6 +6933,14 @@ "node": ">= 4.4.x" } }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-abi": { "version": "3.65.0", "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.65.0.tgz", @@ -7015,6 +6963,42 @@ "node": ">=10" } }, + "node_modules/node-edge-tts": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/node-edge-tts/-/node-edge-tts-1.2.3.tgz", + "integrity": "sha512-ug2osAv7qihpbcuuhWWrnDbqDZ0q7AcIHtRumQ/PJaXV6QR2sI/1bBlUWLNk0tTF6Y6CWCYuBoHYYBQztRterg==", + "dependencies": { + "https-proxy-agent": "^7.0.1", + "ws": "^8.13.0", + "yargs": "^17.7.2" + }, + "bin": { + "node-edge-tts": "bin.js" + } + }, + "node_modules/node-edge-tts/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/node-edge-tts/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "license": "MIT", @@ -7033,15 +7017,421 @@ } } }, + "node_modules/node-gyp": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/node-machine-id": { "version": "1.1.12", "license": "MIT" }, - "node_modules/node-reg": { - "version": "0.2.4", - "license": "ISC", + "node_modules/node-pre-gyp": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/node-pre-gyp/-/node-pre-gyp-0.17.0.tgz", + "integrity": "sha512-abzZt1hmOjkZez29ppg+5gGqdPLUuJeAEwVPtHYEJgx0qzttCbcKFpxrCQn2HYbwCv2c+7JwH4BgEzFkUGpn4A==", + "deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future", "dependencies": { - "q": "^1.4.1" + "detect-libc": "^1.0.3", + "mkdirp": "^0.5.5", + "needle": "^2.5.2", + "nopt": "^4.0.3", + "npm-packlist": "^1.4.8", + "npmlog": "^4.1.2", + "rc": "^1.2.8", + "rimraf": "^2.7.1", + "semver": "^5.7.1", + "tar": "^4.4.13" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/node-pre-gyp/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-pre-gyp/node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/node-pre-gyp/node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/node-pre-gyp/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/node-pre-gyp/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/node-pre-gyp/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/node-pre-gyp/node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/node-pre-gyp/node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmmirror.com/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/node-pre-gyp/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-pre-gyp/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-pre-gyp/node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/node-pre-gyp/node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/node-pre-gyp/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/node-pre-gyp/node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmmirror.com/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/node-pre-gyp/node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/node-pre-gyp/node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/node-pre-gyp/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/node-pre-gyp/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/node-pre-gyp/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/node-pre-gyp/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-pre-gyp/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/node-pre-gyp/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/node-pre-gyp/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/node-pre-gyp/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-pre-gyp/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-pre-gyp/node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmmirror.com/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" } }, "node_modules/node-releases": { @@ -7241,6 +7631,29 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "node_modules/npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmmirror.com/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "dependencies": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "inBundle": true, @@ -9528,23 +9941,21 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/nwsapi": { "version": "2.2.7", "license": "MIT" }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmmirror.com/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -9565,10 +9976,6 @@ "node": ">= 0.4" } }, - "node_modules/omggif": { - "version": "1.0.10", - "license": "MIT" - }, "node_modules/once": { "version": "1.4.0", "license": "ISC", @@ -9608,6 +10015,32 @@ "node": ">= 0.8.0" } }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "license": "MIT", @@ -9642,6 +10075,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==" + }, "node_modules/paddle": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/paddle/-/paddle-1.0.0.tgz", @@ -9650,10 +10102,6 @@ "node": "*" } }, - "node_modules/pako": { - "version": "1.0.11", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "dev": true, @@ -9665,30 +10113,11 @@ "node": ">=6" } }, - "node_modules/parse-bmfont-ascii": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/parse-bmfont-binary": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/parse-bmfont-xml": { - "version": "1.1.4", - "license": "MIT", - "dependencies": { - "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.4.5" - } - }, - "node_modules/parse-headers": { - "version": "2.0.5", - "license": "MIT" - }, "node_modules/parse-node-version": { "version": "1.0.1", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.10" } @@ -9718,7 +10147,6 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9726,22 +10154,21 @@ }, "node_modules/path-key": { "version": "3.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { - "version": "1.10.1", - "dev": true, - "license": "BlueOak-1.0.0", + "version": "1.11.1", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -9749,12 +10176,20 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.2.0", - "dev": true, "license": "ISC", "engines": { "node": "14 || >=16.14" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/peek-readable": { "version": "4.1.0", "license": "MIT", @@ -9770,23 +10205,27 @@ "version": "1.2.0", "license": "MIT" }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/phin": { - "version": "2.9.3", - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.0.0", "license": "ISC" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pify": { "version": "4.0.1", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -9841,23 +10280,6 @@ } } }, - "node_modules/pixelmatch": { - "version": "4.0.2", - "license": "ISC", - "dependencies": { - "pngjs": "^3.0.0" - }, - "bin": { - "pixelmatch": "bin/pixelmatch" - } - }, - "node_modules/pixelmatch/node_modules/pngjs": { - "version": "3.4.0", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/plist": { "version": "3.1.0", "dev": true, @@ -9871,13 +10293,6 @@ "node": ">=10.4.0" } }, - "node_modules/pngjs": { - "version": "6.0.0", - "license": "MIT", - "engines": { - "node": ">=12.13.0" - } - }, "node_modules/postcss": { "version": "8.4.33", "funding": [ @@ -9987,22 +10402,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/process": { - "version": "0.11.10", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/process-nextick-args": { @@ -10018,7 +10423,6 @@ }, "node_modules/promise-retry": { "version": "2.0.1", - "dev": true, "license": "MIT", "dependencies": { "err-code": "^2.0.2", @@ -10057,7 +10461,8 @@ "node_modules/prr": { "version": "1.0.1", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/psl": { "version": "1.9.0", @@ -10078,22 +10483,6 @@ "node": ">=6" } }, - "node_modules/q": { - "version": "1.5.1", - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/querystringify": { "version": "2.2.0", "license": "MIT" @@ -10227,76 +10616,11 @@ }, "node_modules/regenerator-runtime": { "version": "0.14.1", + "dev": true, "license": "MIT" }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmmirror.com/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10338,7 +10662,6 @@ }, "node_modules/retry": { "version": "0.12.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -10493,6 +10816,7 @@ }, "node_modules/seemly": { "version": "0.3.8", + "dev": true, "license": "MIT" }, "node_modules/semver": { @@ -10523,9 +10847,7 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "license": "ISC", - "optional": true, - "peer": true + "license": "ISC" }, "node_modules/sharp": { "version": "0.33.2", @@ -10594,7 +10916,6 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -10605,69 +10926,13 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmmirror.com/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "optional": true, - "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "optional": true, - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", - "optional": true, - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", - "optional": true - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "optional": true, - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "optional": true - }, "node_modules/signal-exit": { "version": "4.1.0", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -10777,10 +11042,65 @@ "dev": true, "license": "ISC" }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/sm3": { "version": "1.0.3", "license": "MIT" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "devOptional": true, @@ -10807,31 +11127,25 @@ }, "node_modules/sprintf-js": { "version": "1.1.3", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmmirror.com/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" + "minipass": "^7.0.3" }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/stack-trace": { @@ -10866,7 +11180,6 @@ }, "node_modules/string-width": { "version": "4.2.3", - "devOptional": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -10880,7 +11193,6 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -10893,7 +11205,6 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "devOptional": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -10905,7 +11216,6 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -10973,21 +11283,6 @@ "version": "3.2.4", "license": "MIT" }, - "node_modules/synckit": { - "version": "0.8.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/systeminformation": { "version": "5.22.10", "resolved": "https://registry.npmmirror.com/systeminformation/-/systeminformation-5.22.10.tgz", @@ -11014,9 +11309,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "devOptional": true, - "license": "ISC", + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -11112,7 +11407,6 @@ }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", - "devOptional": true, "license": "ISC" }, "node_modules/temp-file": { @@ -11208,18 +11502,10 @@ "dev": true, "license": "MIT" }, - "node_modules/timm": { - "version": "1.7.1", - "license": "MIT" - }, "node_modules/tiny-typed-emitter": { "version": "2.1.0", "license": "MIT" }, - "node_modules/tinycolor2": { - "version": "1.6.0", - "license": "MIT" - }, "node_modules/tmp": { "version": "0.2.3", "dev": true, @@ -11247,6 +11533,18 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/token-types": { "version": "4.2.1", "license": "MIT", @@ -11288,6 +11586,7 @@ }, "node_modules/treemate": { "version": "0.3.11", + "dev": true, "license": "MIT" }, "node_modules/triple-beam": { @@ -11306,9 +11605,39 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmmirror.com/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "peer": true + }, + "node_modules/ts-type": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ts-type/-/ts-type-3.0.1.tgz", + "integrity": "sha512-cleRydCkBGBFQ4KAvLH0ARIkciduS745prkGVVxPGvcRGhMMoSJUB7gNR1ByKhFTEYrYRg2CsMRGYnqp+6op+g==", + "dependencies": { + "@types/node": "*", + "tslib": ">=2", + "typedarray-dts": "^1.0.0" + }, + "peerDependencies": { + "ts-toolbelt": "^9.6.0" + } + }, "node_modules/tslib": { "version": "2.6.2", - "devOptional": true, "license": "0BSD" }, "node_modules/tunnel-agent": { @@ -11322,11 +11651,6 @@ "node": "*" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmmirror.com/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, "node_modules/type-check": { "version": "0.4.0", "dev": true, @@ -11349,6 +11673,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typedarray-dts": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/typedarray-dts/-/typedarray-dts-1.0.0.tgz", + "integrity": "sha512-Ka0DBegjuV9IPYFT1h0Qqk5U4pccebNIJCGl8C5uU7xtOs+jpJvKGAY4fHGK25hTmXZOEUl9Cnsg5cS6K/b5DA==" + }, "node_modules/typescript": { "version": "5.3.3", "devOptional": true, @@ -11372,15 +11701,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmmirror.com/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" - }, "node_modules/undici-types": { "version": "5.26.5", "license": "MIT" }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/universalify": { "version": "0.1.2", "license": "MIT", @@ -11436,13 +11782,6 @@ "dev": true, "license": "WTFPL" }, - "node_modules/utif2": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "pako": "^1.0.11" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "license": "MIT" @@ -11460,6 +11799,7 @@ }, "node_modules/vdirs": { "version": "0.1.8", + "dev": true, "license": "MIT", "dependencies": { "evtd": "^0.2.2" @@ -11468,24 +11808,6 @@ "vue": "^3.0.11" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vfonts": { - "version": "0.0.3", - "dev": true, - "license": "MIT" - }, "node_modules/vite": { "version": "5.0.11", "license": "MIT", @@ -11541,6 +11863,7 @@ }, "node_modules/vooks": { "version": "0.2.12", + "dev": true, "license": "MIT", "dependencies": { "evtd": "^0.2.2" @@ -11636,6 +11959,7 @@ }, "node_modules/vueuc": { "version": "0.4.58", + "dev": true, "license": "MIT", "dependencies": { "@css-render/vue3-ssr": "^0.15.10", @@ -11685,10 +12009,6 @@ "node": ">=18" } }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "license": "MIT" - }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "license": "MIT", @@ -11711,7 +12031,6 @@ }, "node_modules/which": { "version": "2.0.2", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -11726,16 +12045,10 @@ "node_modules/wide-align": { "version": "1.1.5", "license": "ISC", - "optional": true, - "peer": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/winreg": { - "version": "1.2.5", - "license": "BSD-2-Clause" - }, "node_modules/winston": { "version": "3.13.0", "resolved": "https://registry.npmmirror.com/winston/-/winston-3.13.0.tgz", @@ -11789,7 +12102,6 @@ }, "node_modules/wrap-ansi": { "version": "7.0.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -11806,7 +12118,6 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -11844,16 +12155,6 @@ } } }, - "node_modules/xhr": { - "version": "2.6.0", - "license": "MIT", - "dependencies": { - "global": "~4.4.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, "node_modules/xml-name-validator": { "version": "4.0.0", "dev": true, @@ -11862,10 +12163,6 @@ "node": ">=12" } }, - "node_modules/xml-parse-from-string": { - "version": "1.0.1", - "license": "MIT" - }, "node_modules/xml2js": { "version": "0.4.23", "license": "MIT", @@ -11905,7 +12202,6 @@ }, "node_modules/y18n": { "version": "5.0.8", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -11917,7 +12213,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -11934,7 +12229,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -12001,4 +12295,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 9c01634..c5b3383 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "2.2.12", + "version": "3.0.1-preview.1", "description": "An AI tool for image processing, video processing, and other functions.", "main": "./out/main/index.js", "author": "laitool.cn", @@ -9,7 +9,7 @@ "format": "prettier --write .", "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", "start": "electron-vite preview", - "dev": "electron-vite dev --watch", + "dev": "cross-env chcp 65001 && cross-env NODE_ENV=development electron-vite dev --watch", "build": "electron-vite build", "postinstall": "electron-builder install-app-deps", "build:win": "npm run build && electron-builder --win --config", @@ -20,7 +20,6 @@ "@alicloud/alimt20181012": "^1.2.0", "@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/utils": "^3.0.0", - "@skit/x.naive-ui": "^0.15.0", "@vicons/ionicons5": "^0.12.0", "@vitejs/plugin-vue-jsx": "^3.1.0", "@volcengine/openapi": "^1.16.0", @@ -28,21 +27,24 @@ "artplayer": "^5.1.6", "awesome-js": "^2.0.0", "axios": "^1.6.5", - "baidu-aip-sdk": "^4.16.16", "blob-to-buffer": "^1.2.9", "compressing": "^1.10.0", + "compressorjs": "^1.2.1", "crypto-js": "^4.2.0", "electron-store": "^9.0.0", "electron-updater": "^6.1.7", "fluent-ffmpeg": "^2.1.3", "highlight.js": "^11.9.0", "install": "^0.13.0", - "jimp": "^0.22.10", + "jieba-js": "^1.0.2", "jsdom": "^24.0.0", "lodash": "^4.17.21", + "moment-timezone": "^0.5.45", "music-metadata": "^7.14.0", + "node-edge-tts": "^1.2.3", + "node-gyp": "^10.2.0", "node-machine-id": "^1.1.12", - "node-reg": "^0.2.4", + "node-pre-gyp": "^0.17.0", "npm": "^10.7.0", "paddle": "^1.0.0", "pinia": "^2.1.7", @@ -53,7 +55,6 @@ "uuid": "^9.0.1", "vue-router": "^4.2.5", "wav-file-info": "^0.0.10", - "winreg": "^1.2.5", "winston": "^3.13.0", "winston-daily-rotate-file": "^5.0.0", "ws": "^8.18.0" @@ -63,17 +64,16 @@ "@rushstack/eslint-patch": "^1.6.1", "@types/fluent-ffmpeg": "^2.1.24", "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^7.16.0", "@vitejs/plugin-vue": "^5.0.2", - "@vue/eslint-config-prettier": "^9.0.0", + "cross-env": "^7.0.3", "electron": "^28.1.1", "electron-builder": "^24.13.3", "electron-vite": "^2.0.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-plugin-vue": "^9.19.2", - "less": "^4.2.0", "naive-ui": "^2.38.2", "prettier": "^3.1.1", - "vfonts": "^0.0.3", "vite": "^5.0.11", "vue": "^3.4.5" }, @@ -98,4 +98,4 @@ "icon": "./resources/icon.ico" } } -} \ No newline at end of file +} diff --git a/resources/icon.ico b/resources/icon.ico index f064692..f930d2a 100644 Binary files a/resources/icon.ico and b/resources/icon.ico differ diff --git a/resources/icon_1.ico b/resources/icon_1.ico new file mode 100644 index 0000000..f064692 Binary files /dev/null and b/resources/icon_1.ico differ diff --git a/resources/package/Improve/ouput/00000-1616097796-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png b/resources/package/Improve/ouput/00000-1616097796-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png new file mode 100644 index 0000000..83ea84b Binary files /dev/null and b/resources/package/Improve/ouput/00000-1616097796-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png differ diff --git a/resources/package/Improve/output/00000-1616097796-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png b/resources/package/Improve/output/00000-1616097796-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png deleted file mode 100644 index d5da84c..0000000 Binary files a/resources/package/Improve/output/00000-1616097796-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png and /dev/null differ diff --git a/resources/package/Improve/output/00001-1083368712-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png b/resources/package/Improve/output/00001-1083368712-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png deleted file mode 100644 index 7a318d7..0000000 Binary files a/resources/package/Improve/output/00001-1083368712-(masterpiece, best quality, a young man emperor with a strong and handsome face domineering and majestic long black hair wearing.png and /dev/null differ diff --git a/resources/package/Improve/output/00002-763798052-(masterpiece, best quality, a group of courtiers in fancy robes faced the young emperor in a chinese-style palace, and the minis.png b/resources/package/Improve/output/00002-763798052-(masterpiece, best quality, a group of courtiers in fancy robes faced the young emperor in a chinese-style palace, and the minis.png deleted file mode 100644 index 82dbdbc..0000000 Binary files a/resources/package/Improve/output/00002-763798052-(masterpiece, best quality, a group of courtiers in fancy robes faced the young emperor in a chinese-style palace, and the minis.png and /dev/null differ diff --git a/resources/package/Improve/output/00003-2561272579-(masterpiece, best quality,a young face stalwart handsome domineering majestic long black hair wearing a gorgeous robe of a man.png b/resources/package/Improve/output/00003-2561272579-(masterpiece, best quality,a young face stalwart handsome domineering majestic long black hair wearing a gorgeous robe of a man.png deleted file mode 100644 index b7029ac..0000000 Binary files a/resources/package/Improve/output/00003-2561272579-(masterpiece, best quality,a young face stalwart handsome domineering majestic long black hair wearing a gorgeous robe of a man.png and /dev/null differ diff --git a/resources/package/Improve/output/00004-1447729042-(masterpiece, best quality, a young face stalwart handsome domineering majestic long black hair wearing gorgeous robes of a man.png b/resources/package/Improve/output/00004-1447729042-(masterpiece, best quality, a young face stalwart handsome domineering majestic long black hair wearing gorgeous robes of a man.png deleted file mode 100644 index 5d9c20f..0000000 Binary files a/resources/package/Improve/output/00004-1447729042-(masterpiece, best quality, a young face stalwart handsome domineering majestic long black hair wearing gorgeous robes of a man.png and /dev/null differ diff --git a/resources/scripts/db/book.realm b/resources/scripts/db/book.realm index 1d71f87..3ec4f47 100644 Binary files a/resources/scripts/db/book.realm and b/resources/scripts/db/book.realm differ diff --git a/resources/scripts/db/book.realm.lock b/resources/scripts/db/book.realm.lock index fafaa4d..71ff5b5 100644 Binary files a/resources/scripts/db/book.realm.lock and b/resources/scripts/db/book.realm.lock differ diff --git a/resources/scripts/db/software.realm.lock b/resources/scripts/db/software.realm.lock index 7cc572a..ac4cace 100644 Binary files a/resources/scripts/db/software.realm.lock and b/resources/scripts/db/software.realm.lock differ diff --git a/resources/tmp/Clip/track_text_segments_temp.json b/resources/tmp/Clip/track_text_segments_temp.json index 609b0ee..765f4f8 100644 --- a/resources/tmp/Clip/track_text_segments_temp.json +++ b/resources/tmp/Clip/track_text_segments_temp.json @@ -59,5 +59,5 @@ "value": 1.0 }, "visible": true, - "volume": 1.0 + "volumn": 1.0 } diff --git a/resources/tmp/Clip/tracks_audio_segments_tmp.json b/resources/tmp/Clip/tracks_audio_segments_tmp.json index eaa4543..9c90d4d 100644 --- a/resources/tmp/Clip/tracks_audio_segments_tmp.json +++ b/resources/tmp/Clip/tracks_audio_segments_tmp.json @@ -47,5 +47,5 @@ "track_render_index": 0, "uniform_scale": null, "visible": true, - "volume": 1.0 + "volumn": 1.0 } diff --git a/resources/tmp/Clip/tracks_segments_tmp.json b/resources/tmp/Clip/tracks_segments_tmp.json index e7d64f4..1f2f15c 100644 --- a/resources/tmp/Clip/tracks_segments_tmp.json +++ b/resources/tmp/Clip/tracks_segments_tmp.json @@ -69,5 +69,5 @@ "value": 1.0 }, "visible": true, - "volume": 1.0 + "volumn": 1.0 } \ No newline at end of file diff --git a/src/api/sdApi.js b/src/api/sdApi.js index e463ab4..5bc1345 100644 --- a/src/api/sdApi.js +++ b/src/api/sdApi.js @@ -1,91 +1,87 @@ -import { basicApi } from "./apiBasic"; -import { define } from "../define/define"; +import { basicApi } from './apiBasic' +import { define } from '../define/define' import { promises as fspromises } from 'fs' -import { errorMessage, successMessage } from "../main/generalTools"; - export class SdApi { - constructor() { - this.baseUrl = global.config?.webui_api_url; - this.sd_setting = null; + constructor() { + this.baseUrl = global.config?.webui_api_url + this.sd_setting = null + } + + /** + * 获取当前SD的服务器中所有的lora信息 + * @returns + */ + async getAllLoras(baseURL = null) { + let url = this.baseUrl + 'sdapi/v1/loras' + if (baseURL != null) { + url = baseURL + 'sdapi/v1/loras' } + return await basicApi.get(url) + } - /** - * 获取当前SD的服务器中所有的lora信息 - * @returns - */ - async getAllLoras(baseURL = null) { - let url = this.baseUrl + "sdapi/v1/loras"; - if (baseURL != null) { - url = baseURL + "sdapi/v1/loras"; - } - return await basicApi.get(url); + /** + * 获取当前的所有的checkpoint模型 + * @param {*} baseURL + */ + async getAllSDModel(baseURL = null) { + let url = this.baseUrl + 'sdapi/v1/sd-models' + if (baseURL != null) { + url = baseURL + 'sdapi/v1/sd-models' } + return await basicApi.get(url) + } - /** - * 获取当前的所有的checkpoint模型 - * @param {*} baseURL - */ - async getAllSDModel(baseURL = null) { - let url = this.baseUrl + "sdapi/v1/sd-models"; - if (baseURL != null) { - url = baseURL + "sdapi/v1/sd-models"; - } - return await basicApi.get(url); + /** + * 获取当前连接的所有的samplers(采样器) + * @param {*} baseURL + * @returns + */ + async getAllSamplers(baseURL = null) { + try { + let url = this.baseUrl + 'sdapi/v1/samplers' + if (baseURL != null) { + url = baseURL + 'sdapi/v1/samplers' + } + return await basicApi.get(url) + } catch (error) { + throw error } + } - /** - * 获取当前连接的所有的samplers(采样器) - * @param {*} baseURL - * @returns - */ - async getAllSamplers(baseURL = null) { - try { + async txt2img(data, baseURL = null) { + try { + if (this.sd_setting == null) { + this.sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')) + this.baseUrl = this.sd_setting.setting.webui_api_url + } - let url = this.baseUrl + "sdapi/v1/samplers"; - if (baseURL != null) { - url = baseURL + "sdapi/v1/samplers"; - } - return await basicApi.get(url); - } catch (error) { - throw error; - } - } - - async txt2img(data, baseURL = null) { - try { - - if (this.sd_setting == null) { - this.sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); - this.baseUrl = this.sd_setting.setting.webui_api_url; - } - - // 加上通用前缀 - data.prompt = this.sd_setting.webui.prompt + data.prompt - - data.negative_prompt = this.sd_setting.webui.negative_prompt; - data.sampler_name = this.sd_setting.webui.sampler_name; - data.cfg_scale = this.sd_setting.webui.cfg_scale; - data.n_iter = 1; - data.steps = this.sd_setting.webui.steps; - data.save_images = false; - data.batch_size = data.batch_size ? data.batch_size : 1; - - if (data.width == null) { - data.width = 512; - } - if (data.height == null) { - data.height = 512; - } - - let url = this.baseUrl + "sdapi/v1/txt2img"; - if (baseURL != null) { - url = baseURL + "sdapi/v1/txt2img"; - } - let res = await basicApi.post(url, data); - return res; - } catch (error) { - throw error; - } + // 加上通用前缀 + data.prompt = this.sd_setting.webui.prompt + data.prompt + + data.negative_prompt = this.sd_setting.webui.negative_prompt + data.sampler_name = this.sd_setting.webui.sampler_name + data.cfg_scale = this.sd_setting.webui.cfg_scale + data.n_iter = 1 + data.steps = this.sd_setting.webui.steps + data.save_images = false + data.batch_size = data.batch_size ? data.batch_size : 1 + + if (data.width == null) { + data.width = 512 + } + if (data.height == null) { + data.height = 512 + } + + let url = this.baseUrl + 'sdapi/v1/txt2img' + if (baseURL != null) { + url = baseURL + 'sdapi/v1/txt2img' + } + let res = await basicApi.post(url, data) + return res + } catch (error) { + throw error } + } } diff --git a/src/define/Tools/common.js b/src/define/Tools/common.js deleted file mode 100644 index 6772cea..0000000 --- a/src/define/Tools/common.js +++ /dev/null @@ -1,3 +0,0 @@ -import fs from 'node:fs'; - -const fspromises = fs.promises; diff --git a/src/define/Tools/common.ts b/src/define/Tools/common.ts new file mode 100644 index 0000000..af35846 --- /dev/null +++ b/src/define/Tools/common.ts @@ -0,0 +1,11 @@ + +/** + * 检查字符串中是不是包含中文或者标点符号 + * @param str 需要判断的字符串 + * @returns 返回的数据,有中文或者标点符号返回true,否则返回false + */ +export function ContainsChineseOrPunctuation(str: string): boolean { + return /[\u4e00-\u9fa5]|[\u3000-\u301e\u2013\u2014\u2018\u2019\u201c\u201d\u2026\u203b\uff08\uff09\uff1a\uff1b\uff1f\uff01\uff0c\u3001\uff0e\u3002\uff1f\uff01\u2018\u2019\u201c\u201d]/.test( + str + ) +} diff --git a/src/define/Tools/file.js b/src/define/Tools/file.ts similarity index 80% rename from src/define/Tools/file.js rename to src/define/Tools/file.ts index 86c0fe7..133541e 100644 --- a/src/define/Tools/file.js +++ b/src/define/Tools/file.ts @@ -34,7 +34,7 @@ export async function CheckFolderExistsOrCreate(folderPath) { * @param {*} subPath 子目录的消息 * @returns */ -export function JoinPath(rootPath, subPath) { +export function JoinPath(rootPath: string, subPath: string): string { // 判断第二个地址是不是存在,不存在返回null,存在返回拼接后的地址 if (subPath && !isEmpty(subPath)) { return path.resolve(rootPath, subPath) @@ -46,8 +46,9 @@ export function JoinPath(rootPath, subPath) { /** * 删除指定的文件中里面所有的文件和文件夹 * @param {*} folderPath 文件夹地址 + * @param {*} isDeleteOut 是否删除最外层的文件夹,默认false,不删除 */ -export async function DeleteFolderAllFile(folderPath) { +export async function DeleteFolderAllFile(folderPath: string, isDeleteOut: boolean = false): Promise { try { let folderIsExist = await CheckFileOrDirExist(folderPath) if (!folderIsExist) { @@ -55,17 +56,22 @@ export async function DeleteFolderAllFile(folderPath) { } // 开始删除 let files = await fspromises.readdir(folderPath) - files.forEach(async (file) => { - const curPath = path.join(folderPath, file) - if ((await fspromises.stat(curPath)).isDirectory()) { + for (const file of files) { + const curPath = path.join(folderPath, file); + const stat = await fspromises.stat(curPath); + if (stat.isDirectory()) { // 判断是不是文件夹 - await DeleteFolderAllFile(curPath) // 递归删除文件夹内容 - fspromises.rmdir(curPath) // 删除空文件夹 + await DeleteFolderAllFile(curPath); // 递归删除文件夹内容 + await fspromises.rmdir(curPath); // 删除空文件夹 } else { // 删除文件 - fspromises.unlink(curPath) + await fspromises.unlink(curPath); } - }) + } + // 判断是不是要删除最外部的文件夹 + if (isDeleteOut) { + await fspromises.rmdir(folderPath) + } } catch (error) { throw error } @@ -142,7 +148,7 @@ export async function IsDirectory(path) { * @param {*} source_path 源文件/文件夹地址 * @param {*} target_path 目标文件/文件夹地址 */ -export async function BackupFileOrFolder(source_path, target_path) { +export async function BackupFileOrFolder(source_path: string, target_path: string): Promise { try { // 判断父文件夹是否存在,不存在创建 const parent_path = path.dirname(target_path) @@ -158,7 +164,7 @@ export async function BackupFileOrFolder(source_path, target_path) { await fspromises.rename(source_path, target_path) } else { // 复制文件 - await fspromises.copyFile(source, target) + await fspromises.copyFile(source_path, target_path) } } catch (error) { throw new Error(error) @@ -171,7 +177,7 @@ export async function BackupFileOrFolder(source_path, target_path) { * @param {*} extensions 拓展地址 * @returns 返回文件中指定的后缀文件地址(绝对地址) */ -export async function GetFilesWithExtensions(folderPath, extensions) { +export async function GetFilesWithExtensions(folderPath: string, extensions: string[]): Promise { try { // 判断当前是不是文件夹 if (!(await IsDirectory(folderPath))) { @@ -213,3 +219,20 @@ export async function GetFilesWithExtensions(folderPath, extensions) { throw new Error(error) } } + +/** + * 获取文件的大小 + * @param filePath 文件的地址 + * @returns 返回的文件大小为 kb单位 + */ +export async function GetFileSize(filePath: string): Promise { + try { + if (!(await CheckFileOrDirExist(filePath))) { + throw new Error("获取文件大小,指定的文件不存在"); + } + const stats = await fspromises.stat(filePath); + return stats.size / 1024 + } catch (error) { + throw error + } +} diff --git a/src/define/Tools/image.js b/src/define/Tools/image.js deleted file mode 100644 index f88c466..0000000 --- a/src/define/Tools/image.js +++ /dev/null @@ -1,72 +0,0 @@ - -import sharp from "sharp"; -import { CheckFileOrDirExist } from "./file" - - -/** - * 将指定的图片的尺寸修改,返回修改后的图片数据(base64或buffer) - * @param {*} image_path - * @param {*} width - * @param {*} height - * @param {*} type - * @returns 返回修改后的图片数据(base64或buffer) - */ -export async function ResizeImage(image_path, width, height, type) { - try { - // 检查 type 参数 - if (type !== 'base64' && type !== 'buffer') { - throw new Error('type 参数必须是 "base64" 或 "buffer"'); - } - - // 判断是不是图片文件 - if (!image_path.match(/\.(jpg|jpeg|png)$/)) { - throw new Error("输入的文件地址不是图片文件地址"); - } - - // 判断文件是否存在 - if (!(await CheckFileOrDirExist(image_path))) { - throw new Error("文件不存在"); - } - - // 修改图片尺寸 - const image = sharp(image_path); - image.resize(width, height); - let data = await image.toBuffer(); - if (type === 'base64') { - return data.toString('base64'); - } else { - return data; - } - } catch (error) { - throw error; - } -} - -/** - * 获取指定图片文件的宽高 - * @param {*} image_path 图片文件的路径 - * @returns 返回以一个对象,包含width和height属性 - */ - -export async function GetImageSize(image_path) { - try { - // 判断文件是否存在 - if (!(await CheckFileOrDirExist(image_path))) { - throw new Error("文件不存在"); - } - - // 判断是不是图片文件 - if (!image_path.match(/\.(jpg|jpeg|png)$/)) { - throw new Error("输入的文件地址不是图片文件地址"); - } - - // 获取图片的宽高 - const metadata = await sharp(image_path).metadata(); - return { - width: metadata.width, - height: metadata.height - } - } catch (error) { - throw error; - } -} \ No newline at end of file diff --git a/src/define/Tools/image.ts b/src/define/Tools/image.ts new file mode 100644 index 0000000..5b9b57c --- /dev/null +++ b/src/define/Tools/image.ts @@ -0,0 +1,282 @@ +import path from 'path' +import sharp from 'sharp' +import { CheckFileOrDirExist } from './file' +import fs from 'fs' +import https from 'https' +import Compressor from 'compressorjs'; + +/** + * 将指定的图片的尺寸修改,返回修改后的图片数据(base64或buffer) + * @param {*} image_path + * @param {*} width + * @param {*} height + * @param {*} type + * @returns 返回修改后的图片数据(base64或buffer) + */ +export async function ResizeImage(image_path: string, width: number | sharp.ResizeOptions, height: number, type: string) { + try { + // 检查 type 参数 + if (type !== 'base64' && type !== 'buffer') { + throw new Error('type 参数必须是 "base64" 或 "buffer"') + } + + // 判断是不是图片文件 + if (!image_path.match(/\.(jpg|jpeg|png)$/)) { + throw new Error('输入的文件地址不是图片文件地址') + } + + // 判断文件是否存在 + if (!(await CheckFileOrDirExist(image_path))) { + throw new Error('文件不存在') + } + + // 修改图片尺寸 + const image = sharp(image_path) + image.resize(width, height) + let data = await image.toBuffer() + if (type === 'base64') { + return data.toString('base64') + } else { + return data + } + } catch (error) { + throw error + } +} + +/** + * 获取指定图片文件的宽高 + * @param {*} image_path 图片文件的路径 + * @returns 返回以一个对象,包含width和height属性 + */ + +export async function GetImageSize(image_path: string) { + try { + // 判断文件是否存在 + if (!(await CheckFileOrDirExist(image_path))) { + throw new Error('文件不存在') + } + + // 判断是不是图片文件 + if (!image_path.match(/\.(jpg|jpeg|png)$/)) { + throw new Error('输入的文件地址不是图片文件地址') + } + + // 获取图片的宽高 + const metadata = await sharp(image_path).metadata() + return { + width: metadata.width, + height: metadata.height + } + } catch (error) { + throw error + } +} + +/** + * 根据文件扩展名获取MIME类型 + * @param filePath 文件路径 + * @returns MIME类型字符串 + */ +export function GetMimeType(filePath: string): string { + const extension = path.extname(filePath).toLowerCase(); + const mimeTypes: { [key: string]: string } = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + // 添加更多文件类型和对应的MIME类型 + }; + return mimeTypes[extension] || 'application/octet-stream'; +} + +/** + * 将本地文件地址或网络图片地址转换为包含MIME类型的base64字符串 + * @param url 本地文件路径或网络图片URL + * @returns Promise 返回一个Promise,解析为包含MIME类型的base64字符串 + */ +export function GetImageBase64(url: string): Promise { + if (!url) { + return Promise.reject('URL不能为空'); + } + if (url.startsWith('http://') || url.startsWith('https://')) { + return new Promise((resolve, reject) => { + https.get(url, (response) => { + const mimeType = response.headers['content-type'] || 'application/octet-stream'; + const data: any[] = []; + response.on('data', (chunk) => data.push(chunk)); + response.on('end', () => { + const buffer = Buffer.concat(data); + const base64Data = `data:${mimeType};base64,${buffer.toString('base64')}`; + resolve(base64Data); + }); + }).on('error', (err) => reject(err)); + }); + } else { + return new Promise((resolve, reject) => { + fs.readFile(url, (err, data) => { + if (err) { + reject(err); + } else { + const mimeType = GetMimeType(url); + const base64Data = `data:${mimeType};base64,${data.toString('base64')}`; + resolve(base64Data); + } + }); + }); + } +} + +/** + * 压缩图片到指定的大小 + * @param file 图片的Blob对象 + * @param maxSizeInBytes 最大文件大小,单位字节 + * @returns 返回一个Promise,解析为压缩后的Blob对象 + */ +export function CompressImageToSize(base64: string, maxSizeInBytes: number): Promise { + let mimeType = this.getMimeType(base64); + let byteCharacters = atob(base64.split(',')[1]); + let byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + let byteArray = new Uint8Array(byteNumbers); + let imageBlob = new Blob([byteArray], { type: mimeType }); + + return new Promise((resolve, reject) => { + const compress = (quality: number) => { + new Compressor(imageBlob as Blob, { + quality, + success(result) { + if (result.size <= maxSizeInBytes || quality <= 0.1) { + resolve(result); + } else { + // 递归降低质量 + compress(quality - 0.1); + } + }, + error(err) { + reject(err); + }, + }); + }; + // 从较高的质量开始 + compress(0.9); + }); +} + +/** + * 生成图片蒙板 + * 将选中的区域涂白,其他区域涂黑(这个颜色可以变) + * @param inputPath 输入的文件路径 + * @param outputPath 输出的文件路径 + * @param region 范围对象,包含x, y, width, height属性 + * @param markColor 标记颜色,默认为黑色 { r: 255, g: 255, b: 255 } + * @param backColor 背景颜色,默认为白色 { r: 0, g: 0, b: 0 } + */ +export async function ProcessImage(inputPath: string, + outputPath: string, + region: { width: any; height: any; x: any; y: any }, + markColor: { r: number; g: number; b: number } = { r: 255, g: 255, b: 255 }, + backColor: { r: number; g: number; b: number } = { r: 0, g: 0, b: 0 }, +): Promise { + try { + // 读取图片并进行处理 + const image = sharp(inputPath); + + // 获取图片的元数据 + const { width, height } = await image.metadata(); + + // 创建一个新的空白图片,背景为白色 + const whiteBackground = await sharp({ + create: { + width, + height, + channels: 3, // RGB channels + background: backColor // White color + } + }).png().toBuffer(); + + // 创建一个黑色的矩形 + const blackRegion = await sharp({ + create: { + width: region.width, + height: region.height, + channels: 3, // RGB channels + background: markColor // Black color + } + }).png().toBuffer(); + + // 在白色背景上叠加黑色矩形 + await sharp(whiteBackground) + .composite([ + { + input: blackRegion, + left: region.x, + top: Math.floor(region.y) + } + ]) + .toFile(outputPath); + + + } catch (err) { + throw err; + } +} + +/** + * 将图片的base64写到本地文件 + * @param base64 base64字符串 + * @param outFilePath 写出的文件路径 + */ +export async function Base64ToFile(base64: string, outFilePath: string): Promise { + try { + let base64Data = base64.replace(/^data:image\/\w+;base64,/, '') + let dataBuffer = Buffer.from(base64Data, 'base64') + let out_folder = path.dirname(outFilePath) + await this.tools.checkFolderExistsOrCreate(out_folder) + await fs.promises.writeFile(outFilePath, dataBuffer) + // await this.tools.writeArrayToFile(dataBuffer, out_file); + } catch (error) { + throw new Error('将base64转换为文件失败,失败信息如下:' + error.toString()) + } +} + +/** + * 将图片分割为4份 + * @param inputPath 输入的文件路径 + * @param reName 重命名的名字,用做新文件名的前缀 + * @param outputDir 输出的文件夹路径 + * @returns + */ +export async function ImageSplit(inputPath: string, reName: string, outputDir: string) { + try { + const metadata = await sharp(inputPath).metadata() + const smallWidth = metadata.width / 2 + const smallHeight = metadata.height / 2 + let times = new Date().getTime() + let imgs = [] + + for (let i = 0; i < 4; i++) { + const xOffset = i % 2 === 0 ? 0 : smallWidth + const yOffset = Math.floor(i / 2) * smallHeight + let out_file = path.join(outputDir, `/${reName}_${times}_${i}.png`) + await sharp(inputPath) + .extract({ + left: xOffset, + top: yOffset, + width: smallWidth, + height: smallHeight + }) + .resize(smallWidth, smallHeight) + .toFile(out_file) + + imgs.push(out_file) + } + return imgs + } catch (err) { + throw err + } +} + diff --git a/src/define/Tools/index.js b/src/define/Tools/index.ts similarity index 68% rename from src/define/Tools/index.js rename to src/define/Tools/index.ts index 87d8c66..53ea6ab 100644 --- a/src/define/Tools/index.js +++ b/src/define/Tools/index.ts @@ -1,9 +1,11 @@ import * as image from './image'; import * as common from './common'; import * as file from './file' +import * as validate from './validate'; export { image, common, - file + file, + validate }; \ No newline at end of file diff --git a/src/define/Tools/time.js b/src/define/Tools/time.ts similarity index 87% rename from src/define/Tools/time.js rename to src/define/Tools/time.ts index 25e6cab..4155abe 100644 --- a/src/define/Tools/time.js +++ b/src/define/Tools/time.ts @@ -43,3 +43,12 @@ export function MillisecondsToTimeString(milliseconds) { timeString = timeString.replace(/(\.\d+)\./g, '$1') return timeString } + +/** + * 延时多少秒,返回一个Promise + * @param time 延时时间,单位毫秒 + * @returns viod + */ +export async function TimeDelay(time: number): Promise { + return new Promise(resolve => setTimeout(resolve, time)); +} \ No newline at end of file diff --git a/src/define/Tools/validate.ts b/src/define/Tools/validate.ts new file mode 100644 index 0000000..9260f55 --- /dev/null +++ b/src/define/Tools/validate.ts @@ -0,0 +1,15 @@ + +/** + * 校验是不是可以进行JSON解析 + * @param str 要解析的字符串 + * @returns 可以解析返回true,否则返回false + */ +export function ValidateJson(str: string): boolean { + + try { + JSON.parse(str); + return true + } catch (e) { + return false; + } +} \ No newline at end of file diff --git a/src/define/api/apiUrlDefine.js b/src/define/api/apiUrlDefine.js index 79368ed..5074582 100644 --- a/src/define/api/apiUrlDefine.js +++ b/src/define/api/apiUrlDefine.js @@ -28,35 +28,6 @@ let apiUrl = [ mj_url: null, buy_url: null }, - { - label: 'DrawAPI(MJ)', - value: '2cabf684-ac48-4733-a427-8c41626f7d8f', - gpt_url: null, - mj_url: { - imagine: 'https://mjapi.deepwl.net/api/mj/submit/imagine', - describe: 'https://mjapi.deepwl.net/api/mj/submit/describe', - update_file: 'https://mjapi.deepwl.net/api/mj/submit/upload-discord-images', - once_get_task: 'https://mjapi.deepwl.net/api/mj/query/task/${id}', - get_task_list: 'https://mjapi.deepwl.net/api/mj/task/list-by-condition' - }, - d3_url: null, - buy_url: 'https://mjapi.deepwl.net/#/home' - }, - { - label: 'ePhoneAPI', - value: 'b8866543-8c27-4888-869c-00aa1eb31272', - gpt_url: 'https://api.ephone.ai/v1/chat/completions', - mj_url: { - imagine: 'https://api.ephone.ai/mj/submit/imagine', - describe: 'https://api.ephone.ai/mj/submit/describe', - update_file: 'https://api.ephone.ai/mj/submit/upload-discord-images', - once_get_task: 'https://api.ephone.ai/mj/task/${id}/fetch' - }, - d3_url: { - image: 'https://api.ephone.ai/v1/images/generations' - }, - buy_url: 'https://ephone.ai/register?aff=55XT' - }, { label: 'KIMI', value: 'b5c8c8c5-f3c4-4c88-b25c-7f5a3d5f9d1f', diff --git a/src/define/db/model/Book/BookBackTaskListModel.ts b/src/define/db/model/Book/BookBackTaskListModel.ts index d57269c..3661582 100644 --- a/src/define/db/model/Book/BookBackTaskListModel.ts +++ b/src/define/db/model/Book/BookBackTaskListModel.ts @@ -13,6 +13,8 @@ export class BookBackTaskList extends Realm.Object { executeType: TaskExecuteType // 任务执行类型,手动还是自动 createTime: Date updateTime: Date + startTime: number + endTime: number static schema: ObjectSchema = { name: 'BookBackTaskList', @@ -27,7 +29,9 @@ export class BookBackTaskList extends Realm.Object { errorMessage: 'string?', executeType: { type: 'string', default: TaskExecuteType.AUTO }, createTime: 'date', - updateTime: 'date' + updateTime: 'date', + startTime: 'int', + endTime: 'int' }, primaryKey: 'id' } diff --git a/src/define/db/model/Book/book.ts b/src/define/db/model/Book/book.ts index b2b8910..2190710 100644 --- a/src/define/db/model/Book/book.ts +++ b/src/define/db/model/Book/book.ts @@ -1,4 +1,5 @@ -import Realm, { ObjectSchema } from 'realm' +// @ts-ignore +import Realm from 'realm' import { BookType } from '../../../enum/bookEnum' export class BookModel extends Realm.Object { @@ -11,12 +12,21 @@ export class BookModel extends Realm.Object { oldVideoPath: string | null srtPath: string | null audioPath: string | null + draftSrtStyle: string | null // 草稿字幕样式 + backgroundMusic: string | null // 背景音乐ID + friendlyReminder: string | null // 友情提示 updateTime: Date createTime: Date version: string + imageStyle: string[] | null // 软件内置的样式 + autoAnalyzeCharacter: string | null // 自动分析角色设置 + customizeImageStyle: string[] | null // 自定义的样式 + videoConfig: string | null // 合成视频设置 + prefixPrompt: string | null // 前缀 + suffixPrompt: string | null // 后缀 subtitlePosition: string | null - static schema: ObjectSchema = { + static schema: Realm.ObjectSchema = { name: 'Book', properties: { id: 'string', @@ -27,10 +37,19 @@ export class BookModel extends Realm.Object { oldVideoPath: 'string?', srtPath: 'string?', audioPath: 'string?', + draftSrtStyle : 'string?', + backgroundMusic: 'string?', + friendlyReminder: 'string?', imageFolder: 'string?', updateTime: 'date', createTime: 'date', version: 'string', + imageStyle: 'string?[]', + autoAnalyzeCharacter: 'string?', + customizeImageStyle: 'string?[]', + videoConfig: "string?", + prefixPrompt: "string?", + suffixPrompt: "string?", subtitlePosition: 'string?' }, // 主键为_id diff --git a/src/define/db/model/Book/bookTask.ts b/src/define/db/model/Book/bookTask.ts index b839635..110d75d 100644 --- a/src/define/db/model/Book/bookTask.ts +++ b/src/define/db/model/Book/bookTask.ts @@ -1,5 +1,40 @@ import Realm, { ObjectSchema } from 'realm' -import { BookTaskStatus, BookType } from '../../../enum/bookEnum' +import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum' + +export class ImageDefineModel extends Realm.Object { + label: string + key: string + value: string + children: string + type: string + prompt: string + image_url: string + cref_cw: number + lora: string + chinese_prompt: string + lora_weight: number + show_image: string + isShow: true + static schema: ObjectSchema = { + name: 'ImageDefine', + properties: { + label: 'string', + key: 'string', + value: 'string', + children: 'string', + type: 'string', + prompt: 'string', + image_url: 'string', + cref_cw: 'int', + lora: 'string', + chinese_prompt: 'string', + lora_weight: 'int', + show_image: 'string', + isShow: 'bool' + }, + primaryKey: 'key' + } +} export class BookTaskModel extends Realm.Object { id: string @@ -9,14 +44,24 @@ export class BookTaskModel extends Realm.Object { generateVideoPath: string | null srtPath: string | null audioPath: string | null + draftSrtStyle: string | null // 草稿字幕样式 + backgroundMusic: string | null // 背景音乐ID + friendlyReminder: string | null // 友情提示 imageFolder: string | null - styleList: Realm.List | null - prefix: string | null + imageStyle: string[] | null // 软件内置的样式 + autoAnalyzeCharacter: string | null // 自动分析角色设置 + customizeImageStyle: string[] | null // 自定义的样式 + videoConfig: string | null // 合成视频设置 + prefixPrompt: string | null // 前缀 + suffixPrompt: string | null // 后缀 + styleList: string[] | null status: BookTaskStatus errorMsg: string | null isAuto: boolean // 是否自动 updateTime: Date createTime: Date + imageCategory: BookImageCategory // 图片出图方式 + subImageFolder: string[] | null // 出图的子文件夹 static schema: ObjectSchema = { name: 'BookTask', @@ -28,14 +73,23 @@ export class BookTaskModel extends Realm.Object { generateVideoPath: 'string?', srtPath: 'string?', audioPath: 'string?', + draftSrtStyle: 'string?', + backgroundMusic: 'string?', + friendlyReminder: 'string?', imageFolder: 'string?', - styleList: 'string[]', - prefix: 'string?', + subImageFolder: "string?[]", + imageStyle: 'string?[]', + autoAnalyzeCharacter: 'string?', + customizeImageStyle: 'string?[]', + videoConfig: "string?", + prefixPrompt: "string?", + suffixPrompt: "string?", status: 'string', errorMsg: 'string?', isAuto: 'bool', updateTime: 'date', - createTime: 'date' + createTime: 'date', + imageCategory: 'string', }, // 主键为_id primaryKey: 'id' diff --git a/src/define/db/model/Book/bookTaskDetail.ts b/src/define/db/model/Book/bookTaskDetail.ts index c77644a..327cfe2 100644 --- a/src/define/db/model/Book/bookTaskDetail.ts +++ b/src/define/db/model/Book/bookTaskDetail.ts @@ -5,8 +5,8 @@ import { BookTaskStatus, BookType, MJAction, - MJCategroy } from '../../../enum/bookEnum' +import { MJImageType } from '../../../enum/mjEnum' export class Subtitle extends Realm.Object { startTime: number @@ -29,7 +29,7 @@ export class MJMessage extends Realm.Object { id: string mjApiUrl: string | null progress: number - category: MJCategroy + category: MJImageType imageClick: string | null // 图片点击(显示的小的) imageShow: string | null // 图片实际的地址 messageId: string // 消息ID(可以是MJ中的,也可以是API中的) @@ -105,6 +105,26 @@ export class SDConfig extends Realm.Object { } } +// 放反推的提示词的对象 +export class ReversePrompt extends Realm.Object { + id: string + bookTaskDetailId: string + prompt: string + promptCN: string + isSelect: boolean + static schema: ObjectSchema = { + name: 'ReversePrompt', + properties: { + id: 'string', + bookTaskDetailId: "string", + prompt: 'string', + promptCN: 'string', + isSelect: 'bool' + }, + primaryKey: 'id' + } +} + export class BookTaskDetailModel extends Realm.Object { id: string no: number @@ -119,16 +139,19 @@ export class BookTaskDetailModel extends Realm.Object { startTime: number | null // 开始时间 endTime: number | null // 结束时间 timeLimit: string | null // 事件实现(0 -- 3000) - subValue: Realm.List | null // 包含的字幕数据 + subValue: string | null // 包含的字幕数据 characterTags: string[] | null // 角色标签 gptPrompt: string | null // GPT提示词 mjMessage: MJMessage | null // MJ消息 outImagePath: string | null // 输出图片地址 subImagePath: string[] | null // 子图片地址 + imageLock: boolean // 图片锁 prompt: string | null // 提示 adetailer: boolean // 是否开启修脸 sdConifg: SDConfig | null // SD配置 + reversePrompt: ReversePrompt[] | null // 反推的提示词(数组) subtitlePosition: string | null // 字幕位置 + status: BookTaskStatus createTime: Date updateTime: Date @@ -148,16 +171,19 @@ export class BookTaskDetailModel extends Realm.Object { startTime: 'int?', endTime: 'int?', timeLimit: 'string?', - subValue: { type: 'list', objectType: 'Subtitle' }, + subValue: 'string?', + reversePrompt: { type: 'list', objectType: 'ReversePrompt' }, characterTags: { type: 'list', objectType: 'string' }, gptPrompt: 'string?', mjMessage: 'MJMessage?', outImagePath: 'string?', subImagePath: 'string[]', + imageLock: 'bool', prompt: 'string?', adetailer: 'bool', sdConifg: 'SDConfig?', subtitlePosition: 'string?', + status: "string", createTime: 'date', updateTime: 'date' }, diff --git a/src/define/db/model/SoftWare/software.ts b/src/define/db/model/SoftWare/software.ts index ecc3b5f..e6f66df 100644 --- a/src/define/db/model/SoftWare/software.ts +++ b/src/define/db/model/SoftWare/software.ts @@ -11,6 +11,8 @@ export class SoftwareModel extends Realm.Object { ttsSetting: string | null // TTS设置的json字符串 writeSetting: string | null // 文案的相关配置的json字符串 aiSetting: string | null // AI相关的配置的json字符串 + watermarkSetting: string | null // 水印相关的配置的json字符串 + translationSetting: string | null // 翻译相关的配置的json字符串 static schema: ObjectSchema = { name: 'Software', @@ -23,7 +25,9 @@ export class SoftwareModel extends Realm.Object { globalSetting: 'string', ttsSetting: 'string?', // 可空 writeSetting: 'string?', - aiSetting: 'string?' + aiSetting: 'string?', + watermarkSetting: 'string?', + translationSetting: 'string?' }, // 主键为_id primaryKey: 'id' diff --git a/src/define/db/service/Book/bookBackTaskListService.ts b/src/define/db/service/Book/bookBackTaskListService.ts index 2775ba6..feaa05d 100644 --- a/src/define/db/service/Book/bookBackTaskListService.ts +++ b/src/define/db/service/Book/bookBackTaskListService.ts @@ -1,21 +1,16 @@ import Realm from 'realm' -import path from 'path' -import { BaseService } from '../baseService.js' -import { define } from '../../../define.js' -import { BookTaskModel } from '../../model/Book/bookTask.js' import { BookBackTaskStatus, BookBackTaskType, - BookTaskStatus, TaskExecuteType } from '../../../enum/bookEnum.js' -import { errorMessage, successMessage } from '../../../../main/generalTools.js' +import { errorMessage, successMessage } from '../../../../main/Public/generalTools' import { BaseRealmService } from './bookBasic' import { isEmpty } from 'lodash' -import { DefaultObject } from 'realm/dist/public-types/schema.js' -import { BookModel } from '../../model/Book/book.js' import { OtherData } from '../../../enum/softwareEnum.js' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js' +import { Book } from '../../../../model/book.js' +import { GeneralResponse } from '../../../../model/generalResponse.js' const { v4: uuidv4 } = require('uuid') export class BookBackTaskListService extends BaseRealmService { @@ -44,7 +39,7 @@ export class BookBackTaskListService extends BaseRealmService { * bookId 和 status 必填一个 * @param query bookId,bookTaskId,name,type,status */ - GetBookBackTaskList(query) { + GetBookBackTaskList(query: Book.QueryBookBackTaskCondition) { try { if (query == null) { throw new Error('查询后台队列任务失败,没有查询条件') @@ -53,33 +48,36 @@ export class BookBackTaskListService extends BaseRealmService { throw new Error('查询后台队列任务失败,没有查询条件') } - // 构建查询条件 - // 下面时可空的条件 - let queryString = '' - if (query.bookId) { - queryString = `bookId = ${query.bookId}` - } - if (query.bookTaskId) { - queryString += ` && bookTaskId = ${query.bookTaskId}` - } - if (query.name) { - queryString += ` && name = ${query.name}` - } - if (query.type) { - queryString += ` && type = ${query.type}` - } - if (query.status) { - queryString += ` && status = ${query.status}` - } - if (query.executeType) { - queryString += ` && executeType = ${query.executeType}` - } - // 获取数据 let tasks = this.realm .objects('BookBackTaskList') - .filtered(queryString) .sorted('createTime', true) + + + // 构建查询条件 + if (query.id) { + tasks.filtered('id = $0', query.id) + } + if (query.bookId) { + tasks.filtered('bookId = $0', query.bookId) + } + if (query.bookTaskId) { + tasks.filtered('bookTaskId = $0', query.bookTaskId) + } + if (query.name) { + tasks.filtered('name = $0', query.name) + } + if (query.type) { + tasks.filtered('type = $0', query.type) + } + if (query.status) { + tasks.filtered('status = $0', query.status) + } + if (query.executeType) { + tasks.filtered('executeType = $0', query.executeType) + } + + let res if (query.count) { res = tasks.slice(0, query.count) @@ -107,12 +105,12 @@ export class BookBackTaskListService extends BaseRealmService { let tasks = this.realm .objects('BookBackTaskList') .filtered( - 'status == $0 && executeType == $1', + '(status == $0 || status == $1) && executeType == $2', BookBackTaskStatus.WAIT, + BookBackTaskStatus.RECONNECT, executeType ? executeType : TaskExecuteType.AUTO ) .sorted('createTime', false) - if (count != null) { tasks = tasks.slice(0, count) as unknown as Realm.Results } @@ -169,9 +167,8 @@ export class BookBackTaskListService extends BaseRealmService { } // 开始往数据库中写数据 - let name = `${book.name}-${bookTask ? bookTask.name : 'default'}-${ - bookTaskDetail ? bookTaskDetail.name : 'default' - }-${taskType}` + let name = `${book.name}-${bookTask ? bookTask.name : 'default'}-${bookTaskDetail ? bookTaskDetail.name : 'default' + }-${taskType}` let bookBackTask = { id: uuidv4(), @@ -183,15 +180,14 @@ export class BookBackTaskListService extends BaseRealmService { executeType: executeType, status: BookBackTaskStatus.WAIT, createTime: new Date(), - updateTime: new Date() - } + updateTime: new Date(), + startTime: 0, + endTime: 0 + } as TaskModal.Task this.realm.write(() => { this.realm.create('BookBackTaskList', bookBackTask) }) - // 添加成功之后,调用开始执行任务的方法 - await global.taskManager.ExecuteAutoTask() - return successMessage( bookBackTask, '新增后台队列任务到数据库成功', @@ -210,7 +206,7 @@ export class BookBackTaskListService extends BaseRealmService { * (对于后台的队列任务只能修改状态)和错误信息 * @param bookBackTask 修改的数据 */ - UpdateTaskStatus(bookBackTask) { + UpdateTaskStatus(bookBackTask: Book.UpdateBookTaskListStatus): GeneralResponse.SuccessItem { try { // 判断数据是不是存在 if (isEmpty(bookBackTask.id) || isEmpty(bookBackTask.status)) { @@ -219,7 +215,7 @@ export class BookBackTaskListService extends BaseRealmService { // 开始修改 this.transaction(() => { // 获取指定ID的队列任务 - let _bookBackTask = this.realm.objectForPrimaryKey('BookBackTaskList', bookBackTask.id) + let _bookBackTask = this.realm.objectForPrimaryKey('BookBackTaskList', bookBackTask.id) as TaskModal.Task // 判断数据是不是存在 if (_bookBackTask == null) { throw new Error('修改后台队列任务失败,数据不存在') @@ -229,6 +225,16 @@ export class BookBackTaskListService extends BaseRealmService { if (bookBackTask.errorMessage) { _bookBackTask.errorMessage = bookBackTask.errorMessage } + // 根据状态修改结束和完成时间 + if (bookBackTask.status == BookBackTaskStatus.RUNNING) { + _bookBackTask.startTime = new Date().getTime() + } else if (bookBackTask.status == BookBackTaskStatus.DONE || bookBackTask.status == BookBackTaskStatus.FAIL || bookBackTask.status == BookBackTaskStatus.PAUSE) { + _bookBackTask.endTime = new Date().getTime() + } else if (bookBackTask.status == BookBackTaskStatus.RECONNECT) { + _bookBackTask.startTime = 0; + _bookBackTask.endTime = 0; + } + }) return successMessage( bookBackTask, diff --git a/src/define/db/service/Book/bookBasic.ts b/src/define/db/service/Book/bookBasic.ts index d6c8953..1e49b15 100644 --- a/src/define/db/service/Book/bookBasic.ts +++ b/src/define/db/service/Book/bookBasic.ts @@ -7,12 +7,13 @@ import path from 'path' import { BookTaskDetailModel, MJMessage, + ReversePrompt, SDConfig, Subtitle, WebuiConfig } from '../../model/Book/bookTaskDetail' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel' -import { TaskExecuteType } from '../../../enum/bookEnum' +import { BookTaskStatus, TaskExecuteType } from '../../../enum/bookEnum' import { version } from '../../../../../package.json' let dbPath = path.resolve(define.db_path, 'book.realm') @@ -77,6 +78,83 @@ const migration = (oldRealm: Realm, newRealm: Realm) => { newBookTask[i].subtitlePosition = null // 设置字幕位置的默认值 } } + if (oldRealm.schemaVersion < 9) { + const oldBookTask = oldRealm.objects('BookBackTaskList') + const newBookTask = newRealm.objects('BookBackTaskList') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].startTime = 0 + newBookTask[i].endTime = 0 + } + } + + if (oldRealm.schemaVersion < 10) { + const oldBookTask = oldRealm.objects('BookTaskDetail') + const newBookTask = newRealm.objects('BookTaskDetail') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].status = BookTaskStatus.WAIT + } + } + if (oldRealm.schemaVersion < 11) { + const oldBookTask = oldRealm.objects('BookTaskDetail') + const newBookTask = newRealm.objects('BookTaskDetail') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].reversePrompt = null + } + } + if (oldRealm.schemaVersion < 13) { + const oldBookTask = oldRealm.objects('ReversePrompt') + const newBookTask = newRealm.objects('ReversePrompt') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].promptCN = null; + newBookTask[i].isSelect = false; + } + } + if (oldRealm.schemaVersion < 15) { + const oldBookTask = oldRealm.objects('BookTaskDetail') + const newBookTask = newRealm.objects('BookTaskDetail') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].imageCategory = null; + } + } + if (oldRealm.schemaVersion < 16) { + const oldBookTask = oldRealm.objects('BookTask') + const newBookTask = newRealm.objects('BookTask') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].subImageFolder = null; + } + } + if (oldRealm.schemaVersion < 17) { + const oldBookTask = oldRealm.objects('BookTaskDetail') + const newBookTask = newRealm.objects('BookTaskDetail') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].imageLock = false; + } + } + if (oldRealm.schemaVersion < 19) { + const oldBookTask = oldRealm.objects('Book') + const newBookTask = newRealm.objects('Book') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].draftSrtStyle = null; + newBookTask[i].backgroundMusic = null; + newBookTask[i].friendlyReminder = null; + } + } + if (oldRealm.schemaVersion < 20) { + const oldBookTask = oldRealm.objects('BookTask') + const newBookTask = newRealm.objects('BookTask') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].draftSrtStyle = null; + newBookTask[i].backgroundMusic = null; + newBookTask[i].friendlyReminder = null; + } + } + if (oldRealm.schemaVersion < 21) { + const oldBookTask = oldRealm.objects('BookTaskDetail') + const newBookTask = newRealm.objects('BookTaskDetail') + for (let i = 0; i < oldBookTask.length; i++) { + newBookTask[i].subValue = '[]' + } + } } export class BaseRealmService extends BaseService { @@ -114,10 +192,11 @@ export class BaseRealmService extends BaseService { SDConfig, WebuiConfig, BookTaskModel, + ReversePrompt, BookTaskDetailModel ], path: this.dbpath, - schemaVersion: 8, + schemaVersion: 21, migration: migration } this.realm = await Realm.open(config) diff --git a/src/define/db/service/Book/bookService.ts b/src/define/db/service/Book/bookService.ts index cd8b6c3..f00db1b 100644 --- a/src/define/db/service/Book/bookService.ts +++ b/src/define/db/service/Book/bookService.ts @@ -3,13 +3,16 @@ import { BookModel } from '../../model/Book/book.js' import path from 'path' import { BaseService } from '../baseService.js' import { define } from '../../../define.js' -import { BookTaskStatus, BookType } from '../../../enum/bookEnum.js' -import { successMessage } from '../../../../main/generalTools.js' -import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file.js' +import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum.js' +import { successMessage } from '../../../../main/Public/generalTools' +import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file' const { v4: uuidv4 } = require('uuid') import { BookTaskService } from './bookTaskService' import { BaseRealmService } from './bookBasic.js' import { isEmpty } from 'lodash' +import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js' +import { version } from '../../../../../package.json' +import { Book } from '../../../../model/book.js' export class BookService extends BaseRealmService { static instance: BookService | null = null @@ -36,14 +39,14 @@ export class BookService extends BaseRealmService { * 获取小说信息,没有找到返回null * @param bookId */ - GetBookDataById(bookId) { + GetBookDataById(bookId): Book.SelectBook | null { try { if (isEmpty(bookId)) { throw new Error('获取小说信息失败,缺少小说ID') } let books = this.realm.objects('Book').filtered('id = $0', bookId) if (books.length <= 0) { - return successMessage(null, '通过ID获取小说数据成功', 'ReverseBook_GetBookDataById') + return null } else { // 对返回的数据进行处理 let resBooks = Array.from(books).map((book) => { @@ -63,8 +66,7 @@ export class BookService extends BaseRealmService { } return bookObj }) - - return successMessage(resBooks[0], '通过ID获取小说数据成功', 'ReverseBook_GetBookDataById') + return resBooks[0] } } catch (error) { throw error @@ -120,8 +122,10 @@ export class BookService extends BaseRealmService { : '', imageFolder: book.imageFolder ? path.resolve(define.project_path, book.imageFolder.replace(/\\/g, '/')) - : '' - } + : '', + imageStyle: book.imageStyle ? Array.from(book.imageStyle) : [], + customizeImageStyle: book.customizeImageStyle ? Array.from(book.imageStyle) : [], + } as Book.SelectBook return bookObj }) @@ -186,14 +190,31 @@ export class BookService extends BaseRealmService { await CopyFileOrFolder(book.oldVideoPath, oldVideoPath) } + let ffmpegOptions = new FfmpegOptions(); + let res = await ffmpegOptions.FfmpegCompressVideo(oldVideoPath, 800, "2000k") + // 创建对应的文件夹 await CheckFolderExistsOrCreate(bookFolderPath) await CheckFolderExistsOrCreate(imageFolder) await CheckFolderExistsOrCreate(bookTaskImageFolder) // 创建默认的任务文件夹 // 修改数据 book.oldVideoPath = path.relative(define.project_path, oldVideoPath) + + let imageCategory = BookImageCategory.MJ + if (book.type == BookType.SD_REVERSE) { + imageCategory = BookImageCategory.SD + } else if (book.type == BookType.MJ_REVERSE) { + imageCategory = BookImageCategory.MJ + } else if (book.type == BookType.ORIGINAL) { + imageCategory = BookImageCategory.MJ + } else { + throw new Error('未知的小说类型') + } + this.realm.write(() => { + book.version = version this.realm.create('Book', book) + // 添加一个任务 let bookTask = { id: uuidv4(), @@ -210,7 +231,9 @@ export class BookService extends BaseRealmService { errorMsg: null, isAuto: false, updateTime: new Date(), - createTime: new Date() + createTime: new Date(), + version: version, + imageCategory: imageCategory } // 添加任务 @@ -265,7 +288,7 @@ export class BookService extends BaseRealmService { // 检查小说ID对应的数据是不是存在 let bookRes = this.GetBookDataById(bookId) - if (bookRes.data == null) { + if (bookRes == null) { throw new Error('修改小说数据失败,小说ID对应的数据不存在') } @@ -279,11 +302,11 @@ export class BookService extends BaseRealmService { }) bookRes = this.GetBookDataById(bookId) - if (bookRes.data == null) { + if (bookRes == null) { throw new Error('获取修改后的小说数据失败,小说ID对应的数据不存在') } - return successMessage(bookRes.data, '修改小说数据成功', 'ReverseBook_UpdateBookData') + return successMessage(bookRes, '修改小说数据成功', 'ReverseBook_UpdateBookData') } catch (error) { throw error } diff --git a/src/define/db/service/Book/bookTaskDetailService.ts b/src/define/db/service/Book/bookTaskDetailService.ts index b0ec4b6..fe8b5bb 100644 --- a/src/define/db/service/Book/bookTaskDetailService.ts +++ b/src/define/db/service/Book/bookTaskDetailService.ts @@ -4,19 +4,21 @@ import { BaseService } from '../baseService.js' import { define } from '../../../define.js' import { BookTaskModel } from '../../model/Book/bookTask.js' import { BookTaskStatus } from '../../../enum/bookEnum.js' -import { successMessage } from '../../../../main/generalTools.js' +import { successMessage } from '../../../../main/Public/generalTools' import { BaseRealmService } from './bookBasic' -import { endsWith, isEmpty } from 'lodash' +import { cloneDeep, endsWith, isEmpty } from 'lodash' import { book } from '../../../../preload/book.js' import { DefaultObject } from 'realm/dist/public-types/schema.js' -import { JoinPath } from '../../../Tools/file.js' -import { BookTaskDetailModel } from '../../model/Book/bookTaskDetail.js' +import { JoinPath } from '../../../Tools/file' +import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js' const { v4: uuidv4 } = require('uuid') +import { Book } from "../../../../model/book" +import { GeneralResponse } from '../../../../model/generalResponse.js' let dbPath = path.resolve(define.db_path, 'book.realm') // 版本迁移 -const migration = (oldRealm: Realm, newRealm: Realm) => {} +const migration = (oldRealm: Realm, newRealm: Realm) => { } export class BookTaskDetailService extends BaseRealmService { static instance: BookTaskDetailService | null = null @@ -43,7 +45,7 @@ export class BookTaskDetailService extends BaseRealmService { * 更具条件查询执行的小说的分镜信息 * @param condition 查询的条件,id,name,bookId,bookTaskId */ - GetBookTaskData(condition) { + GetBookTaskData(condition: Book.QueryBookTaskDetailCondition) { try { if (condition == null) { throw new Error('查询小说分镜信息,查询条件不能为空') @@ -62,6 +64,7 @@ export class BookTaskDetailService extends BaseRealmService { tasksToDelete = tasksToDelete.filtered('name==$0', condition.name) } + let resData = Array.from(tasksToDelete).map((item) => { let resObj = { ...item, @@ -71,9 +74,17 @@ export class BookTaskDetailService extends BaseRealmService { outImagePath: JoinPath(define.project_path, item.outImagePath), subImagePath: (item.subImagePath as string[])?.map((subImage) => { return JoinPath(define.project_path, subImage) - }) + }), + characterTags: item.characterTags ? item.characterTags.map((tag) => tag) : null, + subValue: item.subValue, + reversePrompt: item.reversePrompt.map((reversePrompt) => { + return { + ...reversePrompt + } + }), + mjMessage: item.mjMessage ? item.mjMessage.toJSON() : null, } - return resObj + return cloneDeep(resObj) }) return successMessage( resData, @@ -89,7 +100,7 @@ export class BookTaskDetailService extends BaseRealmService { * 通过ID获取指定的小说任务分镜详细数据 * @param bookTaskDetailId */ - public GetBookTaskDetailDataById(bookTaskDetailId: string) { + public GetBookTaskDetailDataById(bookTaskDetailId: string): Book.SelectBookTaskDetail { try { if (bookTaskDetailId == null) { throw new Error('获取小说任务详细信息失败,缺少ID') @@ -97,17 +108,9 @@ export class BookTaskDetailService extends BaseRealmService { let bookTaskDetails = this.GetBookTaskData({ id: bookTaskDetailId }) if (bookTaskDetails.data.length <= 0) { - return successMessage( - null, - '未找到对应的小说任务详细信息', - 'BookTaskDetailService_GetBookTaskDetailDataById' - ) + return null; } else { - return successMessage( - bookTaskDetails.data[0], - '获取小说任务详细信息成功', - 'BookTaskDetailService_GetBookTaskDetailDataById' - ) + return bookTaskDetails.data[0] } } catch (error) { throw error @@ -142,6 +145,7 @@ export class BookTaskDetailService extends BaseRealmService { bookTaskDetail.name = name bookTaskDetail.id = uuidv4() + bookTaskDetail.imageLock = false bookTaskDetail.createTime = new Date() bookTaskDetail.updateTime = new Date() bookTaskDetail.adetailer = false // 先写死false @@ -165,7 +169,7 @@ export class BookTaskDetailService extends BaseRealmService { * @param bookTaskDetailId * @param updateData */ - UpdateBookTaskDetail(bookTaskDetailId: string, updateData) { + UpdateBookTaskDetail(bookTaskDetailId: string, updateData: Book.SelectBookTaskDetail) { try { this.transaction(() => { let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId) @@ -188,6 +192,53 @@ export class BookTaskDetailService extends BaseRealmService { } } + UpdateBookTaskDetailMjMessage(bookTaskDetailId: string, mjMessage: Book.MJMessage): void { + try { + this.transaction(() => { + let mjMessageRes = this.realm.objectForPrimaryKey('MJMessage', bookTaskDetailId) + let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId) + if (bookTaskDetail.mjMessage == null) { + // 新增 + mjMessage.id = bookTaskDetailId + bookTaskDetail.mjMessage = mjMessage + } else { + for (const key in mjMessage) { + mjMessageRes[key] = mjMessage[key] + } + } + }) + } catch (error) { + throw error + } + } + + /** + * 更新指定ID的反推提示词数据 + * @param bookTaskDetailId 分镜数据的ID + * @param reversePromptId 反推出来的提示词ID + * @param updateData 要更新的数据 + */ + UpdateBookTaskDetailReversePrompt(bookTaskDetailId: string, reversePromptId: string, reversePrompt: Book.ReversePrompt): GeneralResponse.SuccessItem { + try { + this.transaction(() => { + let bookTaskDetails = this.realm.objects("ReversePrompt"); + bookTaskDetails = bookTaskDetails.filtered("id = $0 && bookTaskDetailId = $1", reversePromptId, bookTaskDetailId); + + if (bookTaskDetails.length <= 0) { + throw new Error("未找到执行的翻译数据,无法写回") + } + let bookTaskDetail = bookTaskDetails[0]; + // 直接写入 + for (const key in reversePrompt) { + bookTaskDetail[key] = reversePrompt[key] + } + }) + return successMessage(null, `${reversePromptId} 更新完成`, "BookTaskDetailService_UpdateBookTaskDetailReversePrompt") + } catch (error) { + throw error + } + } + /** * 删除满足条件的对象吗,必传小说ID和小说任务ID * @param condition bookId,bookTaskId,name,id @@ -223,4 +274,18 @@ export class BookTaskDetailService extends BaseRealmService { throw error } } + + /** + * 删除指定ID的小说任务详细数据中的所有的反推提示词数据 + * @param bookTaskDetailId 小说分镜的ID + */ + DeleteBookTaskDetailReversePromptById(bookTaskDetailId: string): void { + let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId); + if (bookTaskDetail == null) { + throw new Error('删除小说任务详细信息的反推提示词失败,未找到对应的分镜信息') + } + this.transaction(() => { + this.realm.delete(bookTaskDetail.reversePrompt) + }) + } } diff --git a/src/define/db/service/Book/bookTaskService.ts b/src/define/db/service/Book/bookTaskService.ts index 8e76f0c..badfdf1 100644 --- a/src/define/db/service/Book/bookTaskService.ts +++ b/src/define/db/service/Book/bookTaskService.ts @@ -1,24 +1,29 @@ import Realm from 'realm' import path from 'path' -import { BaseService } from '../baseService.js' import { define } from '../../../define.js' import { BookTaskModel } from '../../model/Book/bookTask.js' -import { BookBackTaskStatus, BookTaskStatus } from '../../../enum/bookEnum.js' -import { successMessage } from '../../../../main/generalTools.js' +import { BookBackTaskStatus, BookImageCategory, BookTaskStatus } from '../../../enum/bookEnum.js' +import { successMessage } from '../../../../main/Public/generalTools' import { BaseRealmService } from './bookBasic' -import { isEmpty } from 'lodash' -import { JoinPath } from '../../../Tools/file.js' +import { JoinPath } from '../../../Tools/file' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js' const { v4: uuidv4 } = require('uuid') +import { Book } from '../../../../model/book' +import { TagDefine } from '../../../tagDefine.js' +import { ImageStyleDefine } from "../../../../define/iamgeStyleDefine" +import { cloneDeep } from 'lodash' +import { GeneralResponse } from '../../../../model/generalResponse' let dbPath = path.resolve(define.db_path, 'book.realm') export class BookTaskService extends BaseRealmService { static instance: BookTaskService | null = null realm: Realm + tagDefine: TagDefine private constructor() { super() + this.tagDefine = new TagDefine() } /** @@ -38,7 +43,7 @@ export class BookTaskService extends BaseRealmService { * 查询满足条件的小说子任务信息 * @param bookTaskCondition 查询条件 id,bookId,name,no,page, pageSize */ - GetBookTaskData(bookTaskCondition) { + GetBookTaskData(bookTaskCondition: Book.QueryBookTaskCondition): GeneralResponse.ErrorItem | GeneralResponse.SuccessItem { try { // 获取所有的小说数据,并进行时间降序排序 let bookTasks = this.realm.objects('BookTask') @@ -62,7 +67,7 @@ export class BookTaskService extends BaseRealmService { } let bookTask_length = bookTasks.length - bookTasks = bookTasks.sorted('updateTime', true) + // bookTasks = bookTasks.sorted('updateTime', true) // 判断是不是有page和pageSize,有的话对查询返回的信息做分页 if (bookTaskCondition.page && bookTaskCondition.pageSize) { bookTasks = bookTasks.slice( @@ -73,22 +78,24 @@ export class BookTaskService extends BaseRealmService { // 做一下数据转换 // 将realm对象数组转换为普通对象数组 + // 将realm对象数组转换为普通对象数组,并处理异步操作 let res_bookTasks = Array.from(bookTasks).map((bookTask) => { - // 这里可以直接操作普通对象 - let bookObj = { + // 直接操作普通对象 + return { ...bookTask, - styleList: bookTask.styleList ? Array.from(bookTask.styleList) : [], + imageStyle: bookTask.imageStyle ? Array.from(bookTask.imageStyle) : [], + customizeImageStyle: bookTask.customizeImageStyle ? Array.from(bookTask.customizeImageStyle) : [], generateVideoPath: JoinPath(define.project_path, bookTask.generateVideoPath), srtPath: JoinPath(define.project_path, bookTask.srtPath), audioPath: JoinPath(define.project_path, bookTask.audioPath), - imageFolder: JoinPath(define.project_path, bookTask.imageFolder) - } - return bookObj + imageFolder: JoinPath(define.project_path, bookTask.imageFolder), + imageCategory: bookTask.imageCategory ? bookTask.imageCategory : BookImageCategory.MJ, // 默认使用MJ出图 + } as Book.SelectBookTask; }) return successMessage( { - bookTasks: res_bookTasks, + bookTasks: JSON.parse(JSON.stringify(res_bookTasks)), total: bookTask_length }, '查询小说任务成功', @@ -103,7 +110,7 @@ export class BookTaskService extends BaseRealmService { * 通过ID获取小说批次任务的数据 * @param bookTaskId */ - GetBookTaskDataById(bookTaskId: string) { + GetBookTaskDataById(bookTaskId: string): Book.SelectBookTask { try { if (bookTaskId == null) { throw new Error('小说任务ID不能为空') @@ -111,13 +118,9 @@ export class BookTaskService extends BaseRealmService { let bookTasks = this.GetBookTaskData({ id: bookTaskId }) if (bookTasks.data.bookTasks.length <= 0) { - return successMessage(null, '未找到对应的小说任务', 'BookTaskService_GetBookTaskDataById') + throw new Error('未找到对应的小说任务') } else { - return successMessage( - bookTasks.data.bookTasks[0], - '查询小说任务成功', - 'BookTaskService_GetBookTaskDataById' - ) + return bookTasks.data.bookTasks[0] } } catch (error) { throw error @@ -183,6 +186,28 @@ export class BookTaskService extends BaseRealmService { } } + /** + * 修改小说批次任务数据 + * @param bookTaskId 小说批次任务ID + * @param data 要修改的数据 + */ + UpdetedBookTaskData(bookTaskId: string, data: Book.SelectBookTask): void { + try { + this.transaction(() => { + let updateData = this.realm.objectForPrimaryKey('BookTask', bookTaskId) + if (updateData == null) { + throw new Error('未找到对应的小说任务详细信息') + } + // 开始修改 + for (let key in data) { + updateData[key] = data[key] + } + }) + } catch (error) { + throw error + } + } + // 添加一条数据 AddOrModifyBookTask(bookTask) { try { @@ -220,4 +245,73 @@ export class BookTaskService extends BaseRealmService { throw error } } + + private resetBookTask(bookTaskId: string) { + let modifyBookTask = this.realm.objectForPrimaryKey('BookTask', bookTaskId) + modifyBookTask.status = BookTaskStatus.WAIT + modifyBookTask.errorMsg = ""; + modifyBookTask.updateTime = new Date() + modifyBookTask.imageStyle = [] + modifyBookTask.autoAnalyzeCharacter = undefined + modifyBookTask.customizeImageStyle = [] + modifyBookTask.videoConfig = undefined + modifyBookTask.prefixPrompt = undefined + modifyBookTask.suffixPrompt = undefined + modifyBookTask.subImageFolder = [] + + let bookTaskDetails = this.realm.objects('BookTaskDetail').filtered('bookTaskId = $0', bookTaskId) + // 开始删除数据 + bookTaskDetails.forEach(bookTaskDetail => { + // 删除MJMessage + if (bookTaskDetail.mjMessage) { + this.realm.delete(bookTaskDetail.mjMessage) + } + + if (bookTaskDetail.reversePrompt) { + (bookTaskDetail.reversePrompt as any[]).forEach(item => { + this.realm.delete(item) + }) + } + if (bookTaskDetail.sdConifg) { + this.realm.delete(bookTaskDetail.sdConifg) + } + this.realm.delete(bookTaskDetail) + }) + } + + /** + * 删除对应的小说批次数据 + * @param bookTaskId 小说批次ID + */ + ResetBookTask(bookTaskId: string): void { + try { + // 开始重置数据,先重置小说批次数据,在重置其他 + this.transaction(() => { + this.resetBookTask(bookTaskId) + }) + } catch (error) { + throw error + } + } + + /** + * 删除对应的小说批次数据 + * @param bookTaskId 要删除的批次的ID + */ + DeleteBookTask(bookTaskId: string): void { + try { + this.transaction(() => { + // 先调用清除数据的方法 + this.resetBookTask(bookTaskId) + // 删除批次数据 + let bookTask = this.realm.objectForPrimaryKey('BookTask', bookTaskId) + if (bookTask == null) { + throw new Error('未找到对应的小说任务,无法执行删除操作') + } + this.realm.delete(bookTask) + }) + } catch (error) { + throw error + } + } } diff --git a/src/define/db/service/SoftWare/loggerService.ts b/src/define/db/service/SoftWare/loggerService.ts index c5a2795..f0ca160 100644 --- a/src/define/db/service/SoftWare/loggerService.ts +++ b/src/define/db/service/SoftWare/loggerService.ts @@ -2,9 +2,9 @@ import Realm, { UpdateMode } from 'realm' import path from 'path' import { BaseService } from '../baseService.js' import { define } from '../../../define.js' -import { SoftwareModel } from '../../model/SoftWare/software.js' +import { SoftwareModel } from '../../model/SoftWare/software' import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js' -import { errorMessage, successMessage } from '../../../../main/generalTools.js' +import { errorMessage, successMessage } from '../../../../main/Public/generalTools' import { BaseSoftWareService } from './softwareBasic.js' import { isEmpty } from 'lodash' const { v4: uuidv4 } = require('uuid') diff --git a/src/define/db/service/SoftWare/mjSettingService.ts b/src/define/db/service/SoftWare/mjSettingService.ts index b19828c..37564d9 100644 --- a/src/define/db/service/SoftWare/mjSettingService.ts +++ b/src/define/db/service/SoftWare/mjSettingService.ts @@ -2,13 +2,14 @@ import Realm, { UpdateMode } from 'realm' import path from 'path' import { BaseService } from '../baseService' import { define } from '../../../define.js' -import { SoftwareModel } from '../../model/SoftWare/software.js' +import { SoftwareModel } from '../../model/SoftWare/software' import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js' -import { errorMessage, successMessage } from '../../../../main/generalTools.js' +import { errorMessage, successMessage } from '../../../../main/Public/generalTools' import { BaseSoftWareService } from './softwareBasic.js' import { isEmpty, isNumber } from 'lodash' const { v4: uuidv4 } = require('uuid') import { version } from '../../../../../package.json' +import { GeneralResponse } from '../../../../model/generalResponse' export class MJSettingService extends BaseSoftWareService { static instance: MJSettingService | null = null @@ -561,7 +562,7 @@ export class MJSettingService extends BaseSoftWareService { // 判断API设置的数据是不是存在 let apiSetting = mjSetting.apiSetting ? mjSetting.apiSetting : null if (apiSetting != null) { - let apiSettingRes: { code: number; data: any; message: any } + let apiSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem if (isEmpty(apiSetting.id)) { // 新增 apiSettingRes = this.AddAPIMjSetting(apiSetting) @@ -577,7 +578,7 @@ export class MJSettingService extends BaseSoftWareService { // 判断浏览器模式的数据是不是存在 let browserSetting = mjSetting.browserSetting ? mjSetting.browserSetting : null if (browserSetting != null) { - let browserSettingRes: { code: number; data: any; message: any } + let browserSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem if (isEmpty(browserSetting.id)) { // 新增 browserSettingRes = this.AddBrowserMJSetting(browserSetting) @@ -591,7 +592,7 @@ export class MJSettingService extends BaseSoftWareService { } // 添加MJ的基础配置信息 - let mjSettingRes: { code: number; data: any; message: any } + let mjSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem if (isEmpty(mjSetting.id)) { // 新增 mjSettingRes = this.AddMJSetting(mjSetting) diff --git a/src/define/db/service/SoftWare/softwareBasic.ts b/src/define/db/service/SoftWare/softwareBasic.ts index 96a445f..3e80d51 100644 --- a/src/define/db/service/SoftWare/softwareBasic.ts +++ b/src/define/db/service/SoftWare/softwareBasic.ts @@ -131,6 +131,22 @@ const migration = (oldRealm: Realm, newRealm: Realm) => { } }) } + if (oldRealm.schemaVersion < 18) { + newRealm.write(() => { + const newSoftwares = newRealm.objects('Software') + for (let software of newSoftwares) { + software.watermarkSetting = null // 水印的默认设置 + } + }) + } + if (oldRealm.schemaVersion < 19) { + newRealm.write(() => { + const newSoftwares = newRealm.objects('Software') + for (let software of newSoftwares) { + software.translationSetting = null // 翻译的默认设置 + } + }) + } } export class BaseSoftWareService extends BaseService { @@ -169,7 +185,7 @@ export class BaseSoftWareService extends BaseService { MjSettingModel ], path: dbPath, - schemaVersion: 17, // 当前版本号 + schemaVersion: 19, // 当前版本号 migration: migration } // 判断当前全局是不是又当前这个 diff --git a/src/define/db/service/SoftWare/softwareService.ts b/src/define/db/service/SoftWare/softwareService.ts index e2d9895..bf1d19a 100644 --- a/src/define/db/service/SoftWare/softwareService.ts +++ b/src/define/db/service/SoftWare/softwareService.ts @@ -1,10 +1,5 @@ import Realm, { UpdateMode } from 'realm' -import path from 'path' -import { BaseService } from '../baseService.js' -import { define } from '../../../define.js' -import { SoftwareModel } from '../../model/SoftWare/software.js' -import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js' -import { successMessage } from '../../../../main/generalTools.js' +import { successMessage } from '../../../../main/Public/generalTools' import { BaseSoftWareService } from './softwareBasic.js' const { v4: uuidv4 } = require('uuid') @@ -63,12 +58,26 @@ export class SoftwareService extends BaseSoftWareService { */ GetSoftwareData() { try { - let software = this.realm.objects('Software') + let softwares = this.realm.objects('Software') + + let res = Array.from(softwares).map((software) => { + // 这里可以直接操作普通对象 + let bookObj = { + id: software.id, + theme: software.theme, + reverse_show_book_striped: software.reverse_show_book_striped, + reverse_data_table_size: software.reverse_data_table_size, + reverse_display_show: software.reverse_display_show, + } + return bookObj + }) + return successMessage( - software.toJSON(), + res, '获取软件配置信息成功', 'SoftwareService_GetSoftwareData' ) + } catch (error) { global.logger.error( 'SoftwareService_GetSoftwareData', @@ -82,17 +91,15 @@ export class SoftwareService extends BaseSoftWareService { * 获取当前软件指定属性的数据 * @param property 属性名称 */ - GetSoftWarePropertyData(property: string) { + GetSoftWarePropertyData(property: string): string { try { let software = this.realm.objects('Software') if (software.length <= 0) { throw new Error('数据库中没有软件配置信息') } - let softwareData = software.toJSON()[0] - let res = softwareData[property] - - return successMessage(res, '获取软件配置信息成功', 'SoftwareService_GetSoftWarePropertyData') + let res = softwareData[property] as string + return res } catch (error) { global.logger.error( 'SoftwareService_GetSoftWarePropertyData', diff --git a/src/define/define.js b/src/define/define.js index 2941105..b383dad 100644 --- a/src/define/define.js +++ b/src/define/define.js @@ -15,6 +15,8 @@ if (!app.isPackaged) { scripts_path: path.join(__dirname, '../../resources/scripts'), db_path: path.join(__dirname, '../../resources/scripts/db'), project_path: path.join(__dirname, '../../project'), + tts_path: path.join(__dirname, '../../tts'), + logger_path: path.join(__dirname, '../../resources/logger'), package_path: path.join(__dirname, '../../resources/package'), image_path: path.join(__dirname, '../../resources/image'), @@ -82,6 +84,7 @@ if (!app.isPackaged) { scripts_path: path.join(__dirname, '../../../resources/scripts'), db_path: path.join(__dirname, '../../../resources/scripts/db'), project_path: path.join(__dirname, '../../../project'), + tts_path: path.join(__dirname, '../../../tts'), logger_path: path.join(__dirname, '../../../resources/logger'), package_path: path.join(__dirname, '../../../resources/package'), discordScript: path.join(__dirname, '../../../resources/scripts/discordScript.js'), @@ -141,5 +144,7 @@ if (!app.isPackaged) { define['remotemj_api'] = 'https://api.laitool.net/' define['serverUrl'] = 'http://lapi.laitool.cn' +define['hkServerUrl'] = 'https://api.laitool.cc/' +define['bakServerUrl'] = 'https://bakapi.laitool.cc/' define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0' -export { define } \ No newline at end of file +export { define } diff --git a/src/define/define_string.js b/src/define/define_string.ts similarity index 82% rename from src/define/define_string.js rename to src/define/define_string.ts index 5ce4881..b1c8a63 100644 --- a/src/define/define_string.js +++ b/src/define/define_string.ts @@ -1,4 +1,5 @@ export const DEFINE_STRING = { + SHOW_GLOBAL_MAIN_NOTIFICATION: 'SHOW_GLOBAL_MAIN_NOTIFICATION', OPEN_DEV_TOOLS_PASSWORD: 'OPEN_DEV_TOOLS_PASSWORD', OPEN_DEV_TOOLS: 'OPEN_DEV_TOOLS', GET_FILE_BASE64: 'GET_FILE_BASE64', @@ -21,7 +22,6 @@ export const DEFINE_STRING = { SAVE_KEY_FRAME_SETTING: 'SAVE_KEY_FRAME_SETTING', MODIFY_SAMPLE_SETTING: 'MODIFY_SAMPLE_SETTING', GET_SETTING_Dafault_DATA: 'GET_SETTING_Dafault_DATA', - GET_DRAFT_FILE_LIST: 'GET_DRAFT_FILE_LIST', GET_FRAME: 'GET_FRAME', PYTHON_ERROR: 'PYTHON_ERROR', PYTHON_CLOSE: 'PYTHON_CLOSE', @@ -144,9 +144,16 @@ export const DEFINE_STRING = { NORMAL_PERMISSION: 'NORMAL_PERMISSION', AUTO_SAVE_IMAGE_PERMISSION: 'AUTO_SAVE_IMAGE_PERMISSION' }, + TRANSLATE: { + TRANSLATE_NOW_RETURN: 'TRANSLATE_NOW_RETURN', + GET_TRANSLATE_SETTING: 'GET_TRANSLATE_SETTING', + RESET_TRANSLATE_SETTING: 'RESET_TRANSLATE_SETTING', + SAVE_TRANSLATE_SETTING: 'SAVE_TRANSLATE_SETTING' + }, SD: { LOAD_SD_SERVICE_DATA: 'LOAD_SD_SERVICE_DATA', - TXT2IMG: 'TXT2IMG' + TXT2IMG: 'TXT2IMG', + SD_MERGE_PROMPT: "SD_MERGE_PROMPT" }, MJ: { SAVE_WORD_SRT: 'SAVE_WORD_SRT', @@ -167,7 +174,10 @@ export const DEFINE_STRING = { GET_MJ_IMAGE_SCALE: 'GET_MJ_IMAGE_SCALE', GET_MJ_IMAGE_ROBOT_MODEL: 'GET_MJ_IMAGE_ROBOT_MODEL', MACTH_USER_RETURN: 'MACTH_USER_RETURN', - AUTO_MATCH_USER: 'AUTO_MATCH_USER' + AUTO_MATCH_USER: 'AUTO_MATCH_USER', + MJ_MERGE_PROMPT: "MJ_MERGE_PROMPT", + ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK: "ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK", + MJ_IMAGE: "MJ_IMAGE" }, DISCORD: { OPERATE_REFRASH_DISCORD_URL: 'OPERATE_REFRASH_DISCORD_URL', @@ -195,6 +205,8 @@ export const DEFINE_STRING = { BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT' }, BOOK: { + MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回 + GET_BOOK_TYPE: 'GET_BOOK_TYPE', ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK', GET_BOOK_DATA: 'GET_BOOK_DATA', @@ -204,7 +216,39 @@ export const DEFINE_STRING = { SAVE_BOOK_SUBTITLE_POSITION: 'SAVE_BOOK_SUBTITLE_POSITION', OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT: 'OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT', GET_CURRENT_FRAME_TEXT: 'GET_CURRENT_FRAME_TEXT', - GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT' + GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT', + GET_BOOK_TASK_DETAIL: 'GET_BOOK_TASK_DETAIL', + REVERSE_PROMPT_TO_GPT_PROMPT: 'REVERSE_PROMPT_TO_GPT_PROMPT', + SINGLE_REVERSE_TO_GPT_PROMPT: 'SINGLE_REVERSE_TO_GPT_PROMPT', + SAVE_IMAGE_STYLE: 'SAVE_IMAGE_STYLE', + IMAGE_LOCK_OPERATION: "IMAGE_LOCK_OPERATION", + DOWNLOAD_IMAGE_AND_SPLIT: "DOWNLOAD_IMAGE_AND_SPLIT", + ONE_TO_FOUR_BOOK_TASK: "ONE_TO_FOUR_BOOK_TASK", + RESET_BOOK_TASK: "RESET_BOOK_TASK", + DELETE_BOOK_TASK: "DELETE_BOOK_TASK", + GENERATE_IMAGE_ALL: "GENERATE_IMAGE_ALL", + CHECK_IMAGE_FILE_SIZE: "CHECK_IMAGE_FILE_SIZE", + HD_IMAGE: "HD_IMAGE", + USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK", + ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT", + + COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD', + + GET_FRAME: 'GET_FRAME', + + FRAMING: 'FRAMING', + + GET_COPYWRITING: 'GET_COPYWRITING', + GET_COPYWRITING_RETURN: 'GET_COPYWRITING_RETURN', + + REMOVE_WATERMARK: 'REMOVE_WATERMARK', + REMOVE_WATERMARK_RETURN: 'REMOVE_WATERMARK_RETURN', + + SPLI_TAUDIO: 'SPLI_TAUDIO', + SPLI_TAUDIO_RETURN: 'SPLI_TAUDIO_RETURN', + + ADD_REVERSE_PROMPT: 'ADD_REVERSE_PROMPT', + REMOVE_REVERSE_DATA: 'REMOVE_REVERSE_DATA' }, SYSTEM: { OPEN_FILE: 'OPEN_FILE', @@ -225,7 +269,9 @@ export const DEFINE_STRING = { GET_REMOTE_MJ_SETTINGS: 'GET_REMOTE_MJ_SETTINGS', ADD_REMOTE_MJ_SETTING: 'ADD_REMOTE_MJ_SETTING', UPDATE_REMOTE_MJ_SETTING: 'UPDATE_REMOTE_MJ_SETTING', - DELETE_REMOTE_MJ_SETTING: 'DELETE_REMOTE_MJ_SETTING' + DELETE_REMOTE_MJ_SETTING: 'DELETE_REMOTE_MJ_SETTING', + GET_WATER_MARK_SETTING: 'GET_WATER_MARK_SETTING', + SAVE_WATER_MARK_SETTING: 'SAVE_WATER_MARK_SETTING' }, PROMPT: { GET_SORT_OPTIONS: 'GET_SORT_OPTIONS', @@ -235,11 +281,16 @@ export const DEFINE_STRING = { }, TTS: { GET_TTS_CONFIG: 'GET_TTS_CONFIG', + GENERATE_AUDIO: 'GENERATE_AUDIO', SAVE_TTS_CONFIG: 'SAVE_TTS_CONFIG' }, WRITE: { GET_WRITE_CONFIG: 'GET_WRITE_CONFIG', SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG', ACTION_START: 'ACTION_START' + }, + DB: { + UPDATE_BOOK_TASK_DATA: "UPDATE_BOOK_TASK_DATA", + UPDATE_BOOK_TASK_DETAIL_DATA: "UPDATE_BOOK_TASK_DETAIL_DATA" } } diff --git a/src/define/enum/bookEnum.ts b/src/define/enum/bookEnum.ts index f7ba7a6..6b1faca 100644 --- a/src/define/enum/bookEnum.ts +++ b/src/define/enum/bookEnum.ts @@ -7,6 +7,16 @@ export enum BookType { MJ_REVERSE = 'mj_reverse' } +// 出图方式 +export enum BookImageCategory { + // MJ + MJ = 'mj', + // SD + SD = 'sd', + // D3 + D3 = 'd3' +} + export enum MJCategroy { @@ -39,10 +49,16 @@ export enum BookBackTaskType { RECOGNIZE = 'recognize', // 抽帧 FRAME = 'frame', - // 反推 - REVERSE = 'reverse', - // 生成图片 - IMAGE = 'image', + // MJ反推 + MJ_REVERSE = BookType.MJ_REVERSE, + // SD反推 + SD_REVERSE = BookType.SD_REVERSE, + // MJ生成图片 + MJ_IMAGE = 'mj_image', + // SD 生成图片 + SD_IMAGE = 'sd_image', + // D3 生成图片 + D3_IMAGE = 'd3_image', // 高清 HD = 'hd', // 合成视频 @@ -63,7 +79,9 @@ export enum BookBackTaskStatus { // 完成 DONE = 'done', // 失败 - FAIL = 'fail' + FAIL = 'fail', + // 重连 + RECONNECT = 'reconnect' } export enum TaskExecuteType { @@ -74,6 +92,16 @@ export enum TaskExecuteType { OPERATE = 'operate' } +// 弹窗类型 +export enum DialogType { + // 单独弹窗 + DIALOG = 'dialog', + // 消息提示 + MESSAGE = 'message', + // 右上角通知 + NOTIFICATION = 'notification' +} + /** * 小说任务状态 */ @@ -162,3 +190,40 @@ export enum BookTaskStatus { // 合成视频失败 COMPOSING_FAIL = 'composing_fail' } + +export enum TagDefineType { + // 默认风格 + DEFAULT_STYLE = "default_style", + // 角色标签 + CHARACTER_MAIN = "character_main", + + // 角色小标签 + CHARACTER_SUB = "min", + + // 风格主标签 + STYLE_MAIN = "style_main", + + // 场景主标签 + SCENE_MAIN = "scene_main", +} + +export enum MergeType { + BOOKTASK = 'bookTask', // 整个小说批次分镜合并 + BOOKTASKDETAIL = 'bookTaskDetail' // 单个分镜合并 +} + +export enum OperateBookType { + BOOK = 'book', // 这个小说的所有批次 + BOOKTASK = 'bookTask', // 整个小说批次分镜合并 + BOOKTASKDETAIL = 'bookTaskDetail', // 单个分镜合并 + UNDERBOOKTASK = 'underBookTask' // 执行小说批次任务的指定ID以及后面的所有的东西 +} + +export enum CopyImageType { + // 所有,包括原图 + ALL = 'all', + // 出原图外其他,一个个对应 + ONE = 'one', + // 不包含图 + NONE = 'none' +} diff --git a/src/main/Task/mjReverse.js b/src/define/enum/image.ts similarity index 100% rename from src/main/Task/mjReverse.js rename to src/define/enum/image.ts diff --git a/src/define/enum/mjEnum.ts b/src/define/enum/mjEnum.ts index a7a8228..1f08845 100644 --- a/src/define/enum/mjEnum.ts +++ b/src/define/enum/mjEnum.ts @@ -19,3 +19,31 @@ export enum MJRobotType { // niji NIJI = 'niji' } + +export enum MJSpeed { + // 快速 + FAST = 'fast', + + // 休闲 + RELAX = 'relaxed' +} + +export enum MJRespoonseType { + // 创建 + CREATED = "created", + // 更新 + UPDATED = "updated", + // 完成 + FINISHED = "finished", + // 删除 + DELETE = "delete" +} + +export enum MJAction { + // 出图 + IMAGINE = 'IMAGINE', + + // 反推 + DESCRIBE = 'DESCRIBE' + +} diff --git a/src/define/enum/softwareEnum.ts b/src/define/enum/softwareEnum.ts index 631ac1b..fd8e34c 100644 --- a/src/define/enum/softwareEnum.ts +++ b/src/define/enum/softwareEnum.ts @@ -45,6 +45,31 @@ export enum SoftColor { // 棕黄色 BROWN_YELLOW = '#e18a3b', + // 橘色 + ORANGE = '#ee7959', + + // 朱颜酡 + ZHUYANTUO = '#f29a76', + // 错误红色 ERROR_RED = '#c8161d' + } + +export enum ResponseMessageType { + GET_TEXT = 'getText', // 获取文案 + REMOVE_WATERMARK = "REMOVE_WATERMARK",// 删除水印 + MJ_REVERSE = 'MJ_REVERSE',// MJ反推,返回反推结果 + PROMPT_TRANSLATE = 'PROMPT_TRANSLATE',// 提示词翻译 + MJ_IMAGE = 'MJ_IMAGE',// MJ 生成图片 + HD_IMAGE = 'HD_IMAGE',// HD 生成图片 +} + +export enum LaiAPIType { + // 主要的 + MAIN = "main", + // 香港代理 + HK_PROXY = "hk-proxy", + // 备用站点 + BAK_MAIN = 'bak-main' +} \ No newline at end of file diff --git a/src/define/enum/task.ts b/src/define/enum/task.ts new file mode 100644 index 0000000..6c055f3 --- /dev/null +++ b/src/define/enum/task.ts @@ -0,0 +1,6 @@ +export enum TaskQueueType { + // 内存添加 + CACHE_ADD = 'cache_add', + // 数据库添加 + DB_ADD = 'db_add' +} \ No newline at end of file diff --git a/src/define/enum/translate.ts b/src/define/enum/translate.ts new file mode 100644 index 0000000..e3f2a85 --- /dev/null +++ b/src/define/enum/translate.ts @@ -0,0 +1,23 @@ +// 翻译类型(主要用于后端逻辑处理) +export enum TranslateType { + // 反推提示词翻译 + REVERSE_PROMPT_TRANSLATE = 'reverse_prompt_translate', + + // GPT提示词翻译 + GPT_PROMPT_TRANSLATE = 'gpt_prompt_translate', +} + +// 翻译API类型 +export enum TranslateAPIType { + // 百度翻译 + BAIDU = 'baidu', + + // 腾讯翻译 + TENCENT = 'tencent', + + // 火山翻译 + VOLCENGINE = 'volcengine', + + // 阿里翻译 + ALI = 'ali', +} diff --git a/src/define/enum/waterMarkAndSubtitle.ts b/src/define/enum/waterMarkAndSubtitle.ts index 51c7622..d92b5aa 100644 --- a/src/define/enum/waterMarkAndSubtitle.ts +++ b/src/define/enum/waterMarkAndSubtitle.ts @@ -8,6 +8,23 @@ export enum SubtitleSavePositionType { // 分镜视频 STORYBOARD_VIDEO = 'storyboard_video', + // 设置,只是框选 + SETTING = 'setting', + // 其他类型 OTHER = 'other' } + +// 图片去除水印方法,返回数据的格式 +export enum WaterMarkResponseDateType { + // 返回的数据类型 + ArrayBuffer = "arrayBuffer", + // 直接将文件写道本地 + File = "file" +} + + +export enum RemoveWatermarkType { + LOCAL_LAMA = 'local_lama', + IOPAINT = 'iopaint' +} \ No newline at end of file diff --git a/src/define/iamgeStyleDefine.js b/src/define/iamgeStyleDefine.js deleted file mode 100644 index 7cf0548..0000000 --- a/src/define/iamgeStyleDefine.js +++ /dev/null @@ -1,2265 +0,0 @@ -import { SdSettingDefine } from "./setting/sdSettingDefine"; - -const ImageStyleDefine = { - image_style: [{ - "sheet": 1, - "name": "常用画风", - "subStyle": [{ - "chinese_style": "壁纸", - "english_style": "Wallpaper", - "id": "238f8db1-3b9f-48b8-86de-b47c70dbdc25", - "image": "image8.jpeg" - }, { - "chinese_style": "黑白漫画", - "english_style": "Black and white comics", - "id": "bb314303-4f94-4a9a-b9cc-2f0eca64e288", - "image": "image2.jpeg" - }, { - "chinese_style": "水墨风格", - "english_style": "Ink and wash style", - "id": "078dc21d-669c-4a60-8036-2705dfb1d6b6", - "image": "image3.jpeg" - }, { - "chinese_style": "超写实风格", - "english_style": "Super realistic style", - "id": "81dd155f-f69d-4ed0-9fdd-6f639e5e6d06", - "image": "image4.jpeg" - }, { - "chinese_style": "电影截图", - "english_style": "Screenshots of movies", - "id": "3923dfb8-3394-4e6f-a9f4-55caf24b3991", - "image": "image5.jpeg" - }, { - "chinese_style": "宫崎骏漫画风格", - "english_style": "Miyazaki Hayao manga style", - "id": "b1869409-8079-4934-b6b1-b26cf4606e65", - "image": "image6.jpeg" - }, { - "chinese_style": "可爱风格", - "english_style": "cute style", - "id": "79feb583-f4e3-4d35-b7f2-31021693bb90", - "image": "image7.jpeg" - }, { - "chinese_style": "简笔画风格", - "english_style": "Simple stroke style", - "id": "0378c4d7-676f-4693-925a-112d2f8fd76b", - "image": "image1.jpeg" - }, { - "chinese_style": "3D卡通动画", - "english_style": "3D cartoon animation", - "id": "a5732ec7-3b49-40d8-8bbc-ac213ec77df7", - "image": "image11.jpeg" - }, { - "chinese_style": "90年代日本动漫", - "english_style": "90s Japanese anime", - "id": "a9f5eb3b-d4ae-4ba4-aa43-0198f36b6147", - "image": "image9.jpeg" - }, { - "chinese_style": "水彩画风格", - "english_style": "Watercolor painting style", - "id": "9d1e4c03-0406-44cc-88f9-21c117a3c88b", - "image": "image10.jpeg" - }, { - "chinese_style": "钢笔画风格", - "english_style": "Pen drawing style", - "id": "392067d1-cd6e-4baa-b2d3-f319b892f78c", - "image": "image12.jpeg" - }, { - "chinese_style": "恐怖漫画风格", - "english_style": "Horror comic style", - "id": "0e8a0c64-04e8-4370-ad2a-06148afa0662", - "image": "image13.jpeg" - }], - "id": "07246bd9-b811-4e00-b69b-f86c48f2660b" - }, { - "sheet": 2, - "name": "时代风格词", - "subStyle": [{ - "chinese_style": "中世纪时代", - "english_style": "middle ages", - "id": "c0f55669-a90e-4024-9c5c-fe4614eabe29", - "image": "image16.jpeg" - }, { - "chinese_style": "冷战时代", - "english_style": "cold war era", - "id": "e2c3b92b-ab4d-44fd-a477-7e308a4e9dfb", - "image": "image19.jpeg" - }, { - "chinese_style": "古典时代", - "english_style": "classical antiquity", - "id": "f5e943bd-0e34-4f5e-baac-1c2e0304c0cf", - "image": "image20.jpeg" - }, { - "chinese_style": "柴油朋克时代", - "english_style": "diesel punk", - "id": "a2ffb049-2c5b-4f3f-9cef-d27e7d8d8ab0", - "image": "image21.jpeg" - }, { - "chinese_style": "哥特式时代", - "english_style": "gothic period", - "id": "db5e8036-7f8c-4047-8c3d-f64f010fc4dd", - "image": "image14.jpeg" - }, { - "chinese_style": "中国战国时代", - "english_style": "warring states period", - "id": "ba0ae70c-37de-40e3-9dc8-08a7e03de123", - "image": "image17.jpeg" - }, { - "chinese_style": "日本明治时代", - "english_style": "meiji period", - "id": "a465ddc1-9782-430b-b09c-7a79effc1c29", - "image": "image15.jpeg" - }, { - "chinese_style": "现代", - "english_style": "modern", - "id": "a12c8315-28c0-42bd-a3ec-a856d48f5161", - "image": "image18.jpeg" - }, { - "chinese_style": "文艺复兴时代", - "english_style": "renaissance", - "id": "e1329854-1df1-4664-9ca2-27c8f905f2d3", - "image": "image25.jpeg" - }, { - "chinese_style": "中国宋代", - "english_style": "song dynasty", - "id": "df196ffe-012a-4fe5-8db3-d89046d5eb39", - "image": "image26.jpeg" - }, { - "chinese_style": "古埃及时代", - "english_style": "ancient egypt", - "id": "27c5c5df-4b9f-487c-8884-4c50035f4bc6", - "image": "image23.jpeg" - }, { - "chinese_style": "蒸汽朋克时代", - "english_style": "steampunk", - "id": "62018708-10c6-4a1f-8c35-9c162278686f", - "image": "image28.jpeg" - }, { - "chinese_style": "中国明代", - "english_style": "ming dynasty", - "id": "20c9f4cf-99ec-4550-a965-768190d91498", - "image": "image29.jpeg" - }, { - "chinese_style": "青铜时代", - "english_style": "bronze age", - "id": "6ef8bd4f-c843-435c-9d31-ca2f36add104", - "image": "image22.jpeg" - }, { - "chinese_style": "中国唐代", - "english_style": "tang dynasty", - "id": "950518dc-e87b-4be8-8487-8b732b52e175", - "image": "image27.jpeg" - }, { - "chinese_style": "赛博朋克时代", - "english_style": "cyberpunk", - "id": "8e012231-4e38-4d6d-b18c-4e03f1ef45f1", - "image": "image24.jpeg" - }, { - "chinese_style": "工业革命时代", - "english_style": "industrial revolution", - "id": "8a46b74f-91b5-40d5-bec7-afba88b5abb1", - "image": "image30.jpeg" - }, { - "chinese_style": "日本江户时代", - "english_style": "edo period", - "id": "5ab22ed5-b284-4f79-be4f-7c6aef329151", - "image": "image31.jpeg" - }], - "id": "ade4cf78-7bef-43c8-9d13-c2ea5c3f9f38" - }, { - "sheet": 3, - "name": "画家画风风格词", - "subStyle": [{ - "chinese_style": "潘天寿(古风画风-成熟)", - "english_style": "Pan Tianshou", - "id": "6d54b67f-08e3-40ac-816f-d4081ad7d9e2", - "image": "image35.jpeg" - }, { - "chinese_style": "朱屺瞻(古风画风-艳丽)", - "english_style": "Zhu Qizhan", - "id": "121b3d63-b0b2-4dd4-aaa4-e2c3e8d89d76", - "image": "image36.jpeg" - }, { - "chinese_style": "齐白石(古风画风-简约)", - "english_style": "Qi Baishi", - "id": "bc76a884-afac-450e-88cc-54f65c504a7c", - "image": "image37.jpeg" - }, { - "chinese_style": "张大千(古风画风-可爱)", - "english_style": "Zhang Daqian", - "id": "a85caede-767a-4b8b-b14a-981656415637", - "image": "image33.jpeg" - }, { - "chinese_style": "亨利·马蒂斯(都市画风-时尚)", - "english_style": "Henri Matisse", - "id": "9b7e83e6-dc18-41b8-8887-176794a615c3", - "image": "image38.jpeg" - }, { - "chinese_style": "乔治亚·欧姬芙(都市画风-欧美)", - "english_style": "Georgia O'Keeffe", - "id": "ca757d22-c5fc-4974-9768-16323957fe85", - "image": "image32.jpeg" - }, { - "chinese_style": "格伦·基恩(迪斯尼-美女与野兽风)", - "english_style": "Glen Keane", - "id": "1a5655c9-6800-42ab-aa88-7ab2c77c8c0c", - "image": "image39.jpeg" - }, { - "chinese_style": "根迪·塔尔塔科夫斯基(欧美动画风)", - "english_style": "Genndy Tartakovsky", - "id": "60357799-3cba-4809-b50c-db804783599d", - "image": "image34.jpeg" - }, { - "chinese_style": "克雷格·汤普森(都市画风-细腻)", - "english_style": "Craig Thompson", - "id": "a6806093-372a-44f0-8967-d764b1e90709", - "image": "image40.jpeg" - }, { - "chinese_style": "亨利·马蒂斯(都市画风-流行)", - "english_style": "Henri Matisse", - "id": "fac9dd65-8a92-43ab-b026-63231ebf6de2", - "image": "image44.jpeg" - }, { - "chinese_style": "伊藤润二(日式恐怖画风)", - "english_style": "Junji Ito", - "id": "0115613d-ee65-4d32-b9a5-eeb57cdc708d", - "image": "image45.jpeg" - }, { - "chinese_style": "朴泰俊(小鲜肉都市风)", - "english_style": "Park Tae", - "id": "80d2fb78-2a69-4314-989d-57734dce6789", - "image": "image41.jpeg" - }, { - "chinese_style": "朴智旻(都市恋爱风)", - "english_style": "Park Ji", - "id": "6b4c5150-82e0-4259-9617-59434291e4b5", - "image": "image42.jpeg" - }, { - "chinese_style": "金瑞亨(韩风都市恋爱)", - "english_style": "Kim Hyeong", - "id": "f1fee049-109c-4b85-83bf-ed3ed5554f7f", - "image": "image43.jpeg" - }, { - "chinese_style": "查克·琼斯(欧美动画风-猫和老鼠)", - "english_style": "Chuck Jones", - "id": "21c0e23f-d98b-41f0-b351-cb1b47f462ee", - "image": "image46.jpeg" - }, { - "chinese_style": "高畑勋(日式动漫风)", - "english_style": "Isao Takahata", - "id": "55868e27-cc28-4830-b6c9-46096416b03d", - "image": "image47.jpeg" - }, { - "chinese_style": "淺野恭司(细腻日漫)", - "english_style": "Kyoji Asano", - "id": "126ec922-c45d-4a83-b9d7-48f04513cad0", - "image": "image48.jpeg" - }, { - "chinese_style": "卡洛斯·达托利(都市少男风)", - "english_style": "Carlos Dattoli", - "id": "a7aee025-fc08-4e66-82ba-42f38b7d3405", - "image": "image49.jpeg" - }, { - "chinese_style": "库夫希诺夫·伊利亚(都市少女风)", - "english_style": "Kuvshinov Ilya", - "id": "a9679671-a1b9-472b-be78-4fd91adc9e02", - "image": "image50.jpeg" - }, { - "chinese_style": "加里·拉尔森、杰拉德·乌克盖斯特(美式漫画)", - "english_style": "Gary Larson, Gerard Houckgeest", - "id": "c1eb9ca2-4d89-4636-b1ff-7f83d7eba781", - "image": "image54.jpeg" - }, { - "chinese_style": "阮佳(国风插画)", - "english_style": "Ruan Jia", - "id": "32afc969-622a-4792-a270-02cf6952d805", - "image": "image51.jpeg" - }, { - "chinese_style": "保罗·罗沃西、塞西莉·布朗(欧美风偏质感)", - "english_style": "Paolo roversi, Cecily brown", - "id": "f9860c1f-8552-4514-8be7-617527d51f0b", - "image": "image52.jpeg" - }, { - "chinese_style": "山田尚子(日式少女风)", - "english_style": "Naoko Yamada", - "id": "ad6ed3d9-a153-487e-9cd5-8447f99ce43b", - "image": "image53.jpeg" - }, { - "chinese_style": "木村贵宏(日式动漫风)", - "english_style": "Takahiro Kimura", - "id": "199934ed-5d7e-4af7-979b-0b2dd4862199", - "image": "image55.jpeg" - }, { - "chinese_style": "安德烈亚斯·-罗查(欧美风偏细节色彩)", - "english_style": "Andreas Rocha", - "id": "f6358e01-7276-4f26-96c8-56a619ae3126", - "image": "image70.jpeg" - }, { - "chinese_style": "凉香(日式古风)", - "english_style": "Ryohka", - "id": "824b9390-9787-44de-ab43-da5d647101ad", - "image": "image71.jpeg" - }, { - "chinese_style": "丽贝卡·盖伊(西方魔幻)", - "english_style": "Rebecca Guay", - "id": "5d2234ce-d0d8-4330-93d5-21a81d6486d5", - "image": "image64.jpeg" - }, { - "chinese_style": "克雷格·穆林斯(西方末日科幻)", - "english_style": "Craig Mullins", - "id": "a66f32dd-2b73-4d6c-8d83-cc1a37dae7ea", - "image": "image65.jpeg" - }, { - "chinese_style": "小林修(日式画风)", - "english_style": "Osamu Kobayashi", - "id": "a9e9c6ab-02d1-406b-b730-9d4ece86d29c", - "image": "image66.jpeg" - }, { - "chinese_style": "池上辽一(日式画风)", - "english_style": "Ryoichi Ikegami", - "id": "aea0beec-32ea-42c7-9891-09616a01a424", - "image": "image69.jpeg" - }, { - "chinese_style": "岸本齐史(火影忍着风)", - "english_style": "Masashi Kishimoto", - "id": "2ef699da-781d-4f05-9239-6d027d34c42c", - "image": "image67.jpeg" - }, { - "chinese_style": "沃尔特·迪斯尼(迪斯尼风)", - "english_style": "Walt Disney", - "id": "6befee18-2232-4a3e-b1a5-d206d5e29138", - "image": "image68.jpeg" - }, { - "chinese_style": "久保带人(死神画风)", - "english_style": "Tite Kubo", - "id": "c26be074-bc44-41bc-b36c-97845c8fb544", - "image": "image62.jpeg" - }, { - "chinese_style": "谏山创(进击的巨人画风)", - "english_style": "Hajime Isayama", - "id": "5ceeedb9-4977-49f4-9122-220165784683", - "image": "image63.jpeg" - }, { - "chinese_style": "新海诚", - "english_style": "Makoto Shinkai", - "id": "9cc4611f-4ebe-449f-ae9c-3502a37a857e", - "image": "image60.jpeg" - }, { - "chinese_style": "武内直子(美少女战士画风)", - "english_style": "Naoko Takeuchi", - "id": "9176dac9-deaa-4e19-8d0f-e586437d251f", - "image": "image56.jpeg" - }, { - "chinese_style": "濑户目鹤(日漫风)", - "english_style": "Koyori", - "id": "321322f7-0f12-47d5-9d53-1945b8ef4516", - "image": "image57.jpeg" - }, { - "chinese_style": "细田守(日本动漫风)", - "english_style": "Mamoru Hosoda", - "id": "9e2db099-5996-4e8a-8e86-37f8fc81fa1a", - "image": "image58.jpeg" - }, { - "chinese_style": "约翰·拉塞特(欧美动画风)", - "english_style": "John Lasseter", - "id": "d7f5278d-d242-4834-ab82-2adfc11231e8", - "image": "image61.jpeg" - }, { - "chinese_style": "蒂姆·伯顿(魔发奇缘风-暗黑魔幻)", - "english_style": "Tim Burton", - "id": "c104bba3-6611-4178-93d8-2f988fb83fc7", - "image": "image59.jpeg" - }, { - "chinese_style": "鸟山明(龙珠画风)", - "english_style": "Akira Toriyama", - "id": "e51d4ad6-816d-4534-89e0-3fdaf7a3916c", - "image": "image72.jpeg" - }, { - "chinese_style": "高桥留美子(犬夜叉画风)", - "english_style": "Rumiko Takahashi", - "id": "589a6e3d-4707-4eba-8650-e479fe56aa49", - "image": "image73.jpeg" - }, { - "chinese_style": "庵野秀明(EVA画风)", - "english_style": "Hideaki Anno", - "id": "1ba9073e-b530-4520-893e-4f7eb2c8cba6", - "image": "image76.jpeg" - }, { - "chinese_style": "宫崎骏(日式动漫风)", - "english_style": "Hayao Miyazaki", - "id": "92b59244-5b26-47e2-a542-050b28beb631", - "image": "image75.jpeg" - }, { - "chinese_style": "米林宏昌(日式古风)", - "english_style": "hiromasa yonebayashi", - "id": "5f3bfcdd-d6f9-40c8-8ebb-b423ee768d59", - "image": "image74.jpeg" - }, { - "chinese_style": "男鹿和雄(日漫古风)", - "english_style": "kazuo oga", - "id": "b389d682-0ac2-434c-849a-9ceca0468397", - "image": "image77.jpeg" - }], - "id": "661f3673-b448-49c5-95eb-92416c669f21" - }, { - "sheet": 4, - "name": "中华古风", - "subStyle": [{ - "chinese_style": "张光宇风格", - "english_style": "Zhang Guangyu style", - "id": "f67e1f1f-3824-4190-aa7b-22a4ceefc803", - "image": "image85.jpeg" - }, { - "chinese_style": "丰子恺风格", - "english_style": "Feng Zikai style", - "id": "0c6e2462-82a2-492d-8f00-e0571099230a", - "image": "image83.jpeg" - }, { - "chinese_style": "蔡志忠风格", - "english_style": "Cai Zhong style", - "id": "ddc1adda-5bc5-4779-9f70-942f5c8bc739", - "image": "image82.jpeg" - }, { - "chinese_style": "马荣成风格", - "english_style": "Ma Rongcheng style", - "id": "c6ecceb3-4000-4846-ad54-160f7f92ac68", - "image": "image78.jpeg" - }, { - "chinese_style": "陈某风格", - "english_style": "Chen Someone style", - "id": "85cd0650-a62a-4123-a7ac-ae7ecfe549c7", - "image": "image84.jpeg" - }, { - "chinese_style": "夏达风格", - "english_style": "Xia Da style", - "id": "3317e89f-c828-4a60-bab7-9935dd2f841c", - "image": "image79.jpeg" - }, { - "chinese_style": "猫君风格", - "english_style": "Cat Jun style", - "id": "5db7dfa4-306b-4c0f-a8ca-314037c32839", - "image": "image80.jpeg" - }, { - "chinese_style": "Left Hand Han style", - "english_style": "左手韩风格", - "id": "eed2d205-3818-4b3d-9d2a-98a5140cd97e", - "image": "image81.jpeg" - }, { - "chinese_style": "阿梗风格", - "english_style": "A Geng style", - "id": "ad75eb08-6e12-4707-affb-7292471b82c7", - "image": "image89.jpeg" - }, { - "chinese_style": "本杰明风格", - "english_style": "Benjamin style", - "id": "00638bad-7919-4bbe-844d-89bf78787df8", - "image": "image88.jpeg" - }, { - "chinese_style": "寂地风格", - "english_style": "Ji Di style", - "id": "f041d7d9-485f-4ef8-a3ee-a63a480d9980", - "image": "image86.jpeg" - }, { - "chinese_style": "鹿菏风格", - "english_style": "Lu He style", - "id": "33aa10c2-1d70-46b4-b847-bed5eb9f5f41", - "image": "image87.jpeg" - }], - "id": "fc8d672d-8482-445b-8652-a7d8910c621c" - }, { - "sheet": 5, - "name": "中国都市漫画作者风格", - "subStyle": [{ - "chinese_style": "姚非拉风格", - "english_style": "Yao Feila style", - "id": "ad83c246-6c22-42ac-9049-e6e1e3f82022", - "image": "image93.jpeg" - }, { - "chinese_style": "慕容引刀风格", - "english_style": "Murong Yindao style", - "id": "845ec4a6-a1bf-42b3-b2f5-503bae91e24b", - "image": "image90.jpeg" - }, { - "chinese_style": "使徒子风格", - "english_style": "Apostle style", - "id": "59c26e38-33f1-44ad-a471-d747babd0cee", - "image": "image94.jpeg" - }, { - "chinese_style": "幽·灵风格", - "english_style": "Youling style", - "id": "0f6e1954-2b2b-418c-b36b-bb833a49d131", - "image": "image91.jpeg" - }, { - "chinese_style": "猫树风格", - "english_style": "Cat Tree style", - "id": "afed5230-a76a-4709-be26-30040f2a9418", - "image": "image95.jpeg" - }, { - "chinese_style": "青鸟动漫风格", - "english_style": "Blue Bird Animation style", - "id": "06900981-ac49-4b03-b327-512f4b36bcbf", - "image": "image92.jpeg" - }, { - "chinese_style": "阿尤风格", - "english_style": "A You style", - "id": "82887dc9-eb51-4168-91e5-73467c1d9bf5", - "image": "image96.jpeg" - }, { - "chinese_style": "张小溪风格", - "english_style": "Zhang Xiaoxi style", - "id": "1d2901e9-c79c-42b7-843e-0a32be80f420", - "image": "image97.jpeg" - }, { - "chinese_style": "阿衰风格", - "english_style": "A Shuai style", - "id": "83e99b4d-0702-4cf8-a93e-af2a07f10858", - "image": "image99.jpeg" - }, { - "chinese_style": "丁墨风格", - "english_style": "Ding Mo style", - "id": "193a8cb3-d9a7-4f42-9c2f-7c538d45e795", - "image": "image100.jpeg" - }, { - "chinese_style": "老夫子风格", - "english_style": "Old Master style", - "id": "0eab7213-51fe-42cb-9330-c95b0d991d5b", - "image": "image98.jpeg" - }], - "id": "ac972ea7-e2db-48be-9b66-1d914b059b77" - }, { - "sheet": 6, - "name": "都市漫画", - "subStyle": [{ - "chinese_style": "现代都市生活", - "english_style": "Modern city life", - "id": "10d307a4-50ad-4919-afaf-754a9570a7ad", - "image": "image105.jpeg" - }, { - "chinese_style": "职场奋斗", - "english_style": "Workplace struggles", - "id": "79b593b1-fe8c-4a94-a6d0-f2d160c3d005", - "image": "image101.jpeg" - }, { - "chinese_style": "青春校园", - "english_style": "Youthful school life", - "id": "c1d691e9-1211-492f-bebb-71e3106ff35b", - "image": "image102.jpeg" - }, { - "chinese_style": "浪漫爱情", - "english_style": "Romantic love story", - "id": "1b711f31-2e3b-423f-b27f-dba57b760e33", - "image": "image103.jpeg" - }, { - "chinese_style": "悬疑侦探", - "english_style": "Mystery and detective", - "id": "4352c2b1-5cf0-4654-995d-2658f668f6fb", - "image": "image106.jpeg" - }, { - "chinese_style": "超能力者", - "english_style": "Individuals with superpowers", - "id": "acb3a28e-2957-442e-ad47-6550d5af1a94", - "image": "image104.jpeg" - }, { - "chinese_style": "青春成长", - "english_style": "Coming of age", - "id": "31779554-fbea-447f-a9f7-57cdf5aefffd", - "image": "image107.jpeg" - }, { - "chinese_style": "日常生活", - "english_style": "Slice of life", - "id": "4688a2b4-d1e7-4bff-9d9e-e7c740bd0269", - "image": "image108.jpeg" - }, { - "chinese_style": "都市异闻", - "english_style": "Urban legends and folklore", - "id": "0bbec8ab-4e11-430c-a857-1b1693d1ca31", - "image": "image112.jpeg" - }, { - "chinese_style": "时尚潮流", - "english_style": "Fashion and trends", - "id": "59610aa3-1cb0-498e-a701-9ed0456085a6", - "image": "image110.jpeg" - }, { - "chinese_style": "社交网络", - "english_style": "Social media and relationships", - "id": "74549157-8829-45b4-a608-b9752dfcb84f", - "image": "image111.jpeg" - }, { - "chinese_style": "都市奇幻", - "english_style": "Urban fantasy", - "id": "235b4431-f310-4dd0-a6dc-f42cbb7ddf29", - "image": "image109.jpeg" - }], - "id": "c9d2846d-c8c0-40b0-ad9e-ee882aa6efcf" - }, { - "sheet": 7, - "name": "悬疑故事画风", - "subStyle": [{ - "chinese_style": "悬疑恐怖故事", - "english_style": "Suspenseful horror stories", - "id": "97dc5f98-c241-43c8-a918-ab9788b5e982", - "image": "image119.jpeg" - }, { - "chinese_style": "古堡探险", - "english_style": "Castle exploration", - "id": "e8370048-ee70-4664-8b91-a50fee0f93f5", - "image": "image117.jpeg" - }, { - "chinese_style": "失踪之谜", - "english_style": "Mystery of disappearance", - "id": "6145e00c-f5ce-4f4d-97ff-91939c26f92e", - "image": "image115.jpeg" - }, { - "chinese_style": "诡异事件调查", - "english_style": "Investigation of eerie events", - "id": "cd6b74f2-6599-4bd9-9282-84fabfa0e558", - "image": "image113.jpeg" - }, { - "chinese_style": "禁忌解密", - "english_style": "Forbidden decryption", - "id": "b4673555-6dd6-4a7c-95ac-f728ab587fd7", - "image": "image118.jpeg" - }, { - "chinese_style": "幽灵传说", - "english_style": "Ghost legends", - "id": "5d4ace6c-476a-4ae8-a621-ece0dd59c831", - "image": "image120.jpeg" - }, { - "chinese_style": "恐怖短片", - "english_style": "Horror short films", - "id": "9191e13c-2add-41e9-a68f-cee5828c6b55", - "image": "image116.jpeg" - }, { - "chinese_style": "神秘案件破解", - "english_style": "Solving mysterious cases", - "id": "b132999e-3bf5-4828-a963-8f83892f8c40", - "image": "image114.jpeg" - }, { - "chinese_style": "阴谋追踪", - "english_style": "Conspiracy tracking", - "id": "79b40a23-05f4-4915-aa6a-be916e471699", - "image": "image121.jpeg" - }], - "id": "7e83b352-ae2f-4617-91a4-ad10ebc7dd13" - }, { - "sheet": 8, - "name": "3D动画", - "subStyle": [{ - "chinese_style": "皮克斯电影", - "english_style": "Pixar Movie Style", - "id": "2e65894e-6d8b-475e-a93b-f93929860eb3", - "image": "image129.jpeg" - }, { - "chinese_style": "梦工厂电影风格", - "english_style": "Dream Factory Movie Style", - "id": "c4df479b-49fc-49b9-a24c-f901056774e2", - "image": "image130.jpeg" - }, { - "chinese_style": "迪士尼", - "english_style": "Disney", - "id": "9de96904-a2e6-4506-9a5d-be5bd79c9df4", - "image": "image126.jpeg" - }, { - "chinese_style": "哥伦比亚电影公司", - "english_style": "Columbia Pictures", - "id": "0836e43b-0cc6-49f6-894e-e5ed4205c192", - "image": "image131.jpeg" - }, { - "chinese_style": "索尼动画", - "english_style": "Sony Pictures Animation", - "id": "1773b5a6-6109-4bbe-8213-185559689d81", - "image": "image127.jpeg" - }, { - "chinese_style": "蓝天工作室", - "english_style": "Blue Sky Studios", - "id": "ed7dfdd1-6775-4ecb-9110-eb0ac26cd122", - "image": "image132.jpeg" - }, { - "chinese_style": "卡通网络动画", - "english_style": "Cartoon Network Studios", - "id": "37dd259a-1f42-403f-ad36-66e497bdf6e4", - "image": "image128.jpeg" - }, { - "chinese_style": "漫威动画", - "english_style": "Marvel Animation", - "id": "f4317d2a-e827-4da1-a158-26e33812ea89", - "image": "image133.jpeg" - }, { - "chinese_style": "华纳兄弟动画", - "english_style": "Warner Bros. Animation", - "id": "7f6f85c5-18a7-451a-ae5e-f911331a192a", - "image": "image141.jpeg" - }, { - "chinese_style": "环球影业动画", - "english_style": "Universal Pictures Animation", - "id": "90944358-bc96-4bc9-ad59-373365015725", - "image": "image134.jpeg" - }, { - "chinese_style": "20世纪福克斯动画", - "english_style": "20th Century Fox Animation", - "id": "e3c44ec7-cda7-415e-83fc-fde7b1cf948d", - "image": "image140.jpeg" - }, { - "chinese_style": "派拉蒙动画", - "english_style": "Paramount Animation", - "id": "b4353dab-6bd4-419b-9ccd-5f0c9352690e", - "image": "image135.jpeg" - }, { - "chinese_style": "米高梅动画", - "english_style": "Metro-Goldwyn-Mayer Animation", - "id": "8efe38ae-0b66-4441-b394-c63fc144551e", - "image": "image137.jpeg" - }, { - "chinese_style": "乐高影业", - "english_style": "LEGO Animation", - "id": "6caaddde-97fe-405a-8655-61e12d0434c1", - "image": "image138.jpeg" - }, { - "chinese_style": "迪士尼电视动画", - "english_style": "Disney Television Animation", - "id": "f0eb3c60-94ec-44de-8099-7c6018c454a7", - "image": "image139.jpeg" - }, { - "chinese_style": "尼克儿童动画", - "english_style": "Nickelodeon Animation Studio", - "id": "6174cf18-7114-43f0-b139-1db5fd2ad9bb", - "image": "image136.jpeg" - }, { - "chinese_style": "奈飞动画", - "english_style": "Netflix Animation", - "id": "0df4834b-8867-42b3-b680-80c169ba904e", - "image": "image123.jpeg" - }, { - "chinese_style": "华纳动画组", - "english_style": "Warner Animation Group", - "id": "80406a8b-9a23-4d35-b25e-a8984eb5581c", - "image": "image124.jpeg" - }, { - "chinese_style": "索尼娱乐动画", - "english_style": "Sony Entertainment Animation", - "id": "95f8555f-8615-4484-953b-27f14890b219", - "image": "image122.jpeg" - }, { - "chinese_style": "蓝宝儿动画", - "english_style": "Lionsgate Animation", - "id": "5c4a4871-753a-4825-bc45-93d66e97037c", - "image": "image125.jpeg" - }], - "id": "507a1001-efab-4f23-ba0a-e1b6fc65732a" - }, { - "sheet": 9, - "name": "概念艺术", - "subStyle": [{ - "chinese_style": "幻想题材", - "english_style": "Fantasy theme", - "id": "a0586d4b-5add-436f-aa74-74559f66a000", - "image": "image146.jpeg" - }, { - "chinese_style": "科幻题材", - "english_style": "Science fiction theme", - "id": "939ef719-0785-45ec-8a26-a1e56a740790", - "image": "image142.jpeg" - }, { - "chinese_style": "赛博朋克", - "english_style": "Cyberpunk style", - "id": "f488da0b-c5a4-465e-815e-8f8034ae4394", - "image": "image147.jpeg" - }, { - "chinese_style": "蒸汽朋克", - "english_style": "Steampunk style", - "id": "61d188e6-7f5f-469e-bd30-50f810b1285a", - "image": "image143.jpeg" - }, { - "chinese_style": "超自然现象", - "english_style": "Supernatural phenomena", - "id": "381ec4b4-2bf7-4043-beda-aa0fd65520cd", - "image": "image148.jpeg" - }, { - "chinese_style": "神话传说", - "english_style": "Mythology and legends", - "id": "d554974b-d1c5-47d3-8acc-e71911325360", - "image": "image149.jpeg" - }, { - "chinese_style": "魔法与咒语", - "english_style": "Magic and spells", - "id": "0b505a81-36fd-454c-8ccc-285be256896b", - "image": "image144.jpeg" - }, { - "chinese_style": "幻想生物", - "english_style": "Fantasy creatures", - "id": "a0bcefce-c94d-47b3-bbd5-4d7b6b9cdd01", - "image": "image145.jpeg" - }, { - "chinese_style": "历史重现", - "english_style": "Historical reenactment", - "id": "169249c5-dbf3-4ce8-be7f-47237c8535b2", - "image": "image150.jpeg" - }, { - "chinese_style": "未来都市", - "english_style": "Futuristic cityscape", - "id": "2d0eec27-4bab-42f1-9d8b-7909df82c709", - "image": "image152.jpeg" - }, { - "chinese_style": "末日废土", - "english_style": "Post-apocalyptic wasteland", - "id": "ffc43253-b1a4-4f4a-a5a6-d8b9e62c3208", - "image": "image153.jpeg" - }, { - "chinese_style": "异星世界", - "english_style": "Alien world", - "id": "8b7b821a-7f91-437a-8af9-4841f8a24517", - "image": "image151.jpeg" - }], - "id": "6459007e-cb61-4933-b42e-e2ea19ce1f03" - }, { - "sheet": 10, - "name": "新日本动漫风", - "subStyle": [{ - "chinese_style": "《刀剑神域》", - "english_style": "Sword Art Online style, fantasy and adventure, Kawahara Reki", - "id": "d2048c30-56ad-44be-bdf7-6e04a690c310", - "image": "image163.jpeg" - }, { - "chinese_style": "《东京食尸鬼》", - "english_style": "Sui Ishida style", - "id": "e22b50f6-f0c4-4cf7-97b2-084990537cf0", - "image": "image160.jpeg" - }, { - "chinese_style": "《命运之夜》", - "english_style": "Type-Moon style", - "id": "b97c9b77-c423-41b0-8c80-e19c4fe504ba", - "image": "image161.jpeg" - }, { - "chinese_style": "《狼与香辛料》", - "english_style": "Isuna Hasekura style", - "id": "670a7131-915f-4959-a020-3dd1e3ae1f69", - "image": "image158.jpeg" - }, { - "chinese_style": "《鬼灭之刃》", - "english_style": "Koyoharu Gotouge style", - "id": "907db0cb-24ed-457e-a0c0-c4067184b608", - "image": "image159.jpeg" - }, { - "chinese_style": "《名侦探柯南》", - "english_style": "Gosho Aoyama style", - "id": "3cff985d-2b9c-4d52-b373-adc4a415d487", - "image": "image162.jpeg" - }, { - "chinese_style": "《七原罪》", - "english_style": "Nakaba Suzuki style", - "id": "8d8ca7fc-7d78-4240-a300-274dbbf299cb", - "image": "image164.jpeg" - }, { - "chinese_style": "《妖怪手表》", - "english_style": "Level-5 style", - "id": "0569520e-cc54-4fb8-8f7c-ac7102c557e7", - "image": "image157.jpeg" - }, { - "chinese_style": "《灼眼的夏娜》", - "english_style": "Yashichiro Takahashi style", - "id": "899f56d0-67be-4546-a373-04e6648299ef", - "image": "image165.jpeg" - }, { - "chinese_style": "《命运石之门》", - "english_style": "Chiyomaru Shikura and Nitroplus style", - "id": "b0c30ddf-a77b-48f8-bdfe-d29f94ac7902", - "image": "image166.jpeg" - }, { - "chinese_style": "《化物语》", - "english_style": "Nisio Isin and VOFAN style", - "id": "1d50bf91-22b7-4efa-a5b4-1cce44cb82be", - "image": "image167.jpeg" - }, { - "chinese_style": "《未来日记》", - "english_style": "Sakae Esunostyle style", - "id": "b5aadac7-df6e-44fd-b953-9e83f8bef4b8", - "image": "image169.jpeg" - }, { - "chinese_style": "《Angel Beats!》", - "english_style": "Jun Maeda and Na-Ga style", - "id": "85202d85-4cfc-4439-83d5-4504836905f2", - "image": "image168.jpeg" - }, { - "chinese_style": "《Fate/Zero》", - "english_style": "Gen Urobuchi and Takashi Takeuchi style", - "id": "d5e1f5ef-bb4a-4e40-a51b-bd2045d609b7", - "image": "image172.jpeg" - }, { - "chinese_style": "《Re:从零开始的异世界生活》", - "english_style": "Tappei Nagatsuki and Shinichirou Otsuka style", - "id": "f821ed49-c12f-4f87-b81f-58424610ccd1", - "image": "image170.jpeg" - }, { - "chinese_style": "《OVERLORD》", - "english_style": "Kugane Maruyama and so-bin style", - "id": "1977e489-6693-49a5-ade6-72769a4fde00", - "image": "image171.jpeg" - }, { - "chinese_style": "《约会大作战》", - "english_style": "Koushi Tachibana and Tsunako style", - "id": "e7ca9f27-6937-4100-96ca-706d7efa49ec", - "image": "image155.jpeg" - }, { - "chinese_style": "《Re:CREATORS》", - "english_style": "Rei Hiroe style", - "id": "6acc66f5-79f9-40b3-abb0-6435da43c73b", - "image": "image156.jpeg" - }, { - "chinese_style": "《关于我转生变成史莱姆这档事》", - "english_style": "Rimuru Tempest style", - "id": "1ac9cc4a-f1bb-40ae-a76a-6fd593c7bb04", - "image": "image154.jpeg" - }], - "id": "14c7f9d5-32b8-409b-ad4c-f7217fa3defd" - }, { - "sheet": 11, - "name": "少年日本动漫风", - "subStyle": [{ - "chinese_style": "《Another》", - "english_style": "Yukito Ayatsuji and Hiro Kiyohara style", - "id": "d2329e3e-e8e5-4a95-82d7-09fda8a3f3cb", - "image": "image176.jpeg" - }, { - "chinese_style": "《尸体派对》", - "english_style": "Team GrisGris style", - "id": "436251be-eb1a-4825-9565-a2f4d7201632", - "image": "image177.jpeg" - }, { - "chinese_style": "《妖精森林的小不点》", - "english_style": "Takuto Kashiki style", - "id": "85196b76-ffd9-443d-9827-d6b1b5dd9752", - "image": "image178.jpeg" - }, { - "chinese_style": "《怪谈新耳袋》", - "english_style": "Kyouko Hikawa style", - "id": "447a9b2e-4f3c-47d5-acd6-a9e893d35614", - "image": "image174.jpeg" - }, { - "chinese_style": "《地缚少年花子君》", - "english_style": "Kouji Seo style", - "id": "0021c2d2-8f70-4536-9d88-07f287cb419f", - "image": "image179.jpeg" - }, { - "chinese_style": "《彼岸岛》", - "english_style": "Koji Matsumoto style", - "id": "9d884c85-c7c1-447c-96a6-a05416b5d1e7", - "image": "image180.jpeg" - }, { - "chinese_style": "《龙珠》", - "english_style": "Akira Toriyama style", - "id": "c8c3784a-aad1-4fbd-aa0a-ca18af2efa31", - "image": "image175.jpeg" - }, { - "chinese_style": "《海贼王》", - "english_style": "Eiichiro Oda style", - "id": "1ad0e6e6-4ab0-4669-a932-7ecaeef1090d", - "image": "image173.jpeg" - }, { - "chinese_style": "《火影忍者》", - "english_style": "Masashi Kishimoto style", - "id": "5a198a66-9e62-48c4-aaff-8be6586249be", - "image": "image198.jpeg" - }, { - "chinese_style": "《死神》", - "english_style": "Tite Kubo style", - "id": "cc514c8c-2194-403e-ae57-9733a455f3f2", - "image": "image192.jpeg" - }, { - "chinese_style": "《进击的巨人》", - "english_style": "Hajime Isayama style", - "id": "8c356863-e16d-4a4c-a154-1e4d3836aed9", - "image": "image199.jpeg" - }, { - "chinese_style": "《银魂》", - "english_style": "Hideaki Sorachi style", - "id": "4b98130b-ca33-468a-828e-36461e9f4da4", - "image": "image193.jpeg" - }, { - "chinese_style": "《全职猎人》", - "english_style": "Yoshihiro Togashi style", - "id": "22f84eff-c3d9-4c39-87df-8ab5b8908a78", - "image": "image196.jpeg" - }, { - "chinese_style": "《柯南》", - "english_style": "Gosho Aoyama style", - "id": "00abd3b8-d038-4cb0-bc8a-cf24b1c31f18", - "image": "image197.jpeg" - }, { - "chinese_style": "《妖精的尾巴》", - "english_style": "Hiro Mashima style", - "id": "41fe2942-2a96-4b84-ae9c-93b17d4b71c2", - "image": "image194.jpeg" - }, { - "chinese_style": "《黑子的篮球》", - "english_style": "Tadatoshi Fujimaki style", - "id": "96eb6f9d-9aca-40de-add5-6d27f0768660", - "image": "image195.jpeg" - }, { - "chinese_style": "《我的英雄学院》", - "english_style": "Kohei Horikoshi style", - "id": "3aafb780-ac7f-4e83-b1ae-2145026c24a8", - "image": "image201.jpeg" - }, { - "chinese_style": "《灌篮高手》", - "english_style": "Takehiko Inoue style", - "id": "97cf6531-e726-4e3c-b97f-64e6f0528634", - "image": "image200.jpeg" - }, { - "chinese_style": "《排球少年》", - "english_style": "Haruichi Furudate style", - "id": "c6f8d9e7-3d06-42a1-b255-989a2c711d90", - "image": "image204.jpeg" - }, { - "chinese_style": "《钻石王牌》", - "english_style": "Yuji Terajima style", - "id": "59f88205-0bce-4deb-b09c-f2db188e52ff", - "image": "image205.jpeg" - }, { - "chinese_style": "《浪客剑心》", - "english_style": "Nobuhiro Watsuki style", - "id": "73af468f-0b11-4d2e-9f45-142d4248ffe9", - "image": "image206.jpeg" - }, { - "chinese_style": "《幽游白书》", - "english_style": "Yoshihiro Togashi style", - "id": "816e1234-4126-422b-83a9-af25303c721d", - "image": "image202.jpeg" - }, { - "chinese_style": "《城市猎人》", - "english_style": "Tsukasa Hojo style", - "id": "33900424-0e31-44af-b147-fdd916b1405d", - "image": "image203.jpeg" - }, { - "chinese_style": "《圣斗士星矢》", - "english_style": "Masami Kurumada style", - "id": "d780c5bc-4e54-43d9-8291-ed9fbc39bd5a", - "image": "image207.jpeg" - }, { - "chinese_style": "《铁臂阿童木》", - "english_style": "Osamu Tezuka style", - "id": "edb4c3fa-e4af-40f6-b97f-ab7326088b80", - "image": "image181.jpeg" - }, { - "chinese_style": "《东京爱情故事》", - "english_style": "Yuki Fujimoto style", - "id": "db55ec39-a9bf-47aa-813f-ab3be24254d6", - "image": "image185.jpeg" - }, { - "chinese_style": "《深夜食堂》", - "english_style": "Yaro Abe style", - "id": "95d69b5e-19ec-47ce-92cd-7853cf3aa023", - "image": "image186.jpeg" - }, { - "chinese_style": "《恋爱暴君》", - "english_style": "Megane Mihoshi style", - "id": "1cf0fa67-dc9e-41d8-84ca-184b4e9e54ba", - "image": "image188.jpeg" - }, { - "chinese_style": "《请回答1988》", - "english_style": "Ilkwon Ha style", - "id": "fcbeee97-34cd-4dcb-9353-547e1d15b3ca", - "image": "image182.jpeg" - }, { - "chinese_style": "《NANA》", - "english_style": "Ai Yazawa style", - "id": "5c275a98-11bc-4cfe-95ee-f29829f2db45", - "image": "image183.jpeg" - }, { - "chinese_style": "《蜂蜜与四叶草》", - "english_style": "Chica Umino style", - "id": "0be84542-7362-45cc-b2e0-8203fbf9a628", - "image": "image184.jpeg" - }, { - "chinese_style": "《东京塔》", - "english_style": "Lily Franky style", - "id": "b70b64b2-ff97-4fcd-b5f7-aa0b8727ce6b", - "image": "image187.jpeg" - }, { - "chinese_style": "《工作狂》", - "english_style": "Anno Moyoco style", - "id": "c2b9a3ce-c33f-4c16-8d68-db3c36da18b5", - "image": "image213.jpeg" - }, { - "chinese_style": "《单身男女》", - "english_style": "Rakuda Torino style", - "id": "ac5fb0d0-e607-4c7f-ad78-b3759ec3c994", - "image": "image208.jpeg" - }, { - "chinese_style": "《恋爱情结》", - "english_style": "Aya Nakahara style", - "id": "bba7a88f-cd93-49f0-8cc7-977b3fd5422c", - "image": "image214.jpeg" - }, { - "chinese_style": "《白领羽田》", - "english_style": "Akiko Higashimura style", - "id": "d3261949-efa2-4066-8d39-e2061120bb71", - "image": "image215.jpeg" - }, { - "chinese_style": "《东京女子图鉴》", - "english_style": "Akiko Higashimura style", - "id": "0855c327-3127-4377-9bd5-1d12ff9ae684", - "image": "image211.jpeg" - }, { - "chinese_style": "《重版出来!》", - "english_style": "Eiji Nonaka style", - "id": "22f75192-b891-4562-8662-53650f52bf4f", - "image": "image210.jpeg" - }, { - "chinese_style": "《逃避虽可耻但有用》", - "english_style": "Tsunami Umino style", - "id": "9bf58d40-ab11-49b8-8700-8db9067e7369", - "image": "image209.jpeg" - }, { - "chinese_style": "《下辈子再好好过》", - "english_style": "Kazuto Tatsukawa style", - "id": "208444d2-c17c-40f8-93e2-2365676fb900", - "image": "image212.jpeg" - }, { - "chinese_style": "《无法成为野兽的我们》", - "english_style": "Ryoko Fukuyama style", - "id": "39109010-4ce4-4b72-a200-883b8feb62cf", - "image": "image191.jpeg" - }, { - "chinese_style": "《我不是结不了婚,只是不想》", - "english_style": "Ako Shimaki style", - "id": "1be7fe21-d5e2-4a90-b5aa-8df219b55810", - "image": "image190.jpeg" - }, { - "chinese_style": "《恋爱小人们》", - "english_style": "Musawo style", - "id": "3455d950-4d11-426d-891a-b0453606df34", - "image": "image189.jpeg" - }], - "id": "6ab67e20-2fbe-449b-b371-5f411c4c3892" - }, { - "sheet": 12, - "name": "恐怖日本动漫风", - "subStyle": [{ - "chinese_style": "《富江》", - "english_style": "Junji Ito style", - "id": "3bc00c50-04ff-41c1-8104-c31680b469d8", - "image": "image218.jpeg" - }, { - "chinese_style": "《寄生兽》", - "english_style": "Hitoshi Iwaaki style", - "id": "64d9204c-296d-4b09-9b50-36d89268d7fa", - "image": "image220.jpeg" - }, { - "chinese_style": "《漩涡》", - "english_style": "Kyo Shirodaira and Eita Mizuno style", - "id": "0522f21f-9094-4a95-9648-5a9f574d406b", - "image": "image222.jpeg" - }, { - "chinese_style": "《死亡笔记》", - "english_style": "Tsugumi Ohba and Takeshi Obata style", - "id": "bde5b7bc-37ad-4a59-829d-21f26c222b29", - "image": "image219.jpeg" - }, { - "chinese_style": "《怪医黑杰克》", - "english_style": "Osamu Tezuka style", - "id": "78cc9352-b68a-48d3-a32e-ebf13de78b3c", - "image": "image221.jpeg" - }, { - "chinese_style": "《学园默示录》", - "english_style": "Daisuke Sato and Shouji Sato style", - "id": "5e34a2d4-4247-4feb-a369-eaf781668171", - "image": "image217.jpeg" - }, { - "chinese_style": "《鬼灯的冷彻》", - "english_style": "Natsumi Eguchi style", - "id": "854eb4d2-fdf1-40cf-a668-2da4dd0c103a", - "image": "image216.jpeg" - }, { - "chinese_style": "《僵尸借贷》", - "english_style": "Peach-Pit style", - "id": "a98ad1ff-0e25-4ac6-872e-1be6dacfb99e", - "image": "image223.jpeg" - }, { - "chinese_style": "《东京喰种》", - "english_style": "Sui Ishida style", - "id": "2d3b1c15-4ff6-4843-9dec-4b5f16b3d1c6", - "image": "image224.jpeg" - }], - "id": "990502bb-b673-4138-9af6-b2e483acdbc0" - }, { - "sheet": 13, - "name": "机甲日本动漫风", - "subStyle": [{ - "chinese_style": "《机动战士高达》", - "english_style": "Yoshiyuki Tomino style", - "id": "d7f9add9-5dbe-4b9a-ac5a-3905be18c73d", - "image": "image231.jpeg" - }, { - "chinese_style": "《新世纪福音战士》", - "english_style": "Hideaki Anno and Yoshiyuki Sadamoto style", - "id": "138a6ff3-6562-4410-9d2e-aa849f512f98", - "image": "image237.jpeg" - }, { - "chinese_style": "《钢弹0080:口袋中的战争》", - "english_style": "Yoshiyuki Tomino and Takashi Imanishi style", - "id": "350a64e1-8e2b-4f5b-b8d9-a0af5a6454ec", - "image": "image232.jpeg" - }, { - "chinese_style": "《全金属狂潮》", - "english_style": "Shoji Kawamori style", - "id": "b9f034b8-4146-429a-83a6-dc76bacee682", - "image": "image230.jpeg" - }, { - "chinese_style": "《机动警察》", - "english_style": "Kunio Okawara style", - "id": "55cd3098-7c01-406b-ae0e-1964b28d0925", - "image": "image233.jpeg" - }, { - "chinese_style": "《超时空要塞》", - "english_style": "Shoji Kawamori style", - "id": "d2e08101-5654-4edb-a0d3-d65b54a366e5", - "image": "image236.jpeg" - }, { - "chinese_style": "《铁拳小子》", - "english_style": "Naoto Tsushima style", - "id": "8ef6433d-c274-4add-a74d-6b9ac9fff81b", - "image": "image234.jpeg" - }, { - "chinese_style": "《装甲骑兵》", - "english_style": "Ryousuke Takahashi style", - "id": "c9444ff5-a319-4e3f-96b6-4cad9f5d7736", - "image": "image235.jpeg" - }, { - "chinese_style": "《机动战士高达SEED》", - "english_style": "Yoshiyuki Tomino and Kenji Oguro style", - "id": "898c1979-99b2-46a7-b581-8272dd37cdba", - "image": "image227.jpeg" - }, { - "chinese_style": "《交响诗篇》", - "english_style": "Shinichirou Watanabe and Yasuhiro Nightow style", - "id": "2d420511-63f9-4c77-9e06-efab1df5b815", - "image": "image229.jpeg" - }, { - "chinese_style": "《天元突破》", - "english_style": "Kimiyoshi Yasuda and Hiroyuki Imaishi style", - "id": "c6cb088d-2d9f-45c1-a38f-5a69545c1c5d", - "image": "image225.jpeg" - }, { - "chinese_style": "《机动战士高达00》", - "english_style": "Yoshiyuki Tomino and Kenji Oguro style", - "id": "6ab2f65f-4f70-4c57-b4f9-99d66fabe4df", - "image": "image226.jpeg" - }, { - "chinese_style": "《ZEGAPAIN》", - "english_style": "Kouichi Chigira and Shinji Aramaki style", - "id": "8238df2e-c1be-4849-9684-1c217fe51b4c", - "image": "image228.jpeg" - }], - "id": "e646e2a4-a467-4506-8838-316d45cf5120" - }, { - "sheet": 14, - "name": "校园日本动漫风", - "subStyle": [{ - "chinese_style": "《青春猪头少年不会梦到兔女郎学姐》", - "english_style": "Daisuke Aizawa style", - "id": "a4bcfe18-7e85-4ebf-8a89-bc0f946e6dd5", - "image": "image242.jpeg" - }, { - "chinese_style": "《我的青春恋爱物语果然有问题》", - "english_style": "Hajime Kamoshida style", - "id": "dfebd4b2-657c-4c46-9319-c3834fde01d5", - "image": "image240.jpeg" - }, { - "chinese_style": "《某科学的超电磁炮》", - "english_style": "Kazuma Kamachi and Motoi Fushimi style", - "id": "f32174cb-1d1a-4cd6-bb14-6bcef9bf068e", - "image": "image243.jpeg" - }, { - "chinese_style": "《干物妹!小埋》", - "english_style": "Sakura Ikezuki style", - "id": "f2ca5f57-e759-4c9a-aa20-39c6862e5892", - "image": "image244.jpeg" - }, { - "chinese_style": "《四月是你的谎言》", - "english_style": "Kanade Yoko style", - "id": "0a07af72-fd77-468d-b667-5879048eb289", - "image": "image238.jpeg" - }, { - "chinese_style": "《Free!》", - "english_style": "Kouji Oji style", - "id": "4745b1fa-466e-4a25-8aa8-1017a55f6a3f", - "image": "image239.jpeg" - }, { - "chinese_style": "《樱花庄的宠物女孩》", - "english_style": "Hiroshi Kumakura and Tatsuhiko Takimoto style", - "id": "b7f2b370-2660-4653-86f1-e1b1bc959c35", - "image": "image245.jpeg" - }, { - "chinese_style": "《从零开始的异世界生活》", - "english_style": "Nagatsuki Tappei style", - "id": "6a8e1708-835b-4266-8546-cea85db6a7a1", - "image": "image241.jpeg" - }, { - "chinese_style": "《冰菓》", - "english_style": "Hyouka Fujino style", - "id": "423eedf0-0adb-4738-bd8b-a1d1a3b41bae", - "image": "image246.jpeg" - }, { - "chinese_style": "《Love Lab》", - "english_style": "Miki Yoshikawa style", - "id": "8cd108ea-2276-4b2b-9fb9-0078e1b28292", - "image": "image248.jpeg" - }, { - "chinese_style": "《月刊少女野崎君》", - "english_style": "Hima Katsura style", - "id": "57e7d3b8-640b-4ab4-a2c3-708af855dd96", - "image": "image250.jpeg" - }, { - "chinese_style": "《Charlotte》", - "english_style": "Jun Maeda style", - "id": "bd564191-e6f7-459d-8b8e-9df837f09a8a", - "image": "image249.jpeg" - }, { - "chinese_style": "《K-On!》", - "english_style": "Manga Mitogawa and Koji Oji style", - "id": "6c2ded36-ccd4-4ffd-972e-2587737efea6", - "image": "image247.jpeg" - }], - "id": "fbba8ae8-502c-4ae5-856c-ffb70efd852d" - }, { - "sheet": 15, - "name": "传统日本动漫风", - "subStyle": [{ - "chinese_style": "《红辣椒》", - "english_style": "Satoshi Kon style", - "id": "7c2f0bea-67fb-42b1-b02c-e9db30153055", - "image": "image260.jpeg" - }, { - "chinese_style": "《时之歌》", - "english_style": "Makoto Shinkai style", - "id": "997b348f-aef8-4e3a-973c-d93153cbe8c5", - "image": "image263.jpeg" - }, { - "chinese_style": "《完美蓝》", - "english_style": "Mamoru Oshii style", - "id": "a1620fb5-2a77-47f5-9ca9-0245e3bbf278", - "image": "image261.jpeg" - }, { - "chinese_style": "《狼的孩子雨和雪》", - "english_style": "Mamoru Hosoda style", - "id": "4427cac2-4f4a-490b-990f-cf6d46c9ba3a", - "image": "image264.jpeg" - }, { - "chinese_style": "《夏日大作战》", - "english_style": "Kunihiko Ikuhara style", - "id": "1e34a1d4-58cd-4b8c-ab61-b6724bbb053c", - "image": "image262.jpeg" - }, { - "chinese_style": "《未麻的部屋》", - "english_style": "Rintaro style", - "id": "cc689d92-8525-475a-90a4-f42d0adacfd2", - "image": "image265.jpeg" - }, { - "chinese_style": "《千年女优》", - "english_style": "Kon Satoshi style", - "id": "189a8d33-391c-4406-9f77-9811fc39429b", - "image": "image266.jpeg" - }, { - "chinese_style": "《虚空之旅》", - "english_style": "Katsuhiro Otomo style", - "id": "b65d8e8a-1ec1-46cf-a32b-8e16c6d4cada", - "image": "image267.jpeg" - }, { - "chinese_style": "《悬崖上的金鱼姬》", - "english_style": "Goro Miyazaki style", - "id": "fe069298-88d6-4ccd-8076-0af31aa1336c", - "image": "image273.jpeg" - }, { - "chinese_style": "《雪国列车》", - "english_style": "Yoshiaki Kawajiri style", - "id": "bf59ebda-e6df-47cc-9dde-f522b5145b58", - "image": "image270.jpeg" - }, { - "chinese_style": "《猎天使魔女》", - "english_style": "Yasuhito Kikuchi style", - "id": "72877d59-e763-4440-b41b-4826da6718c7", - "image": "image268.jpeg" - }, { - "chinese_style": "《猎魔人》", - "english_style": "Yoshiaki Kawajiri style", - "id": "dfbb0b74-a875-442f-9219-3aad3a66b2bb", - "image": "image274.jpeg" - }, { - "chinese_style": "《幽灵公主》", - "english_style": "Princess Mononoke style", - "id": "7458efbb-97fd-454f-b93f-85ac2e42c139", - "image": "image275.jpeg" - }, { - "chinese_style": "《百变狸猫》", - "english_style": "Pom Poko style", - "id": "0e6b6836-297b-4805-8f65-00e83693a1ae", - "image": "image271.jpeg" - }, { - "chinese_style": "《岁月的童话》", - "english_style": "Only Yesterday style", - "id": "7978dce5-aaa4-46d8-bf68-5b52758d6fa7", - "image": "image269.jpeg" - }, { - "chinese_style": "《我的邻居山田君》", - "english_style": "The Yamadas style", - "id": "c92165f2-00e8-4c06-b3e4-7531f145e3be", - "image": "image272.jpeg" - }, { - "chinese_style": "《猫的报恩》", - "english_style": "The Cat Returns style", - "id": "48aacb4a-501e-4fe6-8cb7-a2ad86657033", - "image": "image258.jpeg" - }, { - "chinese_style": "《借东西的小人阿莉埃蒂》", - "english_style": "Arrietty style", - "id": "48a38ff1-da64-496e-aec0-89c0dea12cc9", - "image": "image255.jpeg" - }, { - "chinese_style": "《起风了》", - "english_style": "The Wind Rises style", - "id": "5ae600ac-6797-4f11-b9e2-176856a1c23a", - "image": "image251.jpeg" - }, { - "chinese_style": "《辉夜姬物语》", - "english_style": "The Tale of The Princess Kaguya style", - "id": "b7ae0ca4-765e-4dda-9d79-63009b943afe", - "image": "image252.jpeg" - }, { - "chinese_style": "《地海传说》", - "english_style": "Tales from Earthsea style", - "id": "ba04d563-d7d5-453e-92d8-84b7dbb04828", - "image": "image253.jpeg" - }, { - "chinese_style": "《侧耳倾听》", - "english_style": "Whisper of the Heart style", - "id": "f3d6adc2-6c8d-4eeb-ac9e-4bc470099a87", - "image": "image254.jpeg" - }, { - "chinese_style": "《时光代理人》", - "english_style": "Link Click style", - "id": "1379ee57-77f0-44ea-ba0f-37d448cf2d86", - "image": "image257.jpeg" - }, { - "chinese_style": "《攻壳机动队》", - "english_style": "Ghost in the Shell style", - "id": "01da912f-f68a-4cff-aac8-5f7cdf945c0e", - "image": "image256.jpeg" - }, { - "chinese_style": "《宇宙战舰大和号》", - "english_style": "Space Battleship Yamato style", - "id": "c24de2c9-1dc7-4c9a-b0ff-7fed7a4f9b32", - "image": "image259.jpeg" - }], - "id": "a4bae3b0-7ca7-45b4-9a83-505e3407fd5e" - }, { - "sheet": 16, - "name": "国产动漫风", - "subStyle": [{ - "chinese_style": "《镖人》", - "english_style": "Biao Ren style", - "id": "12910c0e-1945-47d9-a1a4-0e13a5fa2616", - "image": "image283.jpeg" - }, { - "chinese_style": "《斗破苍穹》", - "english_style": "Battle Through the Heavens style", - "id": "f2d8e7f4-c4c6-497e-9a0d-cf7957e382dd", - "image": "image281.jpeg" - }, { - "chinese_style": "《雪中悍刀行》", - "english_style": "The Swordsman in the Rain style", - "id": "a863dda7-6be4-4c9c-b64a-ccb8561c89d0", - "image": "image287.jpeg" - }, { - "chinese_style": "《大主宰》", - "english_style": "The Great Ruler style", - "id": "bbbc22aa-d99f-4fa3-9f32-d02ff1bb140f", - "image": "image284.jpeg" - }, { - "chinese_style": "《秦时明月》", - "english_style": "The Legend of Qin style", - "id": "77a8e93f-bf9c-433a-9266-03b9b2bf7135", - "image": "image282.jpeg" - }, { - "chinese_style": "《天行九歌》", - "english_style": "Nine Songs style", - "id": "487c6c3d-fed2-43e7-87af-02736ad596b7", - "image": "image285.jpeg" - }, { - "chinese_style": "《画江湖之不良人》", - "english_style": "Hua Jianghu: The Bad Guys style", - "id": "dbc9edbb-72ad-4fc2-a0ae-fc146e0c5226", - "image": "image288.jpeg" - }, { - "chinese_style": "《白箱》", - "english_style": "Shirobako style", - "id": "67043ea7-f930-4144-b8de-6ad3d447e567", - "image": "image286.jpeg" - }, { - "chinese_style": "《百鬼屋》", - "english_style": "House of Hundred Ghosts style", - "id": "3f6b5402-3974-4fd8-83e7-ef88bdc4dad3", - "image": "image291.jpeg" - }, { - "chinese_style": "《端脑》", - "english_style": "Du Nao style", - "id": "dc7994f0-4318-4455-8f8d-e72554be17a9", - "image": "image292.jpeg" - }, { - "chinese_style": "《诛仙》", - "english_style": "Zhuxian style", - "id": "400a1638-0d7e-4a3b-a371-ec2ed1d27a11", - "image": "image294.jpeg" - }, { - "chinese_style": "《哪吒之魔童降世》", - "english_style": "Ne Zha style", - "id": "82517970-da4a-4f8b-aa90-84d84c09cbfb", - "image": "image295.jpeg" - }, { - "chinese_style": "《西游记》", - "english_style": "Journey to the West style", - "id": "3d898e60-ea86-4f20-90f2-f0c09c424b47", - "image": "image296.jpeg" - }, { - "chinese_style": "《封神纪》", - "english_style": "Fengshen Ji style", - "id": "25d49d46-9dc0-45ff-a704-dccdaded159f", - "image": "image289.jpeg" - }, { - "chinese_style": "《山海逆战》", - "english_style": "Shan Hai Ni Zhan style", - "id": "f5083376-cefc-40b3-a8d3-ad4cf3fad713", - "image": "image293.jpeg" - }, { - "chinese_style": "《百炼成神》", - "english_style": "Becoming a God style", - "id": "fd053b5e-1ce3-47bf-95e6-65a6caf739ec", - "image": "image290.jpeg" - }, { - "chinese_style": "《白蛇传》", - "english_style": "The Legend of White Snake style", - "id": "5cf1e3fd-9458-417c-bd7b-33d93a45958d", - "image": "image280.jpeg" - }, { - "chinese_style": "《妖神记》", - "english_style": "Chronicles of the Demon King style", - "id": "e6a1bdbe-8114-4393-a07b-080c8bbb3b1b", - "image": "image277.jpeg" - }, { - "chinese_style": "《镇魂街》", - "english_style": "Rakshasa Street style", - "id": "3dcaff26-29f4-43a1-8141-0ca108420d91", - "image": "image278.jpeg" - }, { - "chinese_style": "《斗罗大陆》", - "english_style": "Soul Land style", - "id": "c7303c09-1aa4-4156-9b21-3c63794d4e8e", - "image": "image276.jpeg" - }, { - "chinese_style": "《血族》", - "english_style": "Bloodline style", - "id": "02a2e278-4eb3-4384-a13a-80e1eef5b627", - "image": "image279.jpeg" - }], - "id": "fa957f58-129e-42cc-b80a-24859fc90789" - }, { - "sheet": 17, - "name": "电影风格", - "subStyle": [{ - "chinese_style": "黑白电影", - "english_style": "Black and white movies", - "id": "7bffd5d9-71e7-4443-99b7-d38cbe5853ee", - "image": "image309.jpeg" - }, { - "chinese_style": "科幻电影", - "english_style": "science fiction film", - "id": "f17c812e-aafd-4ac4-b6e4-36467f75c4e6", - "image": "image310.jpeg" - }, { - "chinese_style": "爱情电影", - "english_style": "romantic movies", - "id": "d2bac280-9bff-4582-a78e-b94a3f75950e", - "image": "image311.jpeg" - }, { - "chinese_style": "动作电影", - "english_style": "action movies", - "id": "a723ca3f-7f59-436b-b4aa-bd99f58bfe45", - "image": "image312.jpeg" - }, { - "chinese_style": "喜剧电影", - "english_style": "comedy movies", - "id": "1a39e88a-2e08-4adf-8859-76d7ac36f2fd", - "image": "image306.jpeg" - }, { - "chinese_style": "恐怖电影", - "english_style": "horror movies", - "id": "cafb6bde-4f43-4fbf-8bf4-9b9a2727c7bd", - "image": "image307.jpeg" - }, { - "chinese_style": "战争电影", - "english_style": "war movies", - "id": "64564b1d-69a2-45ff-b25e-3f073a45b76f", - "image": "image308.jpeg" - }, { - "chinese_style": "纪录片", - "english_style": "documentaries", - "id": "304e6b0f-1b30-4336-8f88-79b907d9592d", - "image": "image305.jpeg" - }, { - "chinese_style": "动画电影", - "english_style": "animated movies", - "id": "3ae766f0-a97b-4004-8a06-b5b8bd76f668", - "image": "image297.jpeg" - }, { - "chinese_style": "奇幻电影", - "english_style": "fantasy movies", - "id": "282b2144-8cf3-470e-9179-7f9419d12a1c", - "image": "image299.jpeg" - }, { - "chinese_style": "惊悚电影", - "english_style": "thriller movies", - "id": "ab55351f-3c55-4828-babc-a34e724d95a2", - "image": "image303.jpeg" - }, { - "chinese_style": "犯罪电影", - "english_style": "crime movies", - "id": "f95ea621-d040-47e5-b6e0-f6f326af1036", - "image": "image300.jpeg" - }, { - "chinese_style": "冒险电影", - "english_style": "adventure movies", - "id": "eb6ecf79-2d53-454b-8302-7cca5c9b591b", - "image": "image301.jpeg" - }, { - "chinese_style": "家庭电影", - "english_style": "family movies", - "id": "c2e2d144-8c87-4110-930b-fdfd41df9b30", - "image": "image302.jpeg" - }, { - "chinese_style": "历史剧", - "english_style": "historical dramas", - "id": "029cc87b-eeab-4f95-ac9f-425460ee9f08", - "image": "image298.jpeg" - }, { - "chinese_style": "音乐剧", - "english_style": "musical movies", - "id": "61993e49-68db-4be4-b8a9-908e1ac7b962", - "image": "image304.jpeg" - }, { - "chinese_style": "悬疑电影", - "english_style": "mystery movies", - "id": "98bf0ddc-4904-4576-9f93-db27363eee28", - "image": "image409.jpeg" - }, { - "chinese_style": "戏剧电影", - "english_style": "drama movies", - "id": "59885546-5b58-40c8-8202-ded895601539", - "image": "image410.jpeg" - }, { - "chinese_style": "运动电影", - "english_style": "sports movies", - "id": "42232865-7ffb-49e1-989b-efab01e44ac0", - "image": "image411.jpeg" - }], - "id": "5d5b2cfa-a417-4723-9931-a3abbac2b452" - }, { - "sheet": 18, - "name": "艺术风格", - "subStyle": [{ - "chinese_style": "幻想艺术风格", - "english_style": "Fantasy art style", - "id": "a7f0560f-fbaa-4fbe-abae-70401e6fedbe", - "image": "image333.jpeg" - }, { - "chinese_style": "复古漫画风格", - "english_style": "Retro comic style", - "id": "7fb47c08-d2e3-439d-9e5d-4a9e3886203e", - "image": "image338.jpeg" - }, { - "chinese_style": "抽象艺术风格", - "english_style": "Abstract art style", - "id": "a7e465c0-8c08-4407-928e-00ddcfc1e14d", - "image": "image331.jpeg" - }, { - "chinese_style": "极简主义风格", - "english_style": "Minimalist style", - "id": "2b4f5f05-9f9f-4605-8ea6-3e9f3e9390b7", - "image": "image335.jpeg" - }, { - "chinese_style": "涂鸦风格", - "english_style": "Graffiti style", - "id": "ca054eb1-ede6-48da-9832-46d2edb04020", - "image": "image336.jpeg" - }, { - "chinese_style": "浮世绘风格", - "english_style": "Ukiyo-e style", - "id": "d73397d8-6535-4a73-a6a8-2107c216c2ae", - "image": "image332.jpeg" - }, { - "chinese_style": "表现主义风格", - "english_style": "Expressionism style", - "id": "ef600238-917d-4bb2-a38d-6619dce4724d", - "image": "image337.jpeg" - }, { - "chinese_style": "超现实主义风格", - "english_style": "Surrealism style", - "id": "c4df7c3e-82b1-44dd-9c7a-6ce48c4c157d", - "image": "image334.jpeg" - }, { - "chinese_style": "装饰艺术风格", - "english_style": "Art Deco style", - "id": "33e5d594-dc86-4396-82d9-89544b356c69", - "image": "image341.jpeg" - }, { - "chinese_style": "未来主义风格", - "english_style": "Futurism style", - "id": "0a59d038-ff76-4575-aa38-616889b38361", - "image": "image344.jpeg" - }, { - "chinese_style": "像素艺术风格", - "english_style": "Pixel art style", - "id": "57e62d07-2d1a-450a-a16e-6641b64c9c07", - "image": "image346.jpeg" - }, { - "chinese_style": "漫画现实主义风格", - "english_style": "Comic realism style", - "id": "6f96a537-a952-4c98-adb5-02ca82025479", - "image": "image340.jpeg" - }, { - "chinese_style": "卡通渲染风格", - "english_style": "Cartoon rendering style", - "id": "0957f80e-2c3e-472a-8f05-263ff831c8ae", - "image": "image339.jpeg" - }, { - "chinese_style": "哥特式漫画风格", - "english_style": "Gothic comic style", - "id": "5ab38c3b-a63c-4852-85d0-5cde07fc900a", - "image": "image345.jpeg" - }, { - "chinese_style": "新古典主义风格", - "english_style": "Neoclassicism style", - "id": "ce5ee6e7-01d2-42b9-85aa-47b4503cfabc", - "image": "image342.jpeg" - }, { - "chinese_style": "浪漫主义风格", - "english_style": "Romanticism style", - "id": "38d106e0-2272-4964-8f83-84b9808d8d1d", - "image": "image343.jpeg" - }, { - "chinese_style": "街头艺术风格", - "english_style": "Street art style", - "id": "ef0a8d87-ba4f-48df-b2f9-11345eb75c10", - "image": "image319.jpeg" - }, { - "chinese_style": "概念艺术风格", - "english_style": "Concept art style", - "id": "347d3e35-5a70-451f-b6cf-58aaf339fc34", - "image": "image317.jpeg" - }, { - "chinese_style": "幻想漫画风格", - "english_style": "Fantasy comic style", - "id": "fdda76a9-25a5-47fa-9570-ec12c6c744c8", - "image": "image316.jpeg" - }, { - "chinese_style": "科幻风格", - "english_style": "Science fiction style", - "id": "8e26e6b3-c109-43c0-ae58-7f68ff050a88", - "image": "image320.jpeg" - }, { - "chinese_style": "西部风格", - "english_style": "Western style", - "id": "b23cf1e2-3449-48ae-b71d-160f081a7202", - "image": "image314.jpeg" - }, { - "chinese_style": "儿童插画风格", - "english_style": "Children's illustration style", - "id": "4572cc54-54e1-41b5-9d63-0b8e53a6bdb6", - "image": "image318.jpeg" - }, { - "chinese_style": "青春漫画风格", - "english_style": "Youth comic style", - "id": "06ccb7d6-d722-418e-ab6f-77f72c048814", - "image": "image315.jpeg" - }, { - "chinese_style": "历史题材漫画风格", - "english_style": "Historical comic style", - "id": "af31cb40-2f6a-486d-9e7b-667837360b15", - "image": "image313.jpeg" - }, { - "chinese_style": "民间艺术风格", - "english_style": "Folk art style", - "id": "750cca85-be64-42c4-b993-b53c414fd01c", - "image": "image325.jpeg" - }, { - "chinese_style": "艺术新潮流风格", - "english_style": "Art Nouveau style", - "id": "755ac85b-8c53-4674-a2ca-3ca86c588814", - "image": "image322.jpeg" - }, { - "chinese_style": "艺术装饰风格", - "english_style": "Art Deco style", - "id": "b20fc215-8631-4334-a345-27de5fbe83b5", - "image": "image328.jpeg" - }, { - "chinese_style": "艺术与工艺风格", - "english_style": "Arts and Crafts style", - "id": "b7bbf919-b6dc-4074-a42e-986b317dc43b", - "image": "image323.jpeg" - }, { - "chinese_style": "野兽派风格", - "english_style": "Fauvism style", - "id": "c8c19729-1cad-424e-8d89-79e024587100", - "image": "image326.jpeg" - }, { - "chinese_style": "立体主义风格", - "english_style": "Cubism style", - "id": "c1c2d744-3e88-4d78-8a91-0626caee0f21", - "image": "image324.jpeg" - }, { - "chinese_style": "原始主义风格", - "english_style": "Primitivism style", - "id": "a784d492-5c63-4ed0-849e-af7fc29ee76e", - "image": "image327.jpeg" - }, { - "chinese_style": "波普艺术风格", - "english_style": "Pop art style", - "id": "cd5726f6-6ef6-4cd8-a59e-8e06584ee6b7", - "image": "image321.jpeg" - }, { - "chinese_style": "后现代主义风格", - "english_style": "Postmodernism style", - "id": "f1f2c258-b5ba-41ba-994b-5f7ab3f180ae", - "image": "image329.jpeg" - }, { - "chinese_style": "超扁平风格", - "english_style": "Superflat style", - "id": "531ec980-a5e0-4126-ba9d-449fa8b3605b", - "image": "image330.jpeg" - }], - "id": "4dfa6a37-ea05-4a09-aabb-eed2c714830e" - }, { - "sheet": 19, - "name": "国风", - "subStyle": [{ - "chinese_style": "中国水墨画", - "english_style": "Chinese ink wash painting", - "id": "9aa7dced-46c5-4b9c-9159-41f3d9239a98", - "image": "image352.jpeg" - }, { - "chinese_style": "工笔花鸟画", - "english_style": "Fine-brushwork floral and bird painting", - "id": "e67ae511-04c0-4493-a696-a6799b07f2f8", - "image": "image347.jpeg" - }, { - "chinese_style": "青绿山水画", - "english_style": "Green and blue landscape painting", - "id": "46e39c6a-5df8-49e7-9eed-807cd3f14db2", - "image": "image349.jpeg" - }, { - "chinese_style": "敦煌壁画", - "english_style": "Dunhuang murals", - "id": "85561e1f-72dc-4c7e-a1ce-f70f5ccb4f5f", - "image": "image348.jpeg" - }, { - "chinese_style": "剪纸艺术", - "english_style": "Chinese paper-cut art", - "id": "7ebba49c-e8ef-45f8-bc68-e80036b92b62", - "image": "image350.jpeg" - }, { - "chinese_style": "年画", - "english_style": "New Year paintings", - "id": "e84038c0-0831-485e-beaa-d9247f960ec0", - "image": "image351.jpeg" - }], - "id": "aa51c366-fcaa-4063-84d7-ad5af8bca191" - }, { - "sheet": 20, - "name": "其他应用", - "subStyle": [{ - "chinese_style": "自然风光", - "english_style": "Natural scenery style", - "id": "75b472ed-192d-4120-87af-c38f353b938f", - "image": "image403.jpeg" - }, { - "chinese_style": "宇宙星空", - "english_style": "Space and stars theme", - "id": "a15f8591-0ce4-4d9d-9301-92b6f98b927d", - "image": "image404.jpeg" - }, { - "chinese_style": "城市建筑", - "english_style": "Urban architecture style", - "id": "be9d8a98-d011-4dd4-ace1-c55119691d0c", - "image": "image405.jpeg" - }, { - "chinese_style": "传统文化", - "english_style": "Traditional culture theme", - "id": "3e1e9539-4e2c-4ed5-82cc-84ec83a7f370", - "image": "image406.jpeg" - }, { - "chinese_style": "现代艺术", - "english_style": "Modern art style", - "id": "bc270c1a-ab1d-4e9d-a9a0-5413323802dd", - "image": "image407.jpeg" - }, { - "chinese_style": "民族风情", - "english_style": "Ethnic style", - "id": "43f968fd-e4d3-417a-b40d-a07c2dd691f7", - "image": "image401.jpeg" - }, { - "chinese_style": "古风仙韵", - "english_style": "Ancient charm style", - "id": "d00cead5-0991-40ed-a64a-5e0a8b5770a9", - "image": "image402.jpeg" - }, { - "chinese_style": "未来幻想", - "english_style": "Future fantasy style", - "id": "3537c9f9-a77f-4daf-84cc-73b260b2c155", - "image": "image408.jpeg" - }, { - "chinese_style": "科技机械", - "english_style": "Technology and machinery theme", - "id": "e83d74ec-697c-4962-8ad3-aa8bd4d85496", - "image": "image356.jpeg" - }, { - "chinese_style": "人物插画", - "english_style": "Character illustration style", - "id": "c1c6b6bb-d1ec-40b4-b703-a9ea67c73786", - "image": "image353.jpeg" - }, { - "chinese_style": "动漫卡通", - "english_style": "Anime and cartoon style", - "id": "73425a65-a3ae-4c51-a64c-670984165e32", - "image": "image357.jpeg" - }, { - "chinese_style": "游戏原画", - "english_style": "Game concept art style", - "id": "cddd0ead-0178-4b9e-86f9-2c83de6b57d0", - "image": "image358.jpeg" - }, { - "chinese_style": "影视剧照", - "english_style": "Film and TV stills style", - "id": "750f6d3b-7e5a-40d2-a581-c3d21ad4640b", - "image": "image355.jpeg" - }, { - "chinese_style": "广告插图", - "english_style": "Advertising illustration style", - "id": "9d95166e-8131-4a33-9ded-8a9c7bfddcb0", - "image": "image359.jpeg" - }, { - "chinese_style": "包装设计", - "english_style": "Packaging design style", - "id": "03aa78fa-3c6f-450d-b64c-b842a78f4237", - "image": "image360.jpeg" - }, { - "chinese_style": "平面海报", - "english_style": "Graphic poster style", - "id": "aaed015c-806c-4d8c-9fb4-7ed0d28098b5", - "image": "image354.jpeg" - }, { - "chinese_style": "网页UI", - "english_style": "Web UI design style", - "id": "063e6129-8f8b-4d61-a4f2-547342a279a3", - "image": "image364.jpeg" - }, { - "chinese_style": "品牌标识", - "english_style": "Brand identity design style", - "id": "5acd5018-661c-4983-8257-773217ba6951", - "image": "image361.jpeg" - }, { - "chinese_style": "时尚插画", - "english_style": "Fashion illustration style", - "id": "a9424c0e-2a33-40ae-b040-71127529de8e", - "image": "image365.jpeg" - }, { - "chinese_style": "儿童绘本", - "english_style": "Children's picture book style", - "id": "d2c5a700-1848-4341-93ea-75e6eae70dc2", - "image": "image363.jpeg" - }, { - "chinese_style": "科普漫画", - "english_style": "Popular science comic style", - "id": "21110cd9-34fd-4acf-86e5-18ea5250b16b", - "image": "image366.jpeg" - }, { - "chinese_style": "教育绘本", - "english_style": "Educational picture book style", - "id": "03fad4d9-2f97-4c83-9226-7f913cb22794", - "image": "image362.jpeg" - }, { - "chinese_style": "医学插图", - "english_style": "Medical illustration style", - "id": "6da6fa32-ded0-485f-a414-17b3d7fff6d2", - "image": "image367.jpeg" - }, { - "chinese_style": "建筑设计", - "english_style": "Architectural design style", - "id": "70c99e02-0578-4ba2-ab44-c49e354b1198", - "image": "image368.jpeg" - }, { - "chinese_style": "汽车设计", - "english_style": "Automotive design style", - "id": "04454eab-0491-4e38-a9a0-1476f4138ecb", - "image": "image370.jpeg" - }, { - "chinese_style": "工业设计", - "english_style": "Industrial design style", - "id": "c18d111c-f3e6-4df8-9867-df8af1207399", - "image": "image371.jpeg" - }, { - "chinese_style": "室内设计", - "english_style": "Interior design style", - "id": "4428d573-1829-4cad-9ce1-54c8dda6b7c2", - "image": "image374.jpeg" - }, { - "chinese_style": "时装设计", - "english_style": "Fashion design style", - "id": "90f7f936-8b54-4aae-bba1-ee6cdae5ce76", - "image": "image373.jpeg" - }, { - "chinese_style": "珠宝设计", - "english_style": "Jewelry design style", - "id": "1f9b2bd8-caa5-4f6c-ad87-0aa3ab5be760", - "image": "image369.jpeg" - }, { - "chinese_style": "家具设计", - "english_style": "Furniture design style", - "id": "b2d1bf95-5c92-4c9d-be02-cc5dcdeff625", - "image": "image372.jpeg" - }, { - "chinese_style": "平面设计", - "english_style": "Graphic design style", - "id": "761c2f31-2b3f-437b-9784-976aff089724", - "image": "image375.jpeg" - }, { - "chinese_style": "插图设计", - "english_style": "Illustration design style", - "id": "3887bd64-8f74-4994-bd0a-3f3798d0a336", - "image": "image376.jpeg" - }, { - "chinese_style": "动画设计", - "english_style": "Animation design style", - "id": "9961b135-7658-47c8-b8b6-94ee646345be", - "image": "image380.jpeg" - }, { - "chinese_style": "游戏设计", - "english_style": "Game design style", - "id": "4b95b12e-d27e-460c-b3c3-4bf7306adfca", - "image": "image377.jpeg" - }, { - "chinese_style": "用户界面", - "english_style": "User interface design style", - "id": "90a47637-a287-4f73-a6d3-e10b6cd52035", - "image": "image378.jpeg" - }, { - "chinese_style": "虚拟现实", - "english_style": "Virtual reality theme", - "id": "805a3511-4037-4981-b628-85b2c1c5805c", - "image": "image382.jpeg" - }, { - "chinese_style": "增强现实", - "english_style": "Augmented reality theme", - "id": "1126d0da-6610-4183-915c-e9fe695d44b1", - "image": "image381.jpeg" - }, { - "chinese_style": "人工智能", - "english_style": "Artificial intelligence theme", - "id": "c98ee53f-427c-4016-8745-b781ed180307", - "image": "image383.jpeg" - }, { - "chinese_style": "机器人科技", - "english_style": "Robotics and technology theme", - "id": "c6edebf7-f2be-4027-8620-888e0dec1e86", - "image": "image384.jpeg" - }, { - "chinese_style": "生物工程", - "english_style": "Biotechnology theme", - "id": "00425a0d-5e3d-4304-83be-f4653315abed", - "image": "image379.jpeg" - }, { - "chinese_style": "环境保护", - "english_style": "Environmental protection theme", - "id": "764a6b68-3530-4d45-bdbe-3db74c659dea", - "image": "image390.jpeg" - }, { - "chinese_style": "可持续发展", - "english_style": "Sustainable development theme", - "id": "7bf52103-ab9a-4fff-8250-42a5f8d32e73", - "image": "image386.jpeg" - }, { - "chinese_style": "社会公益", - "english_style": "Social welfare theme", - "id": "6edb9969-682c-4721-aa80-29b6ca583a57", - "image": "image389.jpeg" - }, { - "chinese_style": "医疗健康", - "english_style": "Medical and health theme", - "id": "e36cdc09-976a-43e5-921e-687950020ac6", - "image": "image391.jpeg" - }, { - "chinese_style": "教育培训", - "english_style": "Education and training theme", - "id": "402fbf92-ff82-422b-a12f-3595ff908197", - "image": "image387.jpeg" - }, { - "chinese_style": "金融经济", - "english_style": "Finance and economy theme", - "id": "e75bbc8c-ba12-47a8-9d42-1f2847533afd", - "image": "image388.jpeg" - }, { - "chinese_style": "科学研究", - "english_style": "Scientific research theme", - "id": "9cb82967-bf9e-4a6c-8a22-11e9b5b3d698", - "image": "image385.jpeg" - }, { - "chinese_style": "文化艺术", - "english_style": "Culture and art theme", - "id": "92fcc6e2-88a3-474d-b0ac-ab657e7605b6", - "image": "image392.jpeg" - }, { - "chinese_style": "体育运动", - "english_style": "Sports and fitness theme", - "id": "ecddba8f-d64e-431b-81fe-6a8234da6bca", - "image": "image395.jpeg" - }, { - "chinese_style": "旅游度假", - "english_style": "Travel and vacation theme", - "id": "7b826dfa-670b-4826-9d45-7803d2ee5c6a", - "image": "image400.jpeg" - }, { - "chinese_style": "美食烹饪", - "english_style": "Food and cooking theme", - "id": "2d72e89e-8208-4d19-ad8a-0f8e3718fb42", - "image": "image398.jpeg" - }, { - "chinese_style": "时尚美妆", - "english_style": "Fashion and beauty theme", - "id": "50acc1db-330c-413b-aa99-efcbda18c94d", - "image": "image393.jpeg" - }, { - "chinese_style": "婚礼庆典", - "english_style": "Wedding and celebration theme", - "id": "353a2699-8906-4d15-beac-1a942bf1e26e", - "image": "image394.jpeg" - }, { - "chinese_style": "家居生活", - "english_style": "Home and lifestyle theme", - "id": "1b9ce9cf-2c7f-43a4-a619-171856e8db41", - "image": "image399.jpeg" - }, { - "chinese_style": "母婴育儿", - "english_style": "Maternity and parenting theme", - "id": "df0f3682-82ae-4729-b469-25f0c1784364", - "image": "image396.jpeg" - }, { - "chinese_style": "科技创新", - "english_style": "Technological innovation theme", - "id": "1fa174c2-3557-4fec-bd03-1774561d494a", - "image": "image397.jpeg" - }], - "id": "cfd1b687-0043-4e39-bf8e-b8f4d4667016" - }], - /** - * 获取所有的生图风格 - * @returns {Array} 返回所有的风格数据 - */ - getImageStyle: function () { - return this.image_style; - }, - - /** - * 获取指定的的ID的风格数据,可以多个 - * @param {*} ids ID的数组 - * @returns - */ - getImageStyleObjectByIds: function (ids) { - let result = []; - if (!ids) { - ids = []; - } - let style = this.getAllSubStyle(); - for (let i = 0; i < ids.length; i++) { - const element = ids[i]; - for (let j = 0; j < style.length; j++) { - const item = style[j]; - if (item.id == element) { - result.push(item); - break; - } - } - } - return result; - }, - - /** - * 获取指定ID数组的风格提示词 - * @param {*} ids 需要拼接的id数组 - * @returns 返回拼接好的英文提示词字符串(会加上SD设置那边的权重) - */ - getImageStyleStringByIds: async function (ids) { - let res = ""; - let tmp = []; - let result = this.getImageStyleObjectByIds(ids); - for (let i = 0; i < result.length; i++) { - const element = result[i]; - tmp.push(element.english_style); - } - let weight = await SdSettingDefine.getSettingSettingProperty("style_weight", false); - weight = weight ? weight : 1; - tmp.map((item) => { - if (global.config.image_generate_category == "sd") { - res += `(${item}:${weight}),`; - } else { - res += `${item},`; - } - }); - return res; - }, - - /** - * - * @returns {Array} 返回所有的风格数据 - */ - getAllSubStyle: function () { - let subStyle = []; - this.image_style.forEach((item) => { - subStyle = subStyle.concat(item.subStyle); - }); - return subStyle; - }, - - /** - * 根据图像风格数据,返回选择菜单 - * @returns {Array} 返回所有的风格数据 - */ - getImageStyleMenu: function () { - let menu = []; - this.image_style.forEach((item) => { - menu.push({ - id: item.id, - label: item.name, - key: item.id, - }); - }); - return menu; - }, - - /** - * 获取指定的ID的下面数据的风格 - * @param {*} id 菜单的ID - */ - getImagePathById: function (id) { - try { - let index = this.image_style.findIndex((item) => item.id == id); - if (index < 0) { - throw new Error("没有找到指定的ID"); - } - let subStyle = this.image_style[index].subStyle; - return subStyle; - - } catch (error) { - throw error; - } - } - -} - -export { ImageStyleDefine } \ No newline at end of file diff --git a/src/define/iamgeStyleDefine.ts b/src/define/iamgeStyleDefine.ts new file mode 100644 index 0000000..9736b94 --- /dev/null +++ b/src/define/iamgeStyleDefine.ts @@ -0,0 +1,2754 @@ +import { cloneDeep } from 'lodash' +import path from 'path' +import { define } from './define' +import { SdSettingDefine } from './setting/sdSettingDefine' +import { errorMessage, successMessage } from '../main/Public/generalTools' + +const ImageStyleDefine = { + image_style: [ + { + sheet: 1, + name: '常用画风', + subStyle: [ + { + chinese_style: '壁纸', + english_style: 'Wallpaper', + id: '238f8db1-3b9f-48b8-86de-b47c70dbdc25', + image: 'image8.jpeg' + }, + { + chinese_style: '黑白漫画', + english_style: 'Black and white comics', + id: 'bb314303-4f94-4a9a-b9cc-2f0eca64e288', + image: 'image2.jpeg' + }, + { + chinese_style: '水墨风格', + english_style: 'Ink and wash style', + id: '078dc21d-669c-4a60-8036-2705dfb1d6b6', + image: 'image3.jpeg' + }, + { + chinese_style: '超写实风格', + english_style: 'Super realistic style', + id: '81dd155f-f69d-4ed0-9fdd-6f639e5e6d06', + image: 'image4.jpeg' + }, + { + chinese_style: '电影截图', + english_style: 'Screenshots of movies', + id: '3923dfb8-3394-4e6f-a9f4-55caf24b3991', + image: 'image5.jpeg' + }, + { + chinese_style: '宫崎骏漫画风格', + english_style: 'Miyazaki Hayao manga style', + id: 'b1869409-8079-4934-b6b1-b26cf4606e65', + image: 'image6.jpeg' + }, + { + chinese_style: '可爱风格', + english_style: 'cute style', + id: '79feb583-f4e3-4d35-b7f2-31021693bb90', + image: 'image7.jpeg' + }, + { + chinese_style: '简笔画风格', + english_style: 'Simple stroke style', + id: '0378c4d7-676f-4693-925a-112d2f8fd76b', + image: 'image1.jpeg' + }, + { + chinese_style: '3D卡通动画', + english_style: '3D cartoon animation', + id: 'a5732ec7-3b49-40d8-8bbc-ac213ec77df7', + image: 'image11.jpeg' + }, + { + chinese_style: '90年代日本动漫', + english_style: '90s Japanese anime', + id: 'a9f5eb3b-d4ae-4ba4-aa43-0198f36b6147', + image: 'image9.jpeg' + }, + { + chinese_style: '水彩画风格', + english_style: 'Watercolor painting style', + id: '9d1e4c03-0406-44cc-88f9-21c117a3c88b', + image: 'image10.jpeg' + }, + { + chinese_style: '钢笔画风格', + english_style: 'Pen drawing style', + id: '392067d1-cd6e-4baa-b2d3-f319b892f78c', + image: 'image12.jpeg' + }, + { + chinese_style: '恐怖漫画风格', + english_style: 'Horror comic style', + id: '0e8a0c64-04e8-4370-ad2a-06148afa0662', + image: 'image13.jpeg' + } + ], + id: '07246bd9-b811-4e00-b69b-f86c48f2660b' + }, + { + sheet: 2, + name: '时代风格词', + subStyle: [ + { + chinese_style: '中世纪时代', + english_style: 'middle ages', + id: 'c0f55669-a90e-4024-9c5c-fe4614eabe29', + image: 'image16.jpeg' + }, + { + chinese_style: '冷战时代', + english_style: 'cold war era', + id: 'e2c3b92b-ab4d-44fd-a477-7e308a4e9dfb', + image: 'image19.jpeg' + }, + { + chinese_style: '古典时代', + english_style: 'classical antiquity', + id: 'f5e943bd-0e34-4f5e-baac-1c2e0304c0cf', + image: 'image20.jpeg' + }, + { + chinese_style: '柴油朋克时代', + english_style: 'diesel punk', + id: 'a2ffb049-2c5b-4f3f-9cef-d27e7d8d8ab0', + image: 'image21.jpeg' + }, + { + chinese_style: '哥特式时代', + english_style: 'gothic period', + id: 'db5e8036-7f8c-4047-8c3d-f64f010fc4dd', + image: 'image14.jpeg' + }, + { + chinese_style: '中国战国时代', + english_style: 'warring states period', + id: 'ba0ae70c-37de-40e3-9dc8-08a7e03de123', + image: 'image17.jpeg' + }, + { + chinese_style: '日本明治时代', + english_style: 'meiji period', + id: 'a465ddc1-9782-430b-b09c-7a79effc1c29', + image: 'image15.jpeg' + }, + { + chinese_style: '现代', + english_style: 'modern', + id: 'a12c8315-28c0-42bd-a3ec-a856d48f5161', + image: 'image18.jpeg' + }, + { + chinese_style: '文艺复兴时代', + english_style: 'renaissance', + id: 'e1329854-1df1-4664-9ca2-27c8f905f2d3', + image: 'image25.jpeg' + }, + { + chinese_style: '中国宋代', + english_style: 'song dynasty', + id: 'df196ffe-012a-4fe5-8db3-d89046d5eb39', + image: 'image26.jpeg' + }, + { + chinese_style: '古埃及时代', + english_style: 'ancient egypt', + id: '27c5c5df-4b9f-487c-8884-4c50035f4bc6', + image: 'image23.jpeg' + }, + { + chinese_style: '蒸汽朋克时代', + english_style: 'steampunk', + id: '62018708-10c6-4a1f-8c35-9c162278686f', + image: 'image28.jpeg' + }, + { + chinese_style: '中国明代', + english_style: 'ming dynasty', + id: '20c9f4cf-99ec-4550-a965-768190d91498', + image: 'image29.jpeg' + }, + { + chinese_style: '青铜时代', + english_style: 'bronze age', + id: '6ef8bd4f-c843-435c-9d31-ca2f36add104', + image: 'image22.jpeg' + }, + { + chinese_style: '中国唐代', + english_style: 'tang dynasty', + id: '950518dc-e87b-4be8-8487-8b732b52e175', + image: 'image27.jpeg' + }, + { + chinese_style: '赛博朋克时代', + english_style: 'cyberpunk', + id: '8e012231-4e38-4d6d-b18c-4e03f1ef45f1', + image: 'image24.jpeg' + }, + { + chinese_style: '工业革命时代', + english_style: 'industrial revolution', + id: '8a46b74f-91b5-40d5-bec7-afba88b5abb1', + image: 'image30.jpeg' + }, + { + chinese_style: '日本江户时代', + english_style: 'edo period', + id: '5ab22ed5-b284-4f79-be4f-7c6aef329151', + image: 'image31.jpeg' + } + ], + id: 'ade4cf78-7bef-43c8-9d13-c2ea5c3f9f38' + }, + { + sheet: 3, + name: '画家画风风格词', + subStyle: [ + { + chinese_style: '潘天寿(古风画风-成熟)', + english_style: 'Pan Tianshou', + id: '6d54b67f-08e3-40ac-816f-d4081ad7d9e2', + image: 'image35.jpeg' + }, + { + chinese_style: '朱屺瞻(古风画风-艳丽)', + english_style: 'Zhu Qizhan', + id: '121b3d63-b0b2-4dd4-aaa4-e2c3e8d89d76', + image: 'image36.jpeg' + }, + { + chinese_style: '齐白石(古风画风-简约)', + english_style: 'Qi Baishi', + id: 'bc76a884-afac-450e-88cc-54f65c504a7c', + image: 'image37.jpeg' + }, + { + chinese_style: '张大千(古风画风-可爱)', + english_style: 'Zhang Daqian', + id: 'a85caede-767a-4b8b-b14a-981656415637', + image: 'image33.jpeg' + }, + { + chinese_style: '亨利·马蒂斯(都市画风-时尚)', + english_style: 'Henri Matisse', + id: '9b7e83e6-dc18-41b8-8887-176794a615c3', + image: 'image38.jpeg' + }, + { + chinese_style: '乔治亚·欧姬芙(都市画风-欧美)', + english_style: "Georgia O'Keeffe", + id: 'ca757d22-c5fc-4974-9768-16323957fe85', + image: 'image32.jpeg' + }, + { + chinese_style: '格伦·基恩(迪斯尼-美女与野兽风)', + english_style: 'Glen Keane', + id: '1a5655c9-6800-42ab-aa88-7ab2c77c8c0c', + image: 'image39.jpeg' + }, + { + chinese_style: '根迪·塔尔塔科夫斯基(欧美动画风)', + english_style: 'Genndy Tartakovsky', + id: '60357799-3cba-4809-b50c-db804783599d', + image: 'image34.jpeg' + }, + { + chinese_style: '克雷格·汤普森(都市画风-细腻)', + english_style: 'Craig Thompson', + id: 'a6806093-372a-44f0-8967-d764b1e90709', + image: 'image40.jpeg' + }, + { + chinese_style: '亨利·马蒂斯(都市画风-流行)', + english_style: 'Henri Matisse', + id: 'fac9dd65-8a92-43ab-b026-63231ebf6de2', + image: 'image44.jpeg' + }, + { + chinese_style: '伊藤润二(日式恐怖画风)', + english_style: 'Junji Ito', + id: '0115613d-ee65-4d32-b9a5-eeb57cdc708d', + image: 'image45.jpeg' + }, + { + chinese_style: '朴泰俊(小鲜肉都市风)', + english_style: 'Park Tae', + id: '80d2fb78-2a69-4314-989d-57734dce6789', + image: 'image41.jpeg' + }, + { + chinese_style: '朴智旻(都市恋爱风)', + english_style: 'Park Ji', + id: '6b4c5150-82e0-4259-9617-59434291e4b5', + image: 'image42.jpeg' + }, + { + chinese_style: '金瑞亨(韩风都市恋爱)', + english_style: 'Kim Hyeong', + id: 'f1fee049-109c-4b85-83bf-ed3ed5554f7f', + image: 'image43.jpeg' + }, + { + chinese_style: '查克·琼斯(欧美动画风-猫和老鼠)', + english_style: 'Chuck Jones', + id: '21c0e23f-d98b-41f0-b351-cb1b47f462ee', + image: 'image46.jpeg' + }, + { + chinese_style: '高畑勋(日式动漫风)', + english_style: 'Isao Takahata', + id: '55868e27-cc28-4830-b6c9-46096416b03d', + image: 'image47.jpeg' + }, + { + chinese_style: '淺野恭司(细腻日漫)', + english_style: 'Kyoji Asano', + id: '126ec922-c45d-4a83-b9d7-48f04513cad0', + image: 'image48.jpeg' + }, + { + chinese_style: '卡洛斯·达托利(都市少男风)', + english_style: 'Carlos Dattoli', + id: 'a7aee025-fc08-4e66-82ba-42f38b7d3405', + image: 'image49.jpeg' + }, + { + chinese_style: '库夫希诺夫·伊利亚(都市少女风)', + english_style: 'Kuvshinov Ilya', + id: 'a9679671-a1b9-472b-be78-4fd91adc9e02', + image: 'image50.jpeg' + }, + { + chinese_style: '加里·拉尔森、杰拉德·乌克盖斯特(美式漫画)', + english_style: 'Gary Larson, Gerard Houckgeest', + id: 'c1eb9ca2-4d89-4636-b1ff-7f83d7eba781', + image: 'image54.jpeg' + }, + { + chinese_style: '阮佳(国风插画)', + english_style: 'Ruan Jia', + id: '32afc969-622a-4792-a270-02cf6952d805', + image: 'image51.jpeg' + }, + { + chinese_style: '保罗·罗沃西、塞西莉·布朗(欧美风偏质感)', + english_style: 'Paolo roversi, Cecily brown', + id: 'f9860c1f-8552-4514-8be7-617527d51f0b', + image: 'image52.jpeg' + }, + { + chinese_style: '山田尚子(日式少女风)', + english_style: 'Naoko Yamada', + id: 'ad6ed3d9-a153-487e-9cd5-8447f99ce43b', + image: 'image53.jpeg' + }, + { + chinese_style: '木村贵宏(日式动漫风)', + english_style: 'Takahiro Kimura', + id: '199934ed-5d7e-4af7-979b-0b2dd4862199', + image: 'image55.jpeg' + }, + { + chinese_style: '安德烈亚斯·-罗查(欧美风偏细节色彩)', + english_style: 'Andreas Rocha', + id: 'f6358e01-7276-4f26-96c8-56a619ae3126', + image: 'image70.jpeg' + }, + { + chinese_style: '凉香(日式古风)', + english_style: 'Ryohka', + id: '824b9390-9787-44de-ab43-da5d647101ad', + image: 'image71.jpeg' + }, + { + chinese_style: '丽贝卡·盖伊(西方魔幻)', + english_style: 'Rebecca Guay', + id: '5d2234ce-d0d8-4330-93d5-21a81d6486d5', + image: 'image64.jpeg' + }, + { + chinese_style: '克雷格·穆林斯(西方末日科幻)', + english_style: 'Craig Mullins', + id: 'a66f32dd-2b73-4d6c-8d83-cc1a37dae7ea', + image: 'image65.jpeg' + }, + { + chinese_style: '小林修(日式画风)', + english_style: 'Osamu Kobayashi', + id: 'a9e9c6ab-02d1-406b-b730-9d4ece86d29c', + image: 'image66.jpeg' + }, + { + chinese_style: '池上辽一(日式画风)', + english_style: 'Ryoichi Ikegami', + id: 'aea0beec-32ea-42c7-9891-09616a01a424', + image: 'image69.jpeg' + }, + { + chinese_style: '岸本齐史(火影忍着风)', + english_style: 'Masashi Kishimoto', + id: '2ef699da-781d-4f05-9239-6d027d34c42c', + image: 'image67.jpeg' + }, + { + chinese_style: '沃尔特·迪斯尼(迪斯尼风)', + english_style: 'Walt Disney', + id: '6befee18-2232-4a3e-b1a5-d206d5e29138', + image: 'image68.jpeg' + }, + { + chinese_style: '久保带人(死神画风)', + english_style: 'Tite Kubo', + id: 'c26be074-bc44-41bc-b36c-97845c8fb544', + image: 'image62.jpeg' + }, + { + chinese_style: '谏山创(进击的巨人画风)', + english_style: 'Hajime Isayama', + id: '5ceeedb9-4977-49f4-9122-220165784683', + image: 'image63.jpeg' + }, + { + chinese_style: '新海诚', + english_style: 'Makoto Shinkai', + id: '9cc4611f-4ebe-449f-ae9c-3502a37a857e', + image: 'image60.jpeg' + }, + { + chinese_style: '武内直子(美少女战士画风)', + english_style: 'Naoko Takeuchi', + id: '9176dac9-deaa-4e19-8d0f-e586437d251f', + image: 'image56.jpeg' + }, + { + chinese_style: '濑户目鹤(日漫风)', + english_style: 'Koyori', + id: '321322f7-0f12-47d5-9d53-1945b8ef4516', + image: 'image57.jpeg' + }, + { + chinese_style: '细田守(日本动漫风)', + english_style: 'Mamoru Hosoda', + id: '9e2db099-5996-4e8a-8e86-37f8fc81fa1a', + image: 'image58.jpeg' + }, + { + chinese_style: '约翰·拉塞特(欧美动画风)', + english_style: 'John Lasseter', + id: 'd7f5278d-d242-4834-ab82-2adfc11231e8', + image: 'image61.jpeg' + }, + { + chinese_style: '蒂姆·伯顿(魔发奇缘风-暗黑魔幻)', + english_style: 'Tim Burton', + id: 'c104bba3-6611-4178-93d8-2f988fb83fc7', + image: 'image59.jpeg' + }, + { + chinese_style: '鸟山明(龙珠画风)', + english_style: 'Akira Toriyama', + id: 'e51d4ad6-816d-4534-89e0-3fdaf7a3916c', + image: 'image72.jpeg' + }, + { + chinese_style: '高桥留美子(犬夜叉画风)', + english_style: 'Rumiko Takahashi', + id: '589a6e3d-4707-4eba-8650-e479fe56aa49', + image: 'image73.jpeg' + }, + { + chinese_style: '庵野秀明(EVA画风)', + english_style: 'Hideaki Anno', + id: '1ba9073e-b530-4520-893e-4f7eb2c8cba6', + image: 'image76.jpeg' + }, + { + chinese_style: '宫崎骏(日式动漫风)', + english_style: 'Hayao Miyazaki', + id: '92b59244-5b26-47e2-a542-050b28beb631', + image: 'image75.jpeg' + }, + { + chinese_style: '米林宏昌(日式古风)', + english_style: 'hiromasa yonebayashi', + id: '5f3bfcdd-d6f9-40c8-8ebb-b423ee768d59', + image: 'image74.jpeg' + }, + { + chinese_style: '男鹿和雄(日漫古风)', + english_style: 'kazuo oga', + id: 'b389d682-0ac2-434c-849a-9ceca0468397', + image: 'image77.jpeg' + } + ], + id: '661f3673-b448-49c5-95eb-92416c669f21' + }, + { + sheet: 4, + name: '中华古风', + subStyle: [ + { + chinese_style: '张光宇风格', + english_style: 'Zhang Guangyu style', + id: 'f67e1f1f-3824-4190-aa7b-22a4ceefc803', + image: 'image85.jpeg' + }, + { + chinese_style: '丰子恺风格', + english_style: 'Feng Zikai style', + id: '0c6e2462-82a2-492d-8f00-e0571099230a', + image: 'image83.jpeg' + }, + { + chinese_style: '蔡志忠风格', + english_style: 'Cai Zhong style', + id: 'ddc1adda-5bc5-4779-9f70-942f5c8bc739', + image: 'image82.jpeg' + }, + { + chinese_style: '马荣成风格', + english_style: 'Ma Rongcheng style', + id: 'c6ecceb3-4000-4846-ad54-160f7f92ac68', + image: 'image78.jpeg' + }, + { + chinese_style: '陈某风格', + english_style: 'Chen Someone style', + id: '85cd0650-a62a-4123-a7ac-ae7ecfe549c7', + image: 'image84.jpeg' + }, + { + chinese_style: '夏达风格', + english_style: 'Xia Da style', + id: '3317e89f-c828-4a60-bab7-9935dd2f841c', + image: 'image79.jpeg' + }, + { + chinese_style: '猫君风格', + english_style: 'Cat Jun style', + id: '5db7dfa4-306b-4c0f-a8ca-314037c32839', + image: 'image80.jpeg' + }, + { + chinese_style: 'Left Hand Han style', + english_style: '左手韩风格', + id: 'eed2d205-3818-4b3d-9d2a-98a5140cd97e', + image: 'image81.jpeg' + }, + { + chinese_style: '阿梗风格', + english_style: 'A Geng style', + id: 'ad75eb08-6e12-4707-affb-7292471b82c7', + image: 'image89.jpeg' + }, + { + chinese_style: '本杰明风格', + english_style: 'Benjamin style', + id: '00638bad-7919-4bbe-844d-89bf78787df8', + image: 'image88.jpeg' + }, + { + chinese_style: '寂地风格', + english_style: 'Ji Di style', + id: 'f041d7d9-485f-4ef8-a3ee-a63a480d9980', + image: 'image86.jpeg' + }, + { + chinese_style: '鹿菏风格', + english_style: 'Lu He style', + id: '33aa10c2-1d70-46b4-b847-bed5eb9f5f41', + image: 'image87.jpeg' + } + ], + id: 'fc8d672d-8482-445b-8652-a7d8910c621c' + }, + { + sheet: 5, + name: '中国都市漫画作者风格', + subStyle: [ + { + chinese_style: '姚非拉风格', + english_style: 'Yao Feila style', + id: 'ad83c246-6c22-42ac-9049-e6e1e3f82022', + image: 'image93.jpeg' + }, + { + chinese_style: '慕容引刀风格', + english_style: 'Murong Yindao style', + id: '845ec4a6-a1bf-42b3-b2f5-503bae91e24b', + image: 'image90.jpeg' + }, + { + chinese_style: '使徒子风格', + english_style: 'Apostle style', + id: '59c26e38-33f1-44ad-a471-d747babd0cee', + image: 'image94.jpeg' + }, + { + chinese_style: '幽·灵风格', + english_style: 'Youling style', + id: '0f6e1954-2b2b-418c-b36b-bb833a49d131', + image: 'image91.jpeg' + }, + { + chinese_style: '猫树风格', + english_style: 'Cat Tree style', + id: 'afed5230-a76a-4709-be26-30040f2a9418', + image: 'image95.jpeg' + }, + { + chinese_style: '青鸟动漫风格', + english_style: 'Blue Bird Animation style', + id: '06900981-ac49-4b03-b327-512f4b36bcbf', + image: 'image92.jpeg' + }, + { + chinese_style: '阿尤风格', + english_style: 'A You style', + id: '82887dc9-eb51-4168-91e5-73467c1d9bf5', + image: 'image96.jpeg' + }, + { + chinese_style: '张小溪风格', + english_style: 'Zhang Xiaoxi style', + id: '1d2901e9-c79c-42b7-843e-0a32be80f420', + image: 'image97.jpeg' + }, + { + chinese_style: '阿衰风格', + english_style: 'A Shuai style', + id: '83e99b4d-0702-4cf8-a93e-af2a07f10858', + image: 'image99.jpeg' + }, + { + chinese_style: '丁墨风格', + english_style: 'Ding Mo style', + id: '193a8cb3-d9a7-4f42-9c2f-7c538d45e795', + image: 'image100.jpeg' + }, + { + chinese_style: '老夫子风格', + english_style: 'Old Master style', + id: '0eab7213-51fe-42cb-9330-c95b0d991d5b', + image: 'image98.jpeg' + } + ], + id: 'ac972ea7-e2db-48be-9b66-1d914b059b77' + }, + { + sheet: 6, + name: '都市漫画', + subStyle: [ + { + chinese_style: '现代都市生活', + english_style: 'Modern city life', + id: '10d307a4-50ad-4919-afaf-754a9570a7ad', + image: 'image105.jpeg' + }, + { + chinese_style: '职场奋斗', + english_style: 'Workplace struggles', + id: '79b593b1-fe8c-4a94-a6d0-f2d160c3d005', + image: 'image101.jpeg' + }, + { + chinese_style: '青春校园', + english_style: 'Youthful school life', + id: 'c1d691e9-1211-492f-bebb-71e3106ff35b', + image: 'image102.jpeg' + }, + { + chinese_style: '浪漫爱情', + english_style: 'Romantic love story', + id: '1b711f31-2e3b-423f-b27f-dba57b760e33', + image: 'image103.jpeg' + }, + { + chinese_style: '悬疑侦探', + english_style: 'Mystery and detective', + id: '4352c2b1-5cf0-4654-995d-2658f668f6fb', + image: 'image106.jpeg' + }, + { + chinese_style: '超能力者', + english_style: 'Individuals with superpowers', + id: 'acb3a28e-2957-442e-ad47-6550d5af1a94', + image: 'image104.jpeg' + }, + { + chinese_style: '青春成长', + english_style: 'Coming of age', + id: '31779554-fbea-447f-a9f7-57cdf5aefffd', + image: 'image107.jpeg' + }, + { + chinese_style: '日常生活', + english_style: 'Slice of life', + id: '4688a2b4-d1e7-4bff-9d9e-e7c740bd0269', + image: 'image108.jpeg' + }, + { + chinese_style: '都市异闻', + english_style: 'Urban legends and folklore', + id: '0bbec8ab-4e11-430c-a857-1b1693d1ca31', + image: 'image112.jpeg' + }, + { + chinese_style: '时尚潮流', + english_style: 'Fashion and trends', + id: '59610aa3-1cb0-498e-a701-9ed0456085a6', + image: 'image110.jpeg' + }, + { + chinese_style: '社交网络', + english_style: 'Social media and relationships', + id: '74549157-8829-45b4-a608-b9752dfcb84f', + image: 'image111.jpeg' + }, + { + chinese_style: '都市奇幻', + english_style: 'Urban fantasy', + id: '235b4431-f310-4dd0-a6dc-f42cbb7ddf29', + image: 'image109.jpeg' + } + ], + id: 'c9d2846d-c8c0-40b0-ad9e-ee882aa6efcf' + }, + { + sheet: 7, + name: '悬疑故事画风', + subStyle: [ + { + chinese_style: '悬疑恐怖故事', + english_style: 'Suspenseful horror stories', + id: '97dc5f98-c241-43c8-a918-ab9788b5e982', + image: 'image119.jpeg' + }, + { + chinese_style: '古堡探险', + english_style: 'Castle exploration', + id: 'e8370048-ee70-4664-8b91-a50fee0f93f5', + image: 'image117.jpeg' + }, + { + chinese_style: '失踪之谜', + english_style: 'Mystery of disappearance', + id: '6145e00c-f5ce-4f4d-97ff-91939c26f92e', + image: 'image115.jpeg' + }, + { + chinese_style: '诡异事件调查', + english_style: 'Investigation of eerie events', + id: 'cd6b74f2-6599-4bd9-9282-84fabfa0e558', + image: 'image113.jpeg' + }, + { + chinese_style: '禁忌解密', + english_style: 'Forbidden decryption', + id: 'b4673555-6dd6-4a7c-95ac-f728ab587fd7', + image: 'image118.jpeg' + }, + { + chinese_style: '幽灵传说', + english_style: 'Ghost legends', + id: '5d4ace6c-476a-4ae8-a621-ece0dd59c831', + image: 'image120.jpeg' + }, + { + chinese_style: '恐怖短片', + english_style: 'Horror short films', + id: '9191e13c-2add-41e9-a68f-cee5828c6b55', + image: 'image116.jpeg' + }, + { + chinese_style: '神秘案件破解', + english_style: 'Solving mysterious cases', + id: 'b132999e-3bf5-4828-a963-8f83892f8c40', + image: 'image114.jpeg' + }, + { + chinese_style: '阴谋追踪', + english_style: 'Conspiracy tracking', + id: '79b40a23-05f4-4915-aa6a-be916e471699', + image: 'image121.jpeg' + } + ], + id: '7e83b352-ae2f-4617-91a4-ad10ebc7dd13' + }, + { + sheet: 8, + name: '3D动画', + subStyle: [ + { + chinese_style: '皮克斯电影', + english_style: 'Pixar Movie Style', + id: '2e65894e-6d8b-475e-a93b-f93929860eb3', + image: 'image129.jpeg' + }, + { + chinese_style: '梦工厂电影风格', + english_style: 'Dream Factory Movie Style', + id: 'c4df479b-49fc-49b9-a24c-f901056774e2', + image: 'image130.jpeg' + }, + { + chinese_style: '迪士尼', + english_style: 'Disney', + id: '9de96904-a2e6-4506-9a5d-be5bd79c9df4', + image: 'image126.jpeg' + }, + { + chinese_style: '哥伦比亚电影公司', + english_style: 'Columbia Pictures', + id: '0836e43b-0cc6-49f6-894e-e5ed4205c192', + image: 'image131.jpeg' + }, + { + chinese_style: '索尼动画', + english_style: 'Sony Pictures Animation', + id: '1773b5a6-6109-4bbe-8213-185559689d81', + image: 'image127.jpeg' + }, + { + chinese_style: '蓝天工作室', + english_style: 'Blue Sky Studios', + id: 'ed7dfdd1-6775-4ecb-9110-eb0ac26cd122', + image: 'image132.jpeg' + }, + { + chinese_style: '卡通网络动画', + english_style: 'Cartoon Network Studios', + id: '37dd259a-1f42-403f-ad36-66e497bdf6e4', + image: 'image128.jpeg' + }, + { + chinese_style: '漫威动画', + english_style: 'Marvel Animation', + id: 'f4317d2a-e827-4da1-a158-26e33812ea89', + image: 'image133.jpeg' + }, + { + chinese_style: '华纳兄弟动画', + english_style: 'Warner Bros. Animation', + id: '7f6f85c5-18a7-451a-ae5e-f911331a192a', + image: 'image141.jpeg' + }, + { + chinese_style: '环球影业动画', + english_style: 'Universal Pictures Animation', + id: '90944358-bc96-4bc9-ad59-373365015725', + image: 'image134.jpeg' + }, + { + chinese_style: '20世纪福克斯动画', + english_style: '20th Century Fox Animation', + id: 'e3c44ec7-cda7-415e-83fc-fde7b1cf948d', + image: 'image140.jpeg' + }, + { + chinese_style: '派拉蒙动画', + english_style: 'Paramount Animation', + id: 'b4353dab-6bd4-419b-9ccd-5f0c9352690e', + image: 'image135.jpeg' + }, + { + chinese_style: '米高梅动画', + english_style: 'Metro-Goldwyn-Mayer Animation', + id: '8efe38ae-0b66-4441-b394-c63fc144551e', + image: 'image137.jpeg' + }, + { + chinese_style: '乐高影业', + english_style: 'LEGO Animation', + id: '6caaddde-97fe-405a-8655-61e12d0434c1', + image: 'image138.jpeg' + }, + { + chinese_style: '迪士尼电视动画', + english_style: 'Disney Television Animation', + id: 'f0eb3c60-94ec-44de-8099-7c6018c454a7', + image: 'image139.jpeg' + }, + { + chinese_style: '尼克儿童动画', + english_style: 'Nickelodeon Animation Studio', + id: '6174cf18-7114-43f0-b139-1db5fd2ad9bb', + image: 'image136.jpeg' + }, + { + chinese_style: '奈飞动画', + english_style: 'Netflix Animation', + id: '0df4834b-8867-42b3-b680-80c169ba904e', + image: 'image123.jpeg' + }, + { + chinese_style: '华纳动画组', + english_style: 'Warner Animation Group', + id: '80406a8b-9a23-4d35-b25e-a8984eb5581c', + image: 'image124.jpeg' + }, + { + chinese_style: '索尼娱乐动画', + english_style: 'Sony Entertainment Animation', + id: '95f8555f-8615-4484-953b-27f14890b219', + image: 'image122.jpeg' + }, + { + chinese_style: '蓝宝儿动画', + english_style: 'Lionsgate Animation', + id: '5c4a4871-753a-4825-bc45-93d66e97037c', + image: 'image125.jpeg' + } + ], + id: '507a1001-efab-4f23-ba0a-e1b6fc65732a' + }, + { + sheet: 9, + name: '概念艺术', + subStyle: [ + { + chinese_style: '幻想题材', + english_style: 'Fantasy theme', + id: 'a0586d4b-5add-436f-aa74-74559f66a000', + image: 'image146.jpeg' + }, + { + chinese_style: '科幻题材', + english_style: 'Science fiction theme', + id: '939ef719-0785-45ec-8a26-a1e56a740790', + image: 'image142.jpeg' + }, + { + chinese_style: '赛博朋克', + english_style: 'Cyberpunk style', + id: 'f488da0b-c5a4-465e-815e-8f8034ae4394', + image: 'image147.jpeg' + }, + { + chinese_style: '蒸汽朋克', + english_style: 'Steampunk style', + id: '61d188e6-7f5f-469e-bd30-50f810b1285a', + image: 'image143.jpeg' + }, + { + chinese_style: '超自然现象', + english_style: 'Supernatural phenomena', + id: '381ec4b4-2bf7-4043-beda-aa0fd65520cd', + image: 'image148.jpeg' + }, + { + chinese_style: '神话传说', + english_style: 'Mythology and legends', + id: 'd554974b-d1c5-47d3-8acc-e71911325360', + image: 'image149.jpeg' + }, + { + chinese_style: '魔法与咒语', + english_style: 'Magic and spells', + id: '0b505a81-36fd-454c-8ccc-285be256896b', + image: 'image144.jpeg' + }, + { + chinese_style: '幻想生物', + english_style: 'Fantasy creatures', + id: 'a0bcefce-c94d-47b3-bbd5-4d7b6b9cdd01', + image: 'image145.jpeg' + }, + { + chinese_style: '历史重现', + english_style: 'Historical reenactment', + id: '169249c5-dbf3-4ce8-be7f-47237c8535b2', + image: 'image150.jpeg' + }, + { + chinese_style: '未来都市', + english_style: 'Futuristic cityscape', + id: '2d0eec27-4bab-42f1-9d8b-7909df82c709', + image: 'image152.jpeg' + }, + { + chinese_style: '末日废土', + english_style: 'Post-apocalyptic wasteland', + id: 'ffc43253-b1a4-4f4a-a5a6-d8b9e62c3208', + image: 'image153.jpeg' + }, + { + chinese_style: '异星世界', + english_style: 'Alien world', + id: '8b7b821a-7f91-437a-8af9-4841f8a24517', + image: 'image151.jpeg' + } + ], + id: '6459007e-cb61-4933-b42e-e2ea19ce1f03' + }, + { + sheet: 10, + name: '新日本动漫风', + subStyle: [ + { + chinese_style: '《刀剑神域》', + english_style: 'Sword Art Online style, fantasy and adventure, Kawahara Reki', + id: 'd2048c30-56ad-44be-bdf7-6e04a690c310', + image: 'image163.jpeg' + }, + { + chinese_style: '《东京食尸鬼》', + english_style: 'Sui Ishida style', + id: 'e22b50f6-f0c4-4cf7-97b2-084990537cf0', + image: 'image160.jpeg' + }, + { + chinese_style: '《命运之夜》', + english_style: 'Type-Moon style', + id: 'b97c9b77-c423-41b0-8c80-e19c4fe504ba', + image: 'image161.jpeg' + }, + { + chinese_style: '《狼与香辛料》', + english_style: 'Isuna Hasekura style', + id: '670a7131-915f-4959-a020-3dd1e3ae1f69', + image: 'image158.jpeg' + }, + { + chinese_style: '《鬼灭之刃》', + english_style: 'Koyoharu Gotouge style', + id: '907db0cb-24ed-457e-a0c0-c4067184b608', + image: 'image159.jpeg' + }, + { + chinese_style: '《名侦探柯南》', + english_style: 'Gosho Aoyama style', + id: '3cff985d-2b9c-4d52-b373-adc4a415d487', + image: 'image162.jpeg' + }, + { + chinese_style: '《七原罪》', + english_style: 'Nakaba Suzuki style', + id: '8d8ca7fc-7d78-4240-a300-274dbbf299cb', + image: 'image164.jpeg' + }, + { + chinese_style: '《妖怪手表》', + english_style: 'Level-5 style', + id: '0569520e-cc54-4fb8-8f7c-ac7102c557e7', + image: 'image157.jpeg' + }, + { + chinese_style: '《灼眼的夏娜》', + english_style: 'Yashichiro Takahashi style', + id: '899f56d0-67be-4546-a373-04e6648299ef', + image: 'image165.jpeg' + }, + { + chinese_style: '《命运石之门》', + english_style: 'Chiyomaru Shikura and Nitroplus style', + id: 'b0c30ddf-a77b-48f8-bdfe-d29f94ac7902', + image: 'image166.jpeg' + }, + { + chinese_style: '《化物语》', + english_style: 'Nisio Isin and VOFAN style', + id: '1d50bf91-22b7-4efa-a5b4-1cce44cb82be', + image: 'image167.jpeg' + }, + { + chinese_style: '《未来日记》', + english_style: 'Sakae Esunostyle style', + id: 'b5aadac7-df6e-44fd-b953-9e83f8bef4b8', + image: 'image169.jpeg' + }, + { + chinese_style: '《Angel Beats!》', + english_style: 'Jun Maeda and Na-Ga style', + id: '85202d85-4cfc-4439-83d5-4504836905f2', + image: 'image168.jpeg' + }, + { + chinese_style: '《Fate/Zero》', + english_style: 'Gen Urobuchi and Takashi Takeuchi style', + id: 'd5e1f5ef-bb4a-4e40-a51b-bd2045d609b7', + image: 'image172.jpeg' + }, + { + chinese_style: '《Re:从零开始的异世界生活》', + english_style: 'Tappei Nagatsuki and Shinichirou Otsuka style', + id: 'f821ed49-c12f-4f87-b81f-58424610ccd1', + image: 'image170.jpeg' + }, + { + chinese_style: '《OVERLORD》', + english_style: 'Kugane Maruyama and so-bin style', + id: '1977e489-6693-49a5-ade6-72769a4fde00', + image: 'image171.jpeg' + }, + { + chinese_style: '《约会大作战》', + english_style: 'Koushi Tachibana and Tsunako style', + id: 'e7ca9f27-6937-4100-96ca-706d7efa49ec', + image: 'image155.jpeg' + }, + { + chinese_style: '《Re:CREATORS》', + english_style: 'Rei Hiroe style', + id: '6acc66f5-79f9-40b3-abb0-6435da43c73b', + image: 'image156.jpeg' + }, + { + chinese_style: '《关于我转生变成史莱姆这档事》', + english_style: 'Rimuru Tempest style', + id: '1ac9cc4a-f1bb-40ae-a76a-6fd593c7bb04', + image: 'image154.jpeg' + } + ], + id: '14c7f9d5-32b8-409b-ad4c-f7217fa3defd' + }, + { + sheet: 11, + name: '少年日本动漫风', + subStyle: [ + { + chinese_style: '《Another》', + english_style: 'Yukito Ayatsuji and Hiro Kiyohara style', + id: 'd2329e3e-e8e5-4a95-82d7-09fda8a3f3cb', + image: 'image176.jpeg' + }, + { + chinese_style: '《尸体派对》', + english_style: 'Team GrisGris style', + id: '436251be-eb1a-4825-9565-a2f4d7201632', + image: 'image177.jpeg' + }, + { + chinese_style: '《妖精森林的小不点》', + english_style: 'Takuto Kashiki style', + id: '85196b76-ffd9-443d-9827-d6b1b5dd9752', + image: 'image178.jpeg' + }, + { + chinese_style: '《怪谈新耳袋》', + english_style: 'Kyouko Hikawa style', + id: '447a9b2e-4f3c-47d5-acd6-a9e893d35614', + image: 'image174.jpeg' + }, + { + chinese_style: '《地缚少年花子君》', + english_style: 'Kouji Seo style', + id: '0021c2d2-8f70-4536-9d88-07f287cb419f', + image: 'image179.jpeg' + }, + { + chinese_style: '《彼岸岛》', + english_style: 'Koji Matsumoto style', + id: '9d884c85-c7c1-447c-96a6-a05416b5d1e7', + image: 'image180.jpeg' + }, + { + chinese_style: '《龙珠》', + english_style: 'Akira Toriyama style', + id: 'c8c3784a-aad1-4fbd-aa0a-ca18af2efa31', + image: 'image175.jpeg' + }, + { + chinese_style: '《海贼王》', + english_style: 'Eiichiro Oda style', + id: '1ad0e6e6-4ab0-4669-a932-7ecaeef1090d', + image: 'image173.jpeg' + }, + { + chinese_style: '《火影忍者》', + english_style: 'Masashi Kishimoto style', + id: '5a198a66-9e62-48c4-aaff-8be6586249be', + image: 'image198.jpeg' + }, + { + chinese_style: '《死神》', + english_style: 'Tite Kubo style', + id: 'cc514c8c-2194-403e-ae57-9733a455f3f2', + image: 'image192.jpeg' + }, + { + chinese_style: '《进击的巨人》', + english_style: 'Hajime Isayama style', + id: '8c356863-e16d-4a4c-a154-1e4d3836aed9', + image: 'image199.jpeg' + }, + { + chinese_style: '《银魂》', + english_style: 'Hideaki Sorachi style', + id: '4b98130b-ca33-468a-828e-36461e9f4da4', + image: 'image193.jpeg' + }, + { + chinese_style: '《全职猎人》', + english_style: 'Yoshihiro Togashi style', + id: '22f84eff-c3d9-4c39-87df-8ab5b8908a78', + image: 'image196.jpeg' + }, + { + chinese_style: '《柯南》', + english_style: 'Gosho Aoyama style', + id: '00abd3b8-d038-4cb0-bc8a-cf24b1c31f18', + image: 'image197.jpeg' + }, + { + chinese_style: '《妖精的尾巴》', + english_style: 'Hiro Mashima style', + id: '41fe2942-2a96-4b84-ae9c-93b17d4b71c2', + image: 'image194.jpeg' + }, + { + chinese_style: '《黑子的篮球》', + english_style: 'Tadatoshi Fujimaki style', + id: '96eb6f9d-9aca-40de-add5-6d27f0768660', + image: 'image195.jpeg' + }, + { + chinese_style: '《我的英雄学院》', + english_style: 'Kohei Horikoshi style', + id: '3aafb780-ac7f-4e83-b1ae-2145026c24a8', + image: 'image201.jpeg' + }, + { + chinese_style: '《灌篮高手》', + english_style: 'Takehiko Inoue style', + id: '97cf6531-e726-4e3c-b97f-64e6f0528634', + image: 'image200.jpeg' + }, + { + chinese_style: '《排球少年》', + english_style: 'Haruichi Furudate style', + id: 'c6f8d9e7-3d06-42a1-b255-989a2c711d90', + image: 'image204.jpeg' + }, + { + chinese_style: '《钻石王牌》', + english_style: 'Yuji Terajima style', + id: '59f88205-0bce-4deb-b09c-f2db188e52ff', + image: 'image205.jpeg' + }, + { + chinese_style: '《浪客剑心》', + english_style: 'Nobuhiro Watsuki style', + id: '73af468f-0b11-4d2e-9f45-142d4248ffe9', + image: 'image206.jpeg' + }, + { + chinese_style: '《幽游白书》', + english_style: 'Yoshihiro Togashi style', + id: '816e1234-4126-422b-83a9-af25303c721d', + image: 'image202.jpeg' + }, + { + chinese_style: '《城市猎人》', + english_style: 'Tsukasa Hojo style', + id: '33900424-0e31-44af-b147-fdd916b1405d', + image: 'image203.jpeg' + }, + { + chinese_style: '《圣斗士星矢》', + english_style: 'Masami Kurumada style', + id: 'd780c5bc-4e54-43d9-8291-ed9fbc39bd5a', + image: 'image207.jpeg' + }, + { + chinese_style: '《铁臂阿童木》', + english_style: 'Osamu Tezuka style', + id: 'edb4c3fa-e4af-40f6-b97f-ab7326088b80', + image: 'image181.jpeg' + }, + { + chinese_style: '《东京爱情故事》', + english_style: 'Yuki Fujimoto style', + id: 'db55ec39-a9bf-47aa-813f-ab3be24254d6', + image: 'image185.jpeg' + }, + { + chinese_style: '《深夜食堂》', + english_style: 'Yaro Abe style', + id: '95d69b5e-19ec-47ce-92cd-7853cf3aa023', + image: 'image186.jpeg' + }, + { + chinese_style: '《恋爱暴君》', + english_style: 'Megane Mihoshi style', + id: '1cf0fa67-dc9e-41d8-84ca-184b4e9e54ba', + image: 'image188.jpeg' + }, + { + chinese_style: '《请回答1988》', + english_style: 'Ilkwon Ha style', + id: 'fcbeee97-34cd-4dcb-9353-547e1d15b3ca', + image: 'image182.jpeg' + }, + { + chinese_style: '《NANA》', + english_style: 'Ai Yazawa style', + id: '5c275a98-11bc-4cfe-95ee-f29829f2db45', + image: 'image183.jpeg' + }, + { + chinese_style: '《蜂蜜与四叶草》', + english_style: 'Chica Umino style', + id: '0be84542-7362-45cc-b2e0-8203fbf9a628', + image: 'image184.jpeg' + }, + { + chinese_style: '《东京塔》', + english_style: 'Lily Franky style', + id: 'b70b64b2-ff97-4fcd-b5f7-aa0b8727ce6b', + image: 'image187.jpeg' + }, + { + chinese_style: '《工作狂》', + english_style: 'Anno Moyoco style', + id: 'c2b9a3ce-c33f-4c16-8d68-db3c36da18b5', + image: 'image213.jpeg' + }, + { + chinese_style: '《单身男女》', + english_style: 'Rakuda Torino style', + id: 'ac5fb0d0-e607-4c7f-ad78-b3759ec3c994', + image: 'image208.jpeg' + }, + { + chinese_style: '《恋爱情结》', + english_style: 'Aya Nakahara style', + id: 'bba7a88f-cd93-49f0-8cc7-977b3fd5422c', + image: 'image214.jpeg' + }, + { + chinese_style: '《白领羽田》', + english_style: 'Akiko Higashimura style', + id: 'd3261949-efa2-4066-8d39-e2061120bb71', + image: 'image215.jpeg' + }, + { + chinese_style: '《东京女子图鉴》', + english_style: 'Akiko Higashimura style', + id: '0855c327-3127-4377-9bd5-1d12ff9ae684', + image: 'image211.jpeg' + }, + { + chinese_style: '《重版出来!》', + english_style: 'Eiji Nonaka style', + id: '22f75192-b891-4562-8662-53650f52bf4f', + image: 'image210.jpeg' + }, + { + chinese_style: '《逃避虽可耻但有用》', + english_style: 'Tsunami Umino style', + id: '9bf58d40-ab11-49b8-8700-8db9067e7369', + image: 'image209.jpeg' + }, + { + chinese_style: '《下辈子再好好过》', + english_style: 'Kazuto Tatsukawa style', + id: '208444d2-c17c-40f8-93e2-2365676fb900', + image: 'image212.jpeg' + }, + { + chinese_style: '《无法成为野兽的我们》', + english_style: 'Ryoko Fukuyama style', + id: '39109010-4ce4-4b72-a200-883b8feb62cf', + image: 'image191.jpeg' + }, + { + chinese_style: '《我不是结不了婚,只是不想》', + english_style: 'Ako Shimaki style', + id: '1be7fe21-d5e2-4a90-b5aa-8df219b55810', + image: 'image190.jpeg' + }, + { + chinese_style: '《恋爱小人们》', + english_style: 'Musawo style', + id: '3455d950-4d11-426d-891a-b0453606df34', + image: 'image189.jpeg' + } + ], + id: '6ab67e20-2fbe-449b-b371-5f411c4c3892' + }, + { + sheet: 12, + name: '恐怖日本动漫风', + subStyle: [ + { + chinese_style: '《富江》', + english_style: 'Junji Ito style', + id: '3bc00c50-04ff-41c1-8104-c31680b469d8', + image: 'image218.jpeg' + }, + { + chinese_style: '《寄生兽》', + english_style: 'Hitoshi Iwaaki style', + id: '64d9204c-296d-4b09-9b50-36d89268d7fa', + image: 'image220.jpeg' + }, + { + chinese_style: '《漩涡》', + english_style: 'Kyo Shirodaira and Eita Mizuno style', + id: '0522f21f-9094-4a95-9648-5a9f574d406b', + image: 'image222.jpeg' + }, + { + chinese_style: '《死亡笔记》', + english_style: 'Tsugumi Ohba and Takeshi Obata style', + id: 'bde5b7bc-37ad-4a59-829d-21f26c222b29', + image: 'image219.jpeg' + }, + { + chinese_style: '《怪医黑杰克》', + english_style: 'Osamu Tezuka style', + id: '78cc9352-b68a-48d3-a32e-ebf13de78b3c', + image: 'image221.jpeg' + }, + { + chinese_style: '《学园默示录》', + english_style: 'Daisuke Sato and Shouji Sato style', + id: '5e34a2d4-4247-4feb-a369-eaf781668171', + image: 'image217.jpeg' + }, + { + chinese_style: '《鬼灯的冷彻》', + english_style: 'Natsumi Eguchi style', + id: '854eb4d2-fdf1-40cf-a668-2da4dd0c103a', + image: 'image216.jpeg' + }, + { + chinese_style: '《僵尸借贷》', + english_style: 'Peach-Pit style', + id: 'a98ad1ff-0e25-4ac6-872e-1be6dacfb99e', + image: 'image223.jpeg' + }, + { + chinese_style: '《东京喰种》', + english_style: 'Sui Ishida style', + id: '2d3b1c15-4ff6-4843-9dec-4b5f16b3d1c6', + image: 'image224.jpeg' + } + ], + id: '990502bb-b673-4138-9af6-b2e483acdbc0' + }, + { + sheet: 13, + name: '机甲日本动漫风', + subStyle: [ + { + chinese_style: '《机动战士高达》', + english_style: 'Yoshiyuki Tomino style', + id: 'd7f9add9-5dbe-4b9a-ac5a-3905be18c73d', + image: 'image231.jpeg' + }, + { + chinese_style: '《新世纪福音战士》', + english_style: 'Hideaki Anno and Yoshiyuki Sadamoto style', + id: '138a6ff3-6562-4410-9d2e-aa849f512f98', + image: 'image237.jpeg' + }, + { + chinese_style: '《钢弹0080:口袋中的战争》', + english_style: 'Yoshiyuki Tomino and Takashi Imanishi style', + id: '350a64e1-8e2b-4f5b-b8d9-a0af5a6454ec', + image: 'image232.jpeg' + }, + { + chinese_style: '《全金属狂潮》', + english_style: 'Shoji Kawamori style', + id: 'b9f034b8-4146-429a-83a6-dc76bacee682', + image: 'image230.jpeg' + }, + { + chinese_style: '《机动警察》', + english_style: 'Kunio Okawara style', + id: '55cd3098-7c01-406b-ae0e-1964b28d0925', + image: 'image233.jpeg' + }, + { + chinese_style: '《超时空要塞》', + english_style: 'Shoji Kawamori style', + id: 'd2e08101-5654-4edb-a0d3-d65b54a366e5', + image: 'image236.jpeg' + }, + { + chinese_style: '《铁拳小子》', + english_style: 'Naoto Tsushima style', + id: '8ef6433d-c274-4add-a74d-6b9ac9fff81b', + image: 'image234.jpeg' + }, + { + chinese_style: '《装甲骑兵》', + english_style: 'Ryousuke Takahashi style', + id: 'c9444ff5-a319-4e3f-96b6-4cad9f5d7736', + image: 'image235.jpeg' + }, + { + chinese_style: '《机动战士高达SEED》', + english_style: 'Yoshiyuki Tomino and Kenji Oguro style', + id: '898c1979-99b2-46a7-b581-8272dd37cdba', + image: 'image227.jpeg' + }, + { + chinese_style: '《交响诗篇》', + english_style: 'Shinichirou Watanabe and Yasuhiro Nightow style', + id: '2d420511-63f9-4c77-9e06-efab1df5b815', + image: 'image229.jpeg' + }, + { + chinese_style: '《天元突破》', + english_style: 'Kimiyoshi Yasuda and Hiroyuki Imaishi style', + id: 'c6cb088d-2d9f-45c1-a38f-5a69545c1c5d', + image: 'image225.jpeg' + }, + { + chinese_style: '《机动战士高达00》', + english_style: 'Yoshiyuki Tomino and Kenji Oguro style', + id: '6ab2f65f-4f70-4c57-b4f9-99d66fabe4df', + image: 'image226.jpeg' + }, + { + chinese_style: '《ZEGAPAIN》', + english_style: 'Kouichi Chigira and Shinji Aramaki style', + id: '8238df2e-c1be-4849-9684-1c217fe51b4c', + image: 'image228.jpeg' + } + ], + id: 'e646e2a4-a467-4506-8838-316d45cf5120' + }, + { + sheet: 14, + name: '校园日本动漫风', + subStyle: [ + { + chinese_style: '《青春猪头少年不会梦到兔女郎学姐》', + english_style: 'Daisuke Aizawa style', + id: 'a4bcfe18-7e85-4ebf-8a89-bc0f946e6dd5', + image: 'image242.jpeg' + }, + { + chinese_style: '《我的青春恋爱物语果然有问题》', + english_style: 'Hajime Kamoshida style', + id: 'dfebd4b2-657c-4c46-9319-c3834fde01d5', + image: 'image240.jpeg' + }, + { + chinese_style: '《某科学的超电磁炮》', + english_style: 'Kazuma Kamachi and Motoi Fushimi style', + id: 'f32174cb-1d1a-4cd6-bb14-6bcef9bf068e', + image: 'image243.jpeg' + }, + { + chinese_style: '《干物妹!小埋》', + english_style: 'Sakura Ikezuki style', + id: 'f2ca5f57-e759-4c9a-aa20-39c6862e5892', + image: 'image244.jpeg' + }, + { + chinese_style: '《四月是你的谎言》', + english_style: 'Kanade Yoko style', + id: '0a07af72-fd77-468d-b667-5879048eb289', + image: 'image238.jpeg' + }, + { + chinese_style: '《Free!》', + english_style: 'Kouji Oji style', + id: '4745b1fa-466e-4a25-8aa8-1017a55f6a3f', + image: 'image239.jpeg' + }, + { + chinese_style: '《樱花庄的宠物女孩》', + english_style: 'Hiroshi Kumakura and Tatsuhiko Takimoto style', + id: 'b7f2b370-2660-4653-86f1-e1b1bc959c35', + image: 'image245.jpeg' + }, + { + chinese_style: '《从零开始的异世界生活》', + english_style: 'Nagatsuki Tappei style', + id: '6a8e1708-835b-4266-8546-cea85db6a7a1', + image: 'image241.jpeg' + }, + { + chinese_style: '《冰菓》', + english_style: 'Hyouka Fujino style', + id: '423eedf0-0adb-4738-bd8b-a1d1a3b41bae', + image: 'image246.jpeg' + }, + { + chinese_style: '《Love Lab》', + english_style: 'Miki Yoshikawa style', + id: '8cd108ea-2276-4b2b-9fb9-0078e1b28292', + image: 'image248.jpeg' + }, + { + chinese_style: '《月刊少女野崎君》', + english_style: 'Hima Katsura style', + id: '57e7d3b8-640b-4ab4-a2c3-708af855dd96', + image: 'image250.jpeg' + }, + { + chinese_style: '《Charlotte》', + english_style: 'Jun Maeda style', + id: 'bd564191-e6f7-459d-8b8e-9df837f09a8a', + image: 'image249.jpeg' + }, + { + chinese_style: '《K-On!》', + english_style: 'Manga Mitogawa and Koji Oji style', + id: '6c2ded36-ccd4-4ffd-972e-2587737efea6', + image: 'image247.jpeg' + } + ], + id: 'fbba8ae8-502c-4ae5-856c-ffb70efd852d' + }, + { + sheet: 15, + name: '传统日本动漫风', + subStyle: [ + { + chinese_style: '《红辣椒》', + english_style: 'Satoshi Kon style', + id: '7c2f0bea-67fb-42b1-b02c-e9db30153055', + image: 'image260.jpeg' + }, + { + chinese_style: '《时之歌》', + english_style: 'Makoto Shinkai style', + id: '997b348f-aef8-4e3a-973c-d93153cbe8c5', + image: 'image263.jpeg' + }, + { + chinese_style: '《完美蓝》', + english_style: 'Mamoru Oshii style', + id: 'a1620fb5-2a77-47f5-9ca9-0245e3bbf278', + image: 'image261.jpeg' + }, + { + chinese_style: '《狼的孩子雨和雪》', + english_style: 'Mamoru Hosoda style', + id: '4427cac2-4f4a-490b-990f-cf6d46c9ba3a', + image: 'image264.jpeg' + }, + { + chinese_style: '《夏日大作战》', + english_style: 'Kunihiko Ikuhara style', + id: '1e34a1d4-58cd-4b8c-ab61-b6724bbb053c', + image: 'image262.jpeg' + }, + { + chinese_style: '《未麻的部屋》', + english_style: 'Rintaro style', + id: 'cc689d92-8525-475a-90a4-f42d0adacfd2', + image: 'image265.jpeg' + }, + { + chinese_style: '《千年女优》', + english_style: 'Kon Satoshi style', + id: '189a8d33-391c-4406-9f77-9811fc39429b', + image: 'image266.jpeg' + }, + { + chinese_style: '《虚空之旅》', + english_style: 'Katsuhiro Otomo style', + id: 'b65d8e8a-1ec1-46cf-a32b-8e16c6d4cada', + image: 'image267.jpeg' + }, + { + chinese_style: '《悬崖上的金鱼姬》', + english_style: 'Goro Miyazaki style', + id: 'fe069298-88d6-4ccd-8076-0af31aa1336c', + image: 'image273.jpeg' + }, + { + chinese_style: '《雪国列车》', + english_style: 'Yoshiaki Kawajiri style', + id: 'bf59ebda-e6df-47cc-9dde-f522b5145b58', + image: 'image270.jpeg' + }, + { + chinese_style: '《猎天使魔女》', + english_style: 'Yasuhito Kikuchi style', + id: '72877d59-e763-4440-b41b-4826da6718c7', + image: 'image268.jpeg' + }, + { + chinese_style: '《猎魔人》', + english_style: 'Yoshiaki Kawajiri style', + id: 'dfbb0b74-a875-442f-9219-3aad3a66b2bb', + image: 'image274.jpeg' + }, + { + chinese_style: '《幽灵公主》', + english_style: 'Princess Mononoke style', + id: '7458efbb-97fd-454f-b93f-85ac2e42c139', + image: 'image275.jpeg' + }, + { + chinese_style: '《百变狸猫》', + english_style: 'Pom Poko style', + id: '0e6b6836-297b-4805-8f65-00e83693a1ae', + image: 'image271.jpeg' + }, + { + chinese_style: '《岁月的童话》', + english_style: 'Only Yesterday style', + id: '7978dce5-aaa4-46d8-bf68-5b52758d6fa7', + image: 'image269.jpeg' + }, + { + chinese_style: '《我的邻居山田君》', + english_style: 'The Yamadas style', + id: 'c92165f2-00e8-4c06-b3e4-7531f145e3be', + image: 'image272.jpeg' + }, + { + chinese_style: '《猫的报恩》', + english_style: 'The Cat Returns style', + id: '48aacb4a-501e-4fe6-8cb7-a2ad86657033', + image: 'image258.jpeg' + }, + { + chinese_style: '《借东西的小人阿莉埃蒂》', + english_style: 'Arrietty style', + id: '48a38ff1-da64-496e-aec0-89c0dea12cc9', + image: 'image255.jpeg' + }, + { + chinese_style: '《起风了》', + english_style: 'The Wind Rises style', + id: '5ae600ac-6797-4f11-b9e2-176856a1c23a', + image: 'image251.jpeg' + }, + { + chinese_style: '《辉夜姬物语》', + english_style: 'The Tale of The Princess Kaguya style', + id: 'b7ae0ca4-765e-4dda-9d79-63009b943afe', + image: 'image252.jpeg' + }, + { + chinese_style: '《地海传说》', + english_style: 'Tales from Earthsea style', + id: 'ba04d563-d7d5-453e-92d8-84b7dbb04828', + image: 'image253.jpeg' + }, + { + chinese_style: '《侧耳倾听》', + english_style: 'Whisper of the Heart style', + id: 'f3d6adc2-6c8d-4eeb-ac9e-4bc470099a87', + image: 'image254.jpeg' + }, + { + chinese_style: '《时光代理人》', + english_style: 'Link Click style', + id: '1379ee57-77f0-44ea-ba0f-37d448cf2d86', + image: 'image257.jpeg' + }, + { + chinese_style: '《攻壳机动队》', + english_style: 'Ghost in the Shell style', + id: '01da912f-f68a-4cff-aac8-5f7cdf945c0e', + image: 'image256.jpeg' + }, + { + chinese_style: '《宇宙战舰大和号》', + english_style: 'Space Battleship Yamato style', + id: 'c24de2c9-1dc7-4c9a-b0ff-7fed7a4f9b32', + image: 'image259.jpeg' + } + ], + id: 'a4bae3b0-7ca7-45b4-9a83-505e3407fd5e' + }, + { + sheet: 16, + name: '国产动漫风', + subStyle: [ + { + chinese_style: '《镖人》', + english_style: 'Biao Ren style', + id: '12910c0e-1945-47d9-a1a4-0e13a5fa2616', + image: 'image283.jpeg' + }, + { + chinese_style: '《斗破苍穹》', + english_style: 'Battle Through the Heavens style', + id: 'f2d8e7f4-c4c6-497e-9a0d-cf7957e382dd', + image: 'image281.jpeg' + }, + { + chinese_style: '《雪中悍刀行》', + english_style: 'The Swordsman in the Rain style', + id: 'a863dda7-6be4-4c9c-b64a-ccb8561c89d0', + image: 'image287.jpeg' + }, + { + chinese_style: '《大主宰》', + english_style: 'The Great Ruler style', + id: 'bbbc22aa-d99f-4fa3-9f32-d02ff1bb140f', + image: 'image284.jpeg' + }, + { + chinese_style: '《秦时明月》', + english_style: 'The Legend of Qin style', + id: '77a8e93f-bf9c-433a-9266-03b9b2bf7135', + image: 'image282.jpeg' + }, + { + chinese_style: '《天行九歌》', + english_style: 'Nine Songs style', + id: '487c6c3d-fed2-43e7-87af-02736ad596b7', + image: 'image285.jpeg' + }, + { + chinese_style: '《画江湖之不良人》', + english_style: 'Hua Jianghu: The Bad Guys style', + id: 'dbc9edbb-72ad-4fc2-a0ae-fc146e0c5226', + image: 'image288.jpeg' + }, + { + chinese_style: '《白箱》', + english_style: 'Shirobako style', + id: '67043ea7-f930-4144-b8de-6ad3d447e567', + image: 'image286.jpeg' + }, + { + chinese_style: '《百鬼屋》', + english_style: 'House of Hundred Ghosts style', + id: '3f6b5402-3974-4fd8-83e7-ef88bdc4dad3', + image: 'image291.jpeg' + }, + { + chinese_style: '《端脑》', + english_style: 'Du Nao style', + id: 'dc7994f0-4318-4455-8f8d-e72554be17a9', + image: 'image292.jpeg' + }, + { + chinese_style: '《诛仙》', + english_style: 'Zhuxian style', + id: '400a1638-0d7e-4a3b-a371-ec2ed1d27a11', + image: 'image294.jpeg' + }, + { + chinese_style: '《哪吒之魔童降世》', + english_style: 'Ne Zha style', + id: '82517970-da4a-4f8b-aa90-84d84c09cbfb', + image: 'image295.jpeg' + }, + { + chinese_style: '《西游记》', + english_style: 'Journey to the West style', + id: '3d898e60-ea86-4f20-90f2-f0c09c424b47', + image: 'image296.jpeg' + }, + { + chinese_style: '《封神纪》', + english_style: 'Fengshen Ji style', + id: '25d49d46-9dc0-45ff-a704-dccdaded159f', + image: 'image289.jpeg' + }, + { + chinese_style: '《山海逆战》', + english_style: 'Shan Hai Ni Zhan style', + id: 'f5083376-cefc-40b3-a8d3-ad4cf3fad713', + image: 'image293.jpeg' + }, + { + chinese_style: '《百炼成神》', + english_style: 'Becoming a God style', + id: 'fd053b5e-1ce3-47bf-95e6-65a6caf739ec', + image: 'image290.jpeg' + }, + { + chinese_style: '《白蛇传》', + english_style: 'The Legend of White Snake style', + id: '5cf1e3fd-9458-417c-bd7b-33d93a45958d', + image: 'image280.jpeg' + }, + { + chinese_style: '《妖神记》', + english_style: 'Chronicles of the Demon King style', + id: 'e6a1bdbe-8114-4393-a07b-080c8bbb3b1b', + image: 'image277.jpeg' + }, + { + chinese_style: '《镇魂街》', + english_style: 'Rakshasa Street style', + id: '3dcaff26-29f4-43a1-8141-0ca108420d91', + image: 'image278.jpeg' + }, + { + chinese_style: '《斗罗大陆》', + english_style: 'Soul Land style', + id: 'c7303c09-1aa4-4156-9b21-3c63794d4e8e', + image: 'image276.jpeg' + }, + { + chinese_style: '《血族》', + english_style: 'Bloodline style', + id: '02a2e278-4eb3-4384-a13a-80e1eef5b627', + image: 'image279.jpeg' + } + ], + id: 'fa957f58-129e-42cc-b80a-24859fc90789' + }, + { + sheet: 17, + name: '电影风格', + subStyle: [ + { + chinese_style: '黑白电影', + english_style: 'Black and white movies', + id: '7bffd5d9-71e7-4443-99b7-d38cbe5853ee', + image: 'image309.jpeg' + }, + { + chinese_style: '科幻电影', + english_style: 'science fiction film', + id: 'f17c812e-aafd-4ac4-b6e4-36467f75c4e6', + image: 'image310.jpeg' + }, + { + chinese_style: '爱情电影', + english_style: 'romantic movies', + id: 'd2bac280-9bff-4582-a78e-b94a3f75950e', + image: 'image311.jpeg' + }, + { + chinese_style: '动作电影', + english_style: 'action movies', + id: 'a723ca3f-7f59-436b-b4aa-bd99f58bfe45', + image: 'image312.jpeg' + }, + { + chinese_style: '喜剧电影', + english_style: 'comedy movies', + id: '1a39e88a-2e08-4adf-8859-76d7ac36f2fd', + image: 'image306.jpeg' + }, + { + chinese_style: '恐怖电影', + english_style: 'horror movies', + id: 'cafb6bde-4f43-4fbf-8bf4-9b9a2727c7bd', + image: 'image307.jpeg' + }, + { + chinese_style: '战争电影', + english_style: 'war movies', + id: '64564b1d-69a2-45ff-b25e-3f073a45b76f', + image: 'image308.jpeg' + }, + { + chinese_style: '纪录片', + english_style: 'documentaries', + id: '304e6b0f-1b30-4336-8f88-79b907d9592d', + image: 'image305.jpeg' + }, + { + chinese_style: '动画电影', + english_style: 'animated movies', + id: '3ae766f0-a97b-4004-8a06-b5b8bd76f668', + image: 'image297.jpeg' + }, + { + chinese_style: '奇幻电影', + english_style: 'fantasy movies', + id: '282b2144-8cf3-470e-9179-7f9419d12a1c', + image: 'image299.jpeg' + }, + { + chinese_style: '惊悚电影', + english_style: 'thriller movies', + id: 'ab55351f-3c55-4828-babc-a34e724d95a2', + image: 'image303.jpeg' + }, + { + chinese_style: '犯罪电影', + english_style: 'crime movies', + id: 'f95ea621-d040-47e5-b6e0-f6f326af1036', + image: 'image300.jpeg' + }, + { + chinese_style: '冒险电影', + english_style: 'adventure movies', + id: 'eb6ecf79-2d53-454b-8302-7cca5c9b591b', + image: 'image301.jpeg' + }, + { + chinese_style: '家庭电影', + english_style: 'family movies', + id: 'c2e2d144-8c87-4110-930b-fdfd41df9b30', + image: 'image302.jpeg' + }, + { + chinese_style: '历史剧', + english_style: 'historical dramas', + id: '029cc87b-eeab-4f95-ac9f-425460ee9f08', + image: 'image298.jpeg' + }, + { + chinese_style: '音乐剧', + english_style: 'musical movies', + id: '61993e49-68db-4be4-b8a9-908e1ac7b962', + image: 'image304.jpeg' + }, + { + chinese_style: '悬疑电影', + english_style: 'mystery movies', + id: '98bf0ddc-4904-4576-9f93-db27363eee28', + image: 'image409.jpeg' + }, + { + chinese_style: '戏剧电影', + english_style: 'drama movies', + id: '59885546-5b58-40c8-8202-ded895601539', + image: 'image410.jpeg' + }, + { + chinese_style: '运动电影', + english_style: 'sports movies', + id: '42232865-7ffb-49e1-989b-efab01e44ac0', + image: 'image411.jpeg' + } + ], + id: '5d5b2cfa-a417-4723-9931-a3abbac2b452' + }, + { + sheet: 18, + name: '艺术风格', + subStyle: [ + { + chinese_style: '幻想艺术风格', + english_style: 'Fantasy art style', + id: 'a7f0560f-fbaa-4fbe-abae-70401e6fedbe', + image: 'image333.jpeg' + }, + { + chinese_style: '复古漫画风格', + english_style: 'Retro comic style', + id: '7fb47c08-d2e3-439d-9e5d-4a9e3886203e', + image: 'image338.jpeg' + }, + { + chinese_style: '抽象艺术风格', + english_style: 'Abstract art style', + id: 'a7e465c0-8c08-4407-928e-00ddcfc1e14d', + image: 'image331.jpeg' + }, + { + chinese_style: '极简主义风格', + english_style: 'Minimalist style', + id: '2b4f5f05-9f9f-4605-8ea6-3e9f3e9390b7', + image: 'image335.jpeg' + }, + { + chinese_style: '涂鸦风格', + english_style: 'Graffiti style', + id: 'ca054eb1-ede6-48da-9832-46d2edb04020', + image: 'image336.jpeg' + }, + { + chinese_style: '浮世绘风格', + english_style: 'Ukiyo-e style', + id: 'd73397d8-6535-4a73-a6a8-2107c216c2ae', + image: 'image332.jpeg' + }, + { + chinese_style: '表现主义风格', + english_style: 'Expressionism style', + id: 'ef600238-917d-4bb2-a38d-6619dce4724d', + image: 'image337.jpeg' + }, + { + chinese_style: '超现实主义风格', + english_style: 'Surrealism style', + id: 'c4df7c3e-82b1-44dd-9c7a-6ce48c4c157d', + image: 'image334.jpeg' + }, + { + chinese_style: '装饰艺术风格', + english_style: 'Art Deco style', + id: '33e5d594-dc86-4396-82d9-89544b356c69', + image: 'image341.jpeg' + }, + { + chinese_style: '未来主义风格', + english_style: 'Futurism style', + id: '0a59d038-ff76-4575-aa38-616889b38361', + image: 'image344.jpeg' + }, + { + chinese_style: '像素艺术风格', + english_style: 'Pixel art style', + id: '57e62d07-2d1a-450a-a16e-6641b64c9c07', + image: 'image346.jpeg' + }, + { + chinese_style: '漫画现实主义风格', + english_style: 'Comic realism style', + id: '6f96a537-a952-4c98-adb5-02ca82025479', + image: 'image340.jpeg' + }, + { + chinese_style: '卡通渲染风格', + english_style: 'Cartoon rendering style', + id: '0957f80e-2c3e-472a-8f05-263ff831c8ae', + image: 'image339.jpeg' + }, + { + chinese_style: '哥特式漫画风格', + english_style: 'Gothic comic style', + id: '5ab38c3b-a63c-4852-85d0-5cde07fc900a', + image: 'image345.jpeg' + }, + { + chinese_style: '新古典主义风格', + english_style: 'Neoclassicism style', + id: 'ce5ee6e7-01d2-42b9-85aa-47b4503cfabc', + image: 'image342.jpeg' + }, + { + chinese_style: '浪漫主义风格', + english_style: 'Romanticism style', + id: '38d106e0-2272-4964-8f83-84b9808d8d1d', + image: 'image343.jpeg' + }, + { + chinese_style: '街头艺术风格', + english_style: 'Street art style', + id: 'ef0a8d87-ba4f-48df-b2f9-11345eb75c10', + image: 'image319.jpeg' + }, + { + chinese_style: '概念艺术风格', + english_style: 'Concept art style', + id: '347d3e35-5a70-451f-b6cf-58aaf339fc34', + image: 'image317.jpeg' + }, + { + chinese_style: '幻想漫画风格', + english_style: 'Fantasy comic style', + id: 'fdda76a9-25a5-47fa-9570-ec12c6c744c8', + image: 'image316.jpeg' + }, + { + chinese_style: '科幻风格', + english_style: 'Science fiction style', + id: '8e26e6b3-c109-43c0-ae58-7f68ff050a88', + image: 'image320.jpeg' + }, + { + chinese_style: '西部风格', + english_style: 'Western style', + id: 'b23cf1e2-3449-48ae-b71d-160f081a7202', + image: 'image314.jpeg' + }, + { + chinese_style: '儿童插画风格', + english_style: "Children's illustration style", + id: '4572cc54-54e1-41b5-9d63-0b8e53a6bdb6', + image: 'image318.jpeg' + }, + { + chinese_style: '青春漫画风格', + english_style: 'Youth comic style', + id: '06ccb7d6-d722-418e-ab6f-77f72c048814', + image: 'image315.jpeg' + }, + { + chinese_style: '历史题材漫画风格', + english_style: 'Historical comic style', + id: 'af31cb40-2f6a-486d-9e7b-667837360b15', + image: 'image313.jpeg' + }, + { + chinese_style: '民间艺术风格', + english_style: 'Folk art style', + id: '750cca85-be64-42c4-b993-b53c414fd01c', + image: 'image325.jpeg' + }, + { + chinese_style: '艺术新潮流风格', + english_style: 'Art Nouveau style', + id: '755ac85b-8c53-4674-a2ca-3ca86c588814', + image: 'image322.jpeg' + }, + { + chinese_style: '艺术装饰风格', + english_style: 'Art Deco style', + id: 'b20fc215-8631-4334-a345-27de5fbe83b5', + image: 'image328.jpeg' + }, + { + chinese_style: '艺术与工艺风格', + english_style: 'Arts and Crafts style', + id: 'b7bbf919-b6dc-4074-a42e-986b317dc43b', + image: 'image323.jpeg' + }, + { + chinese_style: '野兽派风格', + english_style: 'Fauvism style', + id: 'c8c19729-1cad-424e-8d89-79e024587100', + image: 'image326.jpeg' + }, + { + chinese_style: '立体主义风格', + english_style: 'Cubism style', + id: 'c1c2d744-3e88-4d78-8a91-0626caee0f21', + image: 'image324.jpeg' + }, + { + chinese_style: '原始主义风格', + english_style: 'Primitivism style', + id: 'a784d492-5c63-4ed0-849e-af7fc29ee76e', + image: 'image327.jpeg' + }, + { + chinese_style: '波普艺术风格', + english_style: 'Pop art style', + id: 'cd5726f6-6ef6-4cd8-a59e-8e06584ee6b7', + image: 'image321.jpeg' + }, + { + chinese_style: '后现代主义风格', + english_style: 'Postmodernism style', + id: 'f1f2c258-b5ba-41ba-994b-5f7ab3f180ae', + image: 'image329.jpeg' + }, + { + chinese_style: '超扁平风格', + english_style: 'Superflat style', + id: '531ec980-a5e0-4126-ba9d-449fa8b3605b', + image: 'image330.jpeg' + } + ], + id: '4dfa6a37-ea05-4a09-aabb-eed2c714830e' + }, + { + sheet: 19, + name: '国风', + subStyle: [ + { + chinese_style: '中国水墨画', + english_style: 'Chinese ink wash painting', + id: '9aa7dced-46c5-4b9c-9159-41f3d9239a98', + image: 'image352.jpeg' + }, + { + chinese_style: '工笔花鸟画', + english_style: 'Fine-brushwork floral and bird painting', + id: 'e67ae511-04c0-4493-a696-a6799b07f2f8', + image: 'image347.jpeg' + }, + { + chinese_style: '青绿山水画', + english_style: 'Green and blue landscape painting', + id: '46e39c6a-5df8-49e7-9eed-807cd3f14db2', + image: 'image349.jpeg' + }, + { + chinese_style: '敦煌壁画', + english_style: 'Dunhuang murals', + id: '85561e1f-72dc-4c7e-a1ce-f70f5ccb4f5f', + image: 'image348.jpeg' + }, + { + chinese_style: '剪纸艺术', + english_style: 'Chinese paper-cut art', + id: '7ebba49c-e8ef-45f8-bc68-e80036b92b62', + image: 'image350.jpeg' + }, + { + chinese_style: '年画', + english_style: 'New Year paintings', + id: 'e84038c0-0831-485e-beaa-d9247f960ec0', + image: 'image351.jpeg' + } + ], + id: 'aa51c366-fcaa-4063-84d7-ad5af8bca191' + }, + { + sheet: 20, + name: '其他应用', + subStyle: [ + { + chinese_style: '自然风光', + english_style: 'Natural scenery style', + id: '75b472ed-192d-4120-87af-c38f353b938f', + image: 'image403.jpeg' + }, + { + chinese_style: '宇宙星空', + english_style: 'Space and stars theme', + id: 'a15f8591-0ce4-4d9d-9301-92b6f98b927d', + image: 'image404.jpeg' + }, + { + chinese_style: '城市建筑', + english_style: 'Urban architecture style', + id: 'be9d8a98-d011-4dd4-ace1-c55119691d0c', + image: 'image405.jpeg' + }, + { + chinese_style: '传统文化', + english_style: 'Traditional culture theme', + id: '3e1e9539-4e2c-4ed5-82cc-84ec83a7f370', + image: 'image406.jpeg' + }, + { + chinese_style: '现代艺术', + english_style: 'Modern art style', + id: 'bc270c1a-ab1d-4e9d-a9a0-5413323802dd', + image: 'image407.jpeg' + }, + { + chinese_style: '民族风情', + english_style: 'Ethnic style', + id: '43f968fd-e4d3-417a-b40d-a07c2dd691f7', + image: 'image401.jpeg' + }, + { + chinese_style: '古风仙韵', + english_style: 'Ancient charm style', + id: 'd00cead5-0991-40ed-a64a-5e0a8b5770a9', + image: 'image402.jpeg' + }, + { + chinese_style: '未来幻想', + english_style: 'Future fantasy style', + id: '3537c9f9-a77f-4daf-84cc-73b260b2c155', + image: 'image408.jpeg' + }, + { + chinese_style: '科技机械', + english_style: 'Technology and machinery theme', + id: 'e83d74ec-697c-4962-8ad3-aa8bd4d85496', + image: 'image356.jpeg' + }, + { + chinese_style: '人物插画', + english_style: 'Character illustration style', + id: 'c1c6b6bb-d1ec-40b4-b703-a9ea67c73786', + image: 'image353.jpeg' + }, + { + chinese_style: '动漫卡通', + english_style: 'Anime and cartoon style', + id: '73425a65-a3ae-4c51-a64c-670984165e32', + image: 'image357.jpeg' + }, + { + chinese_style: '游戏原画', + english_style: 'Game concept art style', + id: 'cddd0ead-0178-4b9e-86f9-2c83de6b57d0', + image: 'image358.jpeg' + }, + { + chinese_style: '影视剧照', + english_style: 'Film and TV stills style', + id: '750f6d3b-7e5a-40d2-a581-c3d21ad4640b', + image: 'image355.jpeg' + }, + { + chinese_style: '广告插图', + english_style: 'Advertising illustration style', + id: '9d95166e-8131-4a33-9ded-8a9c7bfddcb0', + image: 'image359.jpeg' + }, + { + chinese_style: '包装设计', + english_style: 'Packaging design style', + id: '03aa78fa-3c6f-450d-b64c-b842a78f4237', + image: 'image360.jpeg' + }, + { + chinese_style: '平面海报', + english_style: 'Graphic poster style', + id: 'aaed015c-806c-4d8c-9fb4-7ed0d28098b5', + image: 'image354.jpeg' + }, + { + chinese_style: '网页UI', + english_style: 'Web UI design style', + id: '063e6129-8f8b-4d61-a4f2-547342a279a3', + image: 'image364.jpeg' + }, + { + chinese_style: '品牌标识', + english_style: 'Brand identity design style', + id: '5acd5018-661c-4983-8257-773217ba6951', + image: 'image361.jpeg' + }, + { + chinese_style: '时尚插画', + english_style: 'Fashion illustration style', + id: 'a9424c0e-2a33-40ae-b040-71127529de8e', + image: 'image365.jpeg' + }, + { + chinese_style: '儿童绘本', + english_style: "Children's picture book style", + id: 'd2c5a700-1848-4341-93ea-75e6eae70dc2', + image: 'image363.jpeg' + }, + { + chinese_style: '科普漫画', + english_style: 'Popular science comic style', + id: '21110cd9-34fd-4acf-86e5-18ea5250b16b', + image: 'image366.jpeg' + }, + { + chinese_style: '教育绘本', + english_style: 'Educational picture book style', + id: '03fad4d9-2f97-4c83-9226-7f913cb22794', + image: 'image362.jpeg' + }, + { + chinese_style: '医学插图', + english_style: 'Medical illustration style', + id: '6da6fa32-ded0-485f-a414-17b3d7fff6d2', + image: 'image367.jpeg' + }, + { + chinese_style: '建筑设计', + english_style: 'Architectural design style', + id: '70c99e02-0578-4ba2-ab44-c49e354b1198', + image: 'image368.jpeg' + }, + { + chinese_style: '汽车设计', + english_style: 'Automotive design style', + id: '04454eab-0491-4e38-a9a0-1476f4138ecb', + image: 'image370.jpeg' + }, + { + chinese_style: '工业设计', + english_style: 'Industrial design style', + id: 'c18d111c-f3e6-4df8-9867-df8af1207399', + image: 'image371.jpeg' + }, + { + chinese_style: '室内设计', + english_style: 'Interior design style', + id: '4428d573-1829-4cad-9ce1-54c8dda6b7c2', + image: 'image374.jpeg' + }, + { + chinese_style: '时装设计', + english_style: 'Fashion design style', + id: '90f7f936-8b54-4aae-bba1-ee6cdae5ce76', + image: 'image373.jpeg' + }, + { + chinese_style: '珠宝设计', + english_style: 'Jewelry design style', + id: '1f9b2bd8-caa5-4f6c-ad87-0aa3ab5be760', + image: 'image369.jpeg' + }, + { + chinese_style: '家具设计', + english_style: 'Furniture design style', + id: 'b2d1bf95-5c92-4c9d-be02-cc5dcdeff625', + image: 'image372.jpeg' + }, + { + chinese_style: '平面设计', + english_style: 'Graphic design style', + id: '761c2f31-2b3f-437b-9784-976aff089724', + image: 'image375.jpeg' + }, + { + chinese_style: '插图设计', + english_style: 'Illustration design style', + id: '3887bd64-8f74-4994-bd0a-3f3798d0a336', + image: 'image376.jpeg' + }, + { + chinese_style: '动画设计', + english_style: 'Animation design style', + id: '9961b135-7658-47c8-b8b6-94ee646345be', + image: 'image380.jpeg' + }, + { + chinese_style: '游戏设计', + english_style: 'Game design style', + id: '4b95b12e-d27e-460c-b3c3-4bf7306adfca', + image: 'image377.jpeg' + }, + { + chinese_style: '用户界面', + english_style: 'User interface design style', + id: '90a47637-a287-4f73-a6d3-e10b6cd52035', + image: 'image378.jpeg' + }, + { + chinese_style: '虚拟现实', + english_style: 'Virtual reality theme', + id: '805a3511-4037-4981-b628-85b2c1c5805c', + image: 'image382.jpeg' + }, + { + chinese_style: '增强现实', + english_style: 'Augmented reality theme', + id: '1126d0da-6610-4183-915c-e9fe695d44b1', + image: 'image381.jpeg' + }, + { + chinese_style: '人工智能', + english_style: 'Artificial intelligence theme', + id: 'c98ee53f-427c-4016-8745-b781ed180307', + image: 'image383.jpeg' + }, + { + chinese_style: '机器人科技', + english_style: 'Robotics and technology theme', + id: 'c6edebf7-f2be-4027-8620-888e0dec1e86', + image: 'image384.jpeg' + }, + { + chinese_style: '生物工程', + english_style: 'Biotechnology theme', + id: '00425a0d-5e3d-4304-83be-f4653315abed', + image: 'image379.jpeg' + }, + { + chinese_style: '环境保护', + english_style: 'Environmental protection theme', + id: '764a6b68-3530-4d45-bdbe-3db74c659dea', + image: 'image390.jpeg' + }, + { + chinese_style: '可持续发展', + english_style: 'Sustainable development theme', + id: '7bf52103-ab9a-4fff-8250-42a5f8d32e73', + image: 'image386.jpeg' + }, + { + chinese_style: '社会公益', + english_style: 'Social welfare theme', + id: '6edb9969-682c-4721-aa80-29b6ca583a57', + image: 'image389.jpeg' + }, + { + chinese_style: '医疗健康', + english_style: 'Medical and health theme', + id: 'e36cdc09-976a-43e5-921e-687950020ac6', + image: 'image391.jpeg' + }, + { + chinese_style: '教育培训', + english_style: 'Education and training theme', + id: '402fbf92-ff82-422b-a12f-3595ff908197', + image: 'image387.jpeg' + }, + { + chinese_style: '金融经济', + english_style: 'Finance and economy theme', + id: 'e75bbc8c-ba12-47a8-9d42-1f2847533afd', + image: 'image388.jpeg' + }, + { + chinese_style: '科学研究', + english_style: 'Scientific research theme', + id: '9cb82967-bf9e-4a6c-8a22-11e9b5b3d698', + image: 'image385.jpeg' + }, + { + chinese_style: '文化艺术', + english_style: 'Culture and art theme', + id: '92fcc6e2-88a3-474d-b0ac-ab657e7605b6', + image: 'image392.jpeg' + }, + { + chinese_style: '体育运动', + english_style: 'Sports and fitness theme', + id: 'ecddba8f-d64e-431b-81fe-6a8234da6bca', + image: 'image395.jpeg' + }, + { + chinese_style: '旅游度假', + english_style: 'Travel and vacation theme', + id: '7b826dfa-670b-4826-9d45-7803d2ee5c6a', + image: 'image400.jpeg' + }, + { + chinese_style: '美食烹饪', + english_style: 'Food and cooking theme', + id: '2d72e89e-8208-4d19-ad8a-0f8e3718fb42', + image: 'image398.jpeg' + }, + { + chinese_style: '时尚美妆', + english_style: 'Fashion and beauty theme', + id: '50acc1db-330c-413b-aa99-efcbda18c94d', + image: 'image393.jpeg' + }, + { + chinese_style: '婚礼庆典', + english_style: 'Wedding and celebration theme', + id: '353a2699-8906-4d15-beac-1a942bf1e26e', + image: 'image394.jpeg' + }, + { + chinese_style: '家居生活', + english_style: 'Home and lifestyle theme', + id: '1b9ce9cf-2c7f-43a4-a619-171856e8db41', + image: 'image399.jpeg' + }, + { + chinese_style: '母婴育儿', + english_style: 'Maternity and parenting theme', + id: 'df0f3682-82ae-4729-b469-25f0c1784364', + image: 'image396.jpeg' + }, + { + chinese_style: '科技创新', + english_style: 'Technological innovation theme', + id: '1fa174c2-3557-4fec-bd03-1774561d494a', + image: 'image397.jpeg' + } + ], + id: 'cfd1b687-0043-4e39-bf8e-b8f4d4667016' + } + ], + /** + * 获取所有的生图风格 + * @returns {Array} 返回所有的风格数据 + */ + getImageStyle: function () { + return this.image_style + }, + + /** + * 获取指定的ID的风格信息,传入的是一个数组 + * @param {*} value id集合 + */ + getImageStyleInfomation(value) { + try { + if (value) { + value = JSON.parse(value) + } else { + value = [] + } + value = value ? value : [] + let style = ImageStyleDefine.getAllSubStyle() + let tmp = [] + for (let i = 0; i < value.length; i++) { + const element = value[i] + for (let j = 0; j < style.length; j++) { + const item = style[j] + if (item.id == element) { + tmp.push(item) + break + } + } + } + let newSubStyle = cloneDeep(tmp) + for (let i = 0; i < newSubStyle.length; i++) { + const element = newSubStyle[i] + element.image = path.join(define.image_path, 'style/' + element.image) + } + return successMessage(newSubStyle, "获取成功", "ImageStyleDefine_getImageStyleInfomation") + } catch (error) { + return errorMessage("获取默认的风格数据失败", "ImageStyleDefine_getImageStyleInfomation") + } + }, + + /** + * 获取指定的的ID的风格数据,可以多个 + * @param {*} ids ID的数组 + * @returns + */ + getImageStyleObjectByIds: function (ids) { + let result = [] + if (!ids) { + ids = [] + } + let style = this.getAllSubStyle() + for (let i = 0; i < ids.length; i++) { + const element = ids[i] + for (let j = 0; j < style.length; j++) { + const item = style[j] + if (item.id == element) { + result.push(item) + break + } + } + } + return result + }, + + /** + * 获取指定ID数组的风格提示词 + * @param {*} ids 需要拼接的id数组 + * @returns 返回拼接好的英文提示词字符串(会加上SD设置那边的权重) + */ + getImageStyleStringByIds: async function (ids) { + let res = '' + let tmp = [] + let result = this.getImageStyleObjectByIds(ids) + for (let i = 0; i < result.length; i++) { + const element = result[i] + tmp.push(element.english_style) + } + let weight = await SdSettingDefine.getSettingSettingProperty('style_weight', false) + weight = weight ? weight : 1 + tmp.map((item) => { + if (global.config.image_generate_category == 'sd') { + res += `(${item}:${weight}),` + } else { + res += `${item},` + } + }) + return res + }, + + /** + * + * @returns {Array} 返回所有的风格数据 + */ + getAllSubStyle: function () { + let subStyle = [] + this.image_style.forEach((item) => { + subStyle = subStyle.concat(item.subStyle) + }) + return subStyle + }, + + /** + * 根据图像风格数据,返回选择菜单 + * @returns {Array} 返回所有的风格数据 + */ + getImageStyleMenu: function () { + let menu = [] + this.image_style.forEach((item) => { + menu.push({ + id: item.id, + label: item.name, + key: item.id + }) + }) + return menu + }, + + /** + * 获取指定的ID的下面数据的风格 + * @param {*} id 菜单的ID + */ + getImagePathById: function (id) { + try { + let index = this.image_style.findIndex((item) => item.id == id) + if (index < 0) { + throw new Error('没有找到指定的ID') + } + let subStyle = this.image_style[index].subStyle + return subStyle + } catch (error) { + throw error + } + } +} + +export { ImageStyleDefine } diff --git a/src/define/setting/dynamicSetting.js b/src/define/setting/dynamicSetting.js index 1570b0e..a327eae 100644 --- a/src/define/setting/dynamicSetting.js +++ b/src/define/setting/dynamicSetting.js @@ -2,7 +2,7 @@ let fspromises = require('fs').promises; import { get, cloneDeep } from 'lodash'; import { define } from '../define'; -import { errorMessage } from '../../main/generalTools'; +import { errorMessage } from '../../main/Public/generalTools'; export class DynamicSetting { constructor(global) { diff --git a/src/define/setting/mjSetting.js b/src/define/setting/mjSetting.js index 4708ac4..b3ef976 100644 --- a/src/define/setting/mjSetting.js +++ b/src/define/setting/mjSetting.js @@ -1,4 +1,4 @@ -import { successMessage } from "../../main/generalTools"; +import { successMessage } from "../../main/Public/generalTools"; export class MjSetting { constructor(golbal) { diff --git a/src/define/setting/sdSettingDefine.js b/src/define/setting/sdSettingDefine.js index 9508e40..5185aed 100644 --- a/src/define/setting/sdSettingDefine.js +++ b/src/define/setting/sdSettingDefine.js @@ -2,7 +2,7 @@ import { get } from "lodash"; import { define } from "../define"; let fspromises = require("fs").promises; import { Tools } from "../../main/tools"; -import { errorMessage } from "../../main/generalTools"; +import { errorMessage } from "../../main/Public/generalTools"; let tools = new Tools(); // Create a shared object diff --git a/src/define/tagDefine.js b/src/define/tagDefine.js index 849b830..898d782 100644 --- a/src/define/tagDefine.js +++ b/src/define/tagDefine.js @@ -1,195 +1,196 @@ - -let fspromises = require('fs').promises; -import { get, cloneDeep } from 'lodash'; -import { define } from './define'; -import path from 'path'; -import { Tools } from '../main/tools'; -const { v4: uuidv4 } = require('uuid'); +let fspromises = require('fs').promises +import { get, cloneDeep } from 'lodash' +import { define } from './define' +import path from 'path' +import { Tools } from '../main/tools' +const { v4: uuidv4 } = require('uuid') export class TagDefine { - constructor(global) { - this.global = global; - this.tools = new Tools(); - } + constructor(global) { + this.global = global + this.tools = new Tools() + } - /** - * 获取tag选择模式(标签和下拉select) - */ - async getTagSelectModel() { - return { - code: 1, - data: - [ - { label: "标签", value: "tag" }, - { label: "下拉", value: "drop" }, - ] + /** + * 获取tag选择模式(标签和下拉select) + */ + async getTagSelectModel() { + return { + code: 1, + data: [ + { label: '标签', value: 'tag' }, + { label: '下拉', value: 'drop' } + ] + } + } + + /** + * 通过指定的类型,获取数据 + * @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回 + * @param {*} property 要返回的属性的名称,若是传入null,返回整个属性的数据 + * @param {*} defaultData 默认数据,默认值为null + * @returns + */ + async getTagDataByTypeAndProperty(type, property, defaultData = null) { + try { + let res = [] + // 获取自定义的GPT数据 + let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')) + let data = get(tag_setting, property, {}) + // 若是传入的属性名为null,直接返回当前tags里面的所有的数据 + if (property == null) { + data = tag_setting + } + + if (type == 'default') { + // res = get(this, property, defaultData); + } else if (type == 'dynamic') { + res = data + } else if (type == 'all') { + let tmp_arr = cloneDeep(get([], property, defaultData)) + tmp_arr = tmp_arr.concat(data) + res = tmp_arr + } else { + throw new Error(`不存在的类型 : ${value}`) + } + if (property) { + res.forEach((item) => { + if (item.show_image && item.show_image != '') { + item.show_image = path.join(define.image_path, item.show_image) + } + }) + } else { + // 返回之前,判断里面是不是有预览图片路径 + if (res.hasOwnProperty('character_tags')) { + res.character_tags.forEach((item) => { + if (item.show_image && item.show_image != '') { + item.show_image = path.join(define.image_path, item.show_image) + } + }) } - } - - - /** - * 通过指定的类型,获取数据 - * @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回 - * @param {*} property 要返回的属性的名称,若是传入null,返回整个属性的数据 - * @param {*} defaultData 默认数据,默认值为null - * @returns - */ - async getTagDataByTypeAndProperty(type, property, defaultData = null) { - try { - let res = []; - // 获取自定义的GPT数据 - let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')); - let data = get(tag_setting, property, {}); - // 若是传入的属性名为null,直接返回当前tags里面的所有的数据 - if (property == null) { - data = tag_setting; - } - - if (type == "default") { - // res = get(this, property, defaultData); - } else if (type == "dynamic") { - res = data; - } else if (type == "all") { - let tmp_arr = cloneDeep(get([], property, defaultData)); - tmp_arr = tmp_arr.concat(data); - res = tmp_arr; - } - else { - throw new Error(`不存在的类型 : ${value}`); - } - - // 返回之前,判断里面是不是有预览图片路径 - if (res.hasOwnProperty("character_tags")) { - res.character_tags.forEach(item => { - if (item.show_image && item.show_image != "") { - item.show_image = path.join(define.image_path, item.show_image); - } - }); - } - if (res.hasOwnProperty("scene_tags")) { - res.scene_tags.forEach(item => { - if (item.show_image && item.show_image != "") { - item.show_image = path.join(define.image_path, item.show_image); - } - }); - } - if (res.hasOwnProperty("style_tags")) { - res.style_tags.forEach(item => { - if (item.show_image && item.show_image != "") { - item.show_image = path.join(define.image_path, item.show_image); - } - }); - } - - return { - code: 1, - data: res - } - } catch (error) { - return { - code: 0, - message: error.toString() + if (res.hasOwnProperty('scene_tags')) { + res.scene_tags.forEach((item) => { + if (item.show_image && item.show_image != '') { + item.show_image = path.join(define.image_path, item.show_image) } + }) } - } - - /** - * 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加 - * @param {*} value - * @param {*} property - */ - async saveTagPropertyData(value) { - try { - let property = value[1]; - value = JSON.parse(value[0]); - let tmp_key = uuidv4(); - - // 特殊操作。为角色和场景的时候,需要copy图片 - if (property == "character_tags" || property == "scene_tags" || property == "style_tags") { - let show_image = value.show_image; - if (show_image && show_image != "") { - let file_name = `c_s/${value.key ? value.key : tmp_key}.png` - let new_image_path = path.join(define.image_path, file_name); - await this.tools.copyFileOrDirectory(show_image, new_image_path); - value.show_image = file_name; - value.children?.forEach(item => { - item.show_image = file_name; - }); - } - } - - // 获取自定义的GPT数据 - let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')); - let tag = get(tag_setting, property, []); - if (value.key) { - // 判断当前ID的数据是否存在,存在覆盖,不存在追加 - let index = tag.findIndex(item => item.key == value.key); - value.value = value.key; - if (index < 0) { - // 判断相同名字的数据是不是存在,存在报错 - if (tag.some(item => item.label == value.label)) { - throw new Error("已存在相同名称的数据,请修改名称后再保存"); - } - tag.push(value); - } else { - tag[index] = value; - } - } else { - // 判断相同名字的数据是不是存在,存在报错 - if (tag.some(item => item.label == value.label)) { - throw new Error("已存在相同名称的数据,请修改名称后再保存"); - } - value.key = tmp_key; - value.value = value.key; - tag.push(value); - } - tag_setting[property] = tag; - // 写入文件 - await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting)); - return { - code: 1, - message: "保存成功" - } - } catch (error) { - return { - code: 0, - message: error.toString() + if (res.hasOwnProperty('style_tags')) { + res.style_tags.forEach((item) => { + if (item.show_image && item.show_image != '') { + item.show_image = path.join(define.image_path, item.show_image) } + }) } + } + + return { + code: 1, + data: res + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - /** - * 删除自定义GPT指定属性中的指定ID的数据 - * @param {*} id - * @param {*} property - */ - async deleteTagPropertyData(value) { - try { - let property = value[1]; - let id = value[0]; - // 获取自定义的GPT数据 - let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')); - let tags = tag_setting[property] ? tag_setting[property] : []; - // 判断当前ID的数据是否存在,存在删除 - let index = tags.findIndex(item => item.key == id); - if (index >= 0) { - tags.splice(index, 1); - } - // 将修改后的数据保存 - tag_setting[property] = tags; - // 写入文件 - await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting)); - return { - code: 1, - message: "删除成功" - } + /** + * 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加 + * @param {*} value + * @param {*} property + */ + async saveTagPropertyData(value) { + try { + let property = value[1] + value = JSON.parse(value[0]) + let tmp_key = uuidv4() - } catch (error) { - return { - code: 0, - message: error.toString() - } + // 特殊操作。为角色和场景的时候,需要copy图片 + if (property == 'character_tags' || property == 'scene_tags' || property == 'style_tags') { + let show_image = value.show_image + if (show_image && show_image != '') { + let file_name = `c_s/${value.key ? value.key : tmp_key}.png` + let new_image_path = path.join(define.image_path, file_name) + await this.tools.copyFileOrDirectory(show_image, new_image_path) + value.show_image = file_name + value.children?.forEach((item) => { + item.show_image = file_name + }) } - } + } -} \ No newline at end of file + // 获取自定义的GPT数据 + let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')) + let tag = get(tag_setting, property, []) + if (value.key) { + // 判断当前ID的数据是否存在,存在覆盖,不存在追加 + let index = tag.findIndex((item) => item.key == value.key) + value.value = value.key + if (index < 0) { + // 判断相同名字的数据是不是存在,存在报错 + if (tag.some((item) => item.label == value.label)) { + throw new Error('已存在相同名称的数据,请修改名称后再保存') + } + tag.push(value) + } else { + tag[index] = value + } + } else { + // 判断相同名字的数据是不是存在,存在报错 + if (tag.some((item) => item.label == value.label)) { + throw new Error('已存在相同名称的数据,请修改名称后再保存') + } + value.key = tmp_key + value.value = value.key + tag.push(value) + } + tag_setting[property] = tag + // 写入文件 + await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting)) + return { + code: 1, + message: '保存成功' + } + } catch (error) { + return { + code: 0, + message: error.toString() + } + } + } + + /** + * 删除自定义GPT指定属性中的指定ID的数据 + * @param {*} id + * @param {*} property + */ + async deleteTagPropertyData(value) { + try { + let property = value[1] + let id = value[0] + // 获取自定义的GPT数据 + let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')) + let tags = tag_setting[property] ? tag_setting[property] : [] + // 判断当前ID的数据是否存在,存在删除 + let index = tags.findIndex((item) => item.key == id) + if (index >= 0) { + tags.splice(index, 1) + } + // 将修改后的数据保存 + tag_setting[property] = tags + // 写入文件 + await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting)) + return { + code: 1, + message: '删除成功' + } + } catch (error) { + return { + code: 0, + message: error.toString() + } + } + } +} diff --git a/src/define/tts/edgeTts.ts b/src/define/tts/edgeTts.ts index d67773d..dc5274b 100644 --- a/src/define/tts/edgeTts.ts +++ b/src/define/tts/edgeTts.ts @@ -17,7 +17,7 @@ type configure = { proxy?: string rate?: string pitch?: string - volume?: string + volumn?: string } export class EdgeTTS { @@ -28,7 +28,7 @@ export class EdgeTTS { private proxy: string | null | undefined private rate: string private pitch: string - private volume: string + private volumn: string constructor({ voice = 'zh-CN-XiaoyiNeural', @@ -38,7 +38,7 @@ export class EdgeTTS { proxy, rate = 'default', pitch = 'default', - volume = 'default' + volumn = 'default' }: configure = {}) { this.voice = voice this.lang = lang @@ -47,7 +47,7 @@ export class EdgeTTS { this.proxy = proxy this.rate = rate this.pitch = pitch - this.volume = volume + this.volumn = volumn } async _connectWebSocket(): Promise { @@ -138,7 +138,9 @@ export class EdgeTTS { end: Math.floor((element['Data']['Offset'] + element['Data']['Duration']) / 10000) }) }) - } catch {} + } catch { + throw new Error('解析元数据失败') + } } } }) @@ -148,7 +150,7 @@ export class EdgeTTS { ` + ` - + ${text} diff --git a/src/main/IPCEvent/bookIpc.js b/src/main/IPCEvent/bookIpc.js index 73fc187..1489854 100644 --- a/src/main/IPCEvent/bookIpc.js +++ b/src/main/IPCEvent/bookIpc.js @@ -1,11 +1,23 @@ import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' -import { ReverseBook } from '../ReverseManage/Book/ReverseBook' -import { BasicReverse } from '../Task/basicReverse' -import { WatermarkAndSubtitle } from '../Task/watermarkAndSubtitle' +import { ReverseBook } from '../Service/Book/ReverseBook' +import { BasicReverse } from '../Service/Book/basicReverse' +import { Subtitle } from '../Service/subtitle' +import { BookBasic } from '../Service/Book/BooKBasic' +import { MJOpt } from '../Service/MJ/mj' +import { BookImage } from '../Service/Book/bookImage' +import { ImageStyle } from '../Service/Book/imageStyle' +import { BookTask } from '../Service/Book/bookTask' +import { BookVideo } from '../Service/Book/bookVideo' let reverseBook = new ReverseBook() let basicReverse = new BasicReverse() -let watermarkAndSubtitle = new WatermarkAndSubtitle() +let subtitle = new Subtitle() +let mjOpt = new MJOpt() +let bookBasic = new BookBasic() +let bookImage = new BookImage() +let imageStyle = new ImageStyle() +let bookTask = new BookTask() +let bookVideo = new BookVideo() export function BookIpc() { // 获取样式图片的子列表 @@ -21,6 +33,16 @@ export function BookIpc() { reverseBook.GetBookData(bookQuery) ) + // 获取小说的所有的数据 + ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TASK_DETAIL, async (event, bookTaskId) => + reverseBook.GetBookTaskDetail(bookTaskId) + ) + + // 保存小说任务的风格 + ipcMain.handle(DEFINE_STRING.BOOK.SAVE_IMAGE_STYLE, async (event, styleList, bookTaskId) => + imageStyle.SaveImageStyle(styleList, bookTaskId) + ) + //#region 一键反推 ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TASK_DATA, async (event, bookTaskCondition) => @@ -39,23 +61,145 @@ export function BookIpc() { // 保存一键反推文案位置 ipcMain.handle(DEFINE_STRING.BOOK.SAVE_BOOK_SUBTITLE_POSITION, async (event, value) => - watermarkAndSubtitle.SaveBookSubtitlePosition(value) + subtitle.SaveBookSubtitlePosition(value) ) // 打开对应的字幕提取的图片文件夹 ipcMain.handle(DEFINE_STRING.BOOK.OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT, async (event, value) => - watermarkAndSubtitle.OpenBookSubtitlePositionScreenshot(value) + subtitle.OpenBookSubtitlePositionScreenshot(value) ) // 获取当前帧的字幕文字 ipcMain.handle(DEFINE_STRING.BOOK.GET_CURRENT_FRAME_TEXT, async (event, value) => - watermarkAndSubtitle.GetCurrentFrameText(value) + subtitle.GetCurrentFrameText(value) ) // 获取当前视频中的所有的字幕 - ipcMain.handle(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, async (event,value)=>{ - watermarkAndSubtitle.GetVideoFrameText(value) + ipcMain.handle(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, async (event, value) => { + subtitle.GetVideoFrameText(value) }) - + + //#endregion + + //#region 一键反推的单个任务 + + // 开始计算分镜 + ipcMain.handle( + DEFINE_STRING.BOOK.COMPUTE_STORYBOARD, + async (event, bookId) => await reverseBook.ComputeStoryboard(bookId) + ) + + // 开始执行分镜,切分视频 + ipcMain.handle( + DEFINE_STRING.BOOK.FRAMING, + async (event, bookId) => await reverseBook.Framing(bookId) + ) + + // 开始执行分镜任务 + ipcMain.handle( + DEFINE_STRING.BOOK.GET_COPYWRITING, + async (event, bookId) => await reverseBook.GetCopywriting(bookId) + ) + + // 执行去除水印 + ipcMain.handle( + DEFINE_STRING.BOOK.REMOVE_WATERMARK, + async (event, bookId) => await reverseBook.RemoveWatermark(bookId) + ) + + // 添加反推任务到任务列表 + ipcMain.handle( + DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT, + async (event, bookTaskDetailIds, type) => + await reverseBook.AddReversePromptTask(bookTaskDetailIds, type) + ) + + // 将反推出来的提示词写入到GPT提示词里面 + ipcMain.handle( + DEFINE_STRING.BOOK.REVERSE_PROMPT_TO_GPT_PROMPT, + async (event, bookId, bookTaskId, index) => + await mjOpt.ReversePromptToGptPrompt(bookId, bookTaskId, index) + ) + + // 重选单个反推提示词到输出种 + ipcMain.handle( + DEFINE_STRING.BOOK.SINGLE_REVERSE_TO_GPT_PROMPT, + async (event, bookTaskDetailId, index) => + await mjOpt.SingleReverseToGptPrompt(bookTaskDetailId, index) + ) + + // 删除所有的反推出来的提示词 + ipcMain.handle( + DEFINE_STRING.BOOK.REMOVE_REVERSE_DATA, + async (event, bookTaskDetailIds) => await reverseBook.RemoveReverseData(bookTaskDetailIds) + ) + + //#endregion + // 对指定的小说任务的图片进行锁定和解锁 + ipcMain.handle( + DEFINE_STRING.BOOK.IMAGE_LOCK_OPERATION, + async (event, id, lockType, operateBookType) => + await bookImage.ImageLockOperation(id, lockType, operateBookType) + ) + + // 下载或者使用指定的图片并裁剪 + ipcMain.handle( + DEFINE_STRING.BOOK.DOWNLOAD_IMAGE_AND_SPLIT, + async (event, bookTaskDetailId, imageUrl) => + await bookImage.DownloadImageUrlAndSplit(bookTaskDetailId, imageUrl) + ) + + // 一拆四,将一个任务拆分成四个任务,并且复制对应的图片 + ipcMain.handle( + DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK, + async (event, bookTaskDetailId) => await bookBasic.OneToFourBookTask(bookTaskDetailId) + ) + + //#region 小说批次任务相关 + + // 重置小说批次数据 + ipcMain.handle( + DEFINE_STRING.BOOK.RESET_BOOK_TASK, + async (event, bookTaskId) => await bookTask.ReSetBookTask(bookTaskId) + ) + + // 删除小说批次数据 + ipcMain.handle( + DEFINE_STRING.BOOK.DELETE_BOOK_TASK, + async (event, bookTaskId) => await bookTask.DeleteBookTask(bookTaskId) + ) + + // 一键生成当前批次的所有图片 + ipcMain.handle( + DEFINE_STRING.BOOK.GENERATE_IMAGE_ALL, + async (event, bookTaskId, imageCategory) => + await bookImage.GenerateImageAll(bookTaskId, imageCategory) + ) + + // 高清图片前检查,主要是检查图片大小 + ipcMain.handle( + DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE, + async (event, id, fileSize, operateBookType) => + await bookImage.CheckImageFileSize(id, fileSize, operateBookType) + ) + + // 开始执行高清图片 + ipcMain.handle( + DEFINE_STRING.BOOK.HD_IMAGE, + async (event, id, scale, operateBookType) => await bookImage.HDImage(id, scale, operateBookType) + ) + + // 将小说视频相关的设置添加到小说任务批次 + ipcMain.handle( + DEFINE_STRING.BOOK.USE_BOOK_VIDEO_DATA_TO_BOOK_TASK, + async (event, id, operateBookType) => + await bookVideo.UseBookVideoDataToBookTask(id, operateBookType) + ) + + // 将小说任务添加到剪映草稿 + ipcMain.handle( + DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, + async (event, id, operateBookType) => await bookVideo.AddJianyingDraft(id, operateBookType) + ) //#endregion } diff --git a/src/main/IPCEvent/dbIpc.ts b/src/main/IPCEvent/dbIpc.ts new file mode 100644 index 0000000..269572e --- /dev/null +++ b/src/main/IPCEvent/dbIpc.ts @@ -0,0 +1,37 @@ +import { ipcMain } from 'electron' +import { DEFINE_STRING } from '../../define/define_string' +import { errorMessage, successMessage } from '../Public/generalTools' +import { Book } from '../../model/book' +import { BookTaskService } from '../../define/db/service/Book/bookTaskService' +import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService' + + +async function DBIpc() { + let bookTaskService = await BookTaskService.getInstance() + let bookTaskDetailService = await BookTaskDetailService.getInstance() + //#region 小说相关的修改 + + // 修改小说任务的数据 + ipcMain.handle(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DATA, async (event, bookTaskId: string, data: Book.SelectBookTask) => { + try { + bookTaskService.UpdetedBookTaskData(bookTaskId, data) + return successMessage(null, "修改小说任务数据成功", "DBIpc_UpdateBookTaskData") + } catch (error) { + return errorMessage("修改小说任务数据失败", "DBIpc_UpdateBookTaskData") + } + }) + + // 修改小说分镜的详细任务数据 + ipcMain.handle(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DETAIL_DATA, async (event, bookTaskDetailId: string, data: Book.SelectBookTaskDetail) => { + try { + bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, data) + return successMessage(null, "修改小说分镜详细任务数据成功", "DBIpc_UpdateBookTaskDetailData") + } catch (error) { + return errorMessage("修改小说分镜详细任务数据失败", "DBIpc_UpdateBookTaskDetailData") + } + }) + + //#endregion + +} +export { DBIpc } diff --git a/src/main/IPCEvent/discordIpc.js b/src/main/IPCEvent/discordIpc.js index 735ad5c..5666ad4 100644 --- a/src/main/IPCEvent/discordIpc.js +++ b/src/main/IPCEvent/discordIpc.js @@ -71,7 +71,6 @@ function WinddowUrlRefresh(thisWindow) { channelID: channelID } }); - } // 判断是不是需要登录(登录提示) diff --git a/src/main/IPCEvent/globalIpc.js b/src/main/IPCEvent/globalIpc.js index 7fb69d6..27ef707 100644 --- a/src/main/IPCEvent/globalIpc.js +++ b/src/main/IPCEvent/globalIpc.js @@ -1,46 +1,51 @@ -import { - ipcMain -} from "electron"; +import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' -import { Tools } from "../tools"; -import path from "path"; -import { errorMessage, successMessage } from "../generalTools"; -let tools = new Tools(); +import { Tools } from '../tools' +import path from 'path' +import { errorMessage, successMessage } from '../Public/generalTools' +let tools = new Tools() function GlobalIpc() { + /** + * 将传入的文件地址修改为base64 + */ + ipcMain.handle(DEFINE_STRING.GET_FILE_BASE64, async (event, value) => { + try { + value = path.normalize(value) + //检查文件或者时文件夹是不是存在 + let isExists = await tools.checkExists(value) + console.log('isExists', value, isExists) + // 获取文件,将其转换为base64 + if (!isExists) { + throw new Error('文件不存在') + } + return successMessage(await tools.readFileBase64(value)) + } catch (error) { + return errorMessage('获取文件失败' + error) + } + }) - /** - * 将传入的文件地址修改为base64 - */ - ipcMain.handle(DEFINE_STRING.GET_FILE_BASE64, async (event, value) => { - try { - value = path.normalize(value) - //检查文件或者时文件夹是不是存在 - let isExists = await tools.checkExists(value); - console.log("isExists", value, isExists); - // 获取文件,将其转换为base64 - if (!isExists) { - throw new Error("文件不存在"); - } - return successMessage(await tools.readFileBase64(value)); - } catch (error) { - return errorMessage("获取文件失败" + error) - } - }); + ipcMain.on(DEFINE_STRING.OPEN_DEV_TOOLS, (event) => { + global.newWindow[0].win.webContents.openDevTools() + }) - ipcMain.on(DEFINE_STRING.OPEN_DEV_TOOLS, (event) => { - global.newWindow[0].win.webContents.openDevTools(); - }) + ipcMain.handle(DEFINE_STRING.OPEN_DEV_TOOLS_PASSWORD, (event, value) => { + if (value === '297ab55d41e9f5d3eba95b9df432f991') { + return successMessage('打开成功') + } else { + return errorMessage('管理控制台密码错误') + } + }) - ipcMain.handle(DEFINE_STRING.OPEN_DEV_TOOLS_PASSWORD, (event, value) => { - if (value === "297ab55d41e9f5d3eba95b9df432f991") { - return successMessage("打开成功") - } else { - return errorMessage("管理控制台密码错误") - } - }) + // 监听打开全局窗口事件 + ipcMain.on(DEFINE_STRING.SHOW_GLOABAL_MESSAGE_DIALOG, (event, value) => { + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, value) + }) + + // 监听打开notification事件 + ipcMain.on(DEFINE_STRING.SHOW_GLOBAL_MAIN_NOTIFICATION, (event, value) => { + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, value) + }) } -export { - GlobalIpc -} \ No newline at end of file +export { GlobalIpc } diff --git a/src/main/IPCEvent/imageIpc.js b/src/main/IPCEvent/imageIpc.js index df0abd0..5f96609 100644 --- a/src/main/IPCEvent/imageIpc.js +++ b/src/main/IPCEvent/imageIpc.js @@ -2,7 +2,7 @@ import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' import { Image } from '../Public/Image' import { LOGGER_DEFINE } from '../../define/logger_define' -import { errorMessage } from '../generalTools' +import { errorMessage } from '../Public/generalTools' let image = new Image(global) function ImageIpc() { diff --git a/src/main/IPCEvent/index.js b/src/main/IPCEvent/index.js index 26aa036..a96a926 100644 --- a/src/main/IPCEvent/index.js +++ b/src/main/IPCEvent/index.js @@ -14,8 +14,9 @@ import { ImageIpc } from './imageIpc.js' import { SystemIpc } from './systemIpc.js' import { BookIpc } from './bookIpc.js' import { TTSIpc } from './ttsIpc.js' +import { DBIpc } from './dbIpc' -export function RegisterIpc(createWindow) { +export async function RegisterIpc(createWindow) { PromptIpc() SettingIpc() ImageGenerateIpc() @@ -24,6 +25,7 @@ export function RegisterIpc(createWindow) { TranslateIpc() GptIpc() SdIpc() + await DBIpc() MjIpc() MainIpc(createWindow) OriginalImageGenerateIpc() diff --git a/src/main/IPCEvent/mjIpc.js b/src/main/IPCEvent/mjIpc.js index f92c720..c52ffe3 100644 --- a/src/main/IPCEvent/mjIpc.js +++ b/src/main/IPCEvent/mjIpc.js @@ -1,87 +1,154 @@ -import { ipcMain } from "electron" -import { DEFINE_STRING } from "../../define/define_string" -import { MjSimple } from "../discord/mjSimple" -import { TagCustomize } from "../Original/TagCustomize" +import { ipcMain } from 'electron' +import { DEFINE_STRING } from '../../define/define_string' +import { MjSimple } from '../discord/mjSimple' +import { TagCustomize } from '../Original/TagCustomize' import { MJOriginalImageGenerate } from '../Original/MJOriginalImageGenerate' -import { PublicMethod } from "../Public/publicMethod" -import { DiscordSimple } from "../discord/discordSimple" -import { Tools } from "../tools" +import { PublicMethod } from '../Public/publicMethod' +import { DiscordSimple } from '../discord/discordSimple' +import { Tools } from '../tools' import path from 'path' +import { MJOpt } from '../Service/MJ/mj' let mjSimple = new MjSimple(global) let discordSimple = new DiscordSimple(null) -let tagCustomize = new TagCustomize(global); -let mJOriginalImageGenerate = new MJOriginalImageGenerate(global); -let publicMethod = new PublicMethod(global); -let tools = new Tools(); - +let tagCustomize = new TagCustomize(global) +let mJOriginalImageGenerate = new MJOriginalImageGenerate(global) +let publicMethod = new PublicMethod(global) +let mjOpt = new MJOpt() function MjIpc() { - // 监听保存mj的文案配置信息 - ipcMain.handle(DEFINE_STRING.MJ.SAVE_WORD_SRT, async (event, value) => await mjSimple.SvaeMJWordSrt(value)); + // 监听保存mj的文案配置信息 + ipcMain.handle( + DEFINE_STRING.MJ.SAVE_WORD_SRT, + async (event, value) => await mjSimple.SvaeMJWordSrt(value) + ) - // 监听获取MJ的文件配置信息 - ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION, async (event, value) => await mjSimple.GetMJConfigSrtInformation(value)); + // 监听获取MJ的文件配置信息 + ipcMain.handle( + DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION, + async (event, value) => await mjSimple.GetMJConfigSrtInformation(value) + ) - // 监听获取标签数据 - ipcMain.handle(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, async (event, value) => await tagCustomize.GetTagDataByTypeAndProperty(value)); + // 监听获取标签数据 + ipcMain.handle( + DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, + async (event, value) => await tagCustomize.GetTagDataByTypeAndProperty(value) + ) - // 保存指定的标签数据 - ipcMain.handle(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.SaveTagPropertyData(value)); + // 保存指定的标签数据 + ipcMain.handle( + DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, + async (event, value) => await tagCustomize.SaveTagPropertyData(value) + ) - // 删除指定的标签数据 - ipcMain.handle(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.DeleteTagPropertyData(value)); + // 删除指定的标签数据 + ipcMain.handle( + DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, + async (event, value) => await tagCustomize.DeleteTagPropertyData(value) + ) - // MJ 原创生图 - ipcMain.handle(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value)); + // MJ 原创生图 + ipcMain.handle( + DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, + async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value) + ) - // 获取discord的频道机器人 - ipcMain.handle(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, async (event, value) => await mjSimple.GetChannelRobots(value)); + // 获取discord的频道机器人 + ipcMain.handle( + DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, + async (event, value) => await mjSimple.GetChannelRobots(value) + ) - // 获取MJ生图的方式 - // GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)), - ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY, async (event) => await mjSimple.GetMJGenerateCategory()); + // 获取MJ生图的方式 + // GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)), + ipcMain.handle( + DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY, + async (event) => await mjSimple.GetMJGenerateCategory() + ) - // MJ生成的图片分割 - ipcMain.handle(DEFINE_STRING.MJ.IMAGE_SPLIT, async (event, value) => await mJOriginalImageGenerate.ImageSplit(value)); + // MJ生成的图片分割 + ipcMain.handle( + DEFINE_STRING.MJ.IMAGE_SPLIT, + async (event, value) => await mJOriginalImageGenerate.ImageSplit(value) + ) - // 添加MJ敏感词 - ipcMain.handle(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, async (event, value) => await mjSimple.AddMJBadPrompt(value)); + // 添加MJ敏感词 + ipcMain.handle( + DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, + async (event, value) => await mjSimple.AddMJBadPrompt(value) + ) - // 添加MJ敏感词检查 - ipcMain.handle(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, async (event, value) => await mjSimple.MJBadPromptCheck(value)); + // 添加MJ敏感词检查 + ipcMain.handle( + DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, + async (event, value) => await mjSimple.MJBadPromptCheck(value) + ) - // 获取已经生图完成的数据,并获取图片 - ipcMain.handle(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.GetGeneratedMJImageAndSplit(value)); + // 获取已经生图完成的数据,并获取图片 + ipcMain.handle( + DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, + async (event, value) => await mJOriginalImageGenerate.GetGeneratedMJImageAndSplit(value) + ) - // 给图片链接,下载指定的图片并分割保存 - ipcMain.handle(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.DownloadImageUrlAndSplit(value)); + // 给图片链接,下载指定的图片并分割保存 + ipcMain.handle( + DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, + async (event, value) => await mJOriginalImageGenerate.DownloadImageUrlAndSplit(value) + ) - // 获取MJ图片的所有的分割尺寸 - ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE, async (event) => await mjSimple.GetMJImageScale()); + // 获取MJ图片的所有的分割尺寸 + ipcMain.handle( + DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE, + async (event) => await mjSimple.GetMJImageScale() + ) - // 获取所有的MJ生图模型 - ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL, async (event) => await mjSimple.GetMJImageRobotModel()); + // 获取所有的MJ生图模型 + ipcMain.handle( + DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL, + async (event) => await mjSimple.GetMJImageRobotModel() + ) - // 自动匹配用户表标签 - ipcMain.handle(DEFINE_STRING.MJ.AUTO_MATCH_USER, async (event, value) => await mJOriginalImageGenerate.AutoMatchUser(value)); + // 自动匹配用户表标签 + ipcMain.handle( + DEFINE_STRING.MJ.AUTO_MATCH_USER, + async (event, value) => await mJOriginalImageGenerate.AutoMatchUser(value) + ) - /** - * 监听DISCORD界面创建消息,并修改数据 - */ - ipcMain.on(DEFINE_STRING.DISCORD.CREATE_MESSAGE, async (event, value) => await discordSimple.DiscordCreateMessage(value)); + /** + * 监听DISCORD界面创建消息,并修改数据 + */ + ipcMain.on( + DEFINE_STRING.DISCORD.CREATE_MESSAGE, + async (event, value) => await discordSimple.DiscordCreateMessage(value) + ) - /** - * 监听DISCORD界面的更新消息,并修改数据 - */ - ipcMain.on(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, async (event, value) => await discordSimple.DiscordUpdateMessage(value)); + /** + * 监听DISCORD界面的更新消息,并修改数据 + */ + ipcMain.on( + DEFINE_STRING.DISCORD.UPDATE_MESSAGE, + async (event, value) => await discordSimple.DiscordUpdateMessage(value) + ) - /** - * 监听DISCORD界面的删除消息,并修改数据 - */ - ipcMain.on(DEFINE_STRING.DISCORD.DELETE_MESSAGE, async (event, value) => await discordSimple.DiscordDeleteMessage(value)); + /** + * 监听DISCORD界面的删除消息,并修改数据 + */ + ipcMain.on( + DEFINE_STRING.DISCORD.DELETE_MESSAGE, + async (event, value) => await discordSimple.DiscordDeleteMessage(value) + ) + + // MJ合并提示词命令 + ipcMain.handle( + DEFINE_STRING.MJ.MJ_MERGE_PROMPT, + async (event, id, mergeType) => await mjOpt.MergePrompt(id, mergeType) + ) + + // MJ出单张图 + ipcMain.handle( + DEFINE_STRING.MJ.ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK, + async (event, id, operateBookType) => await mjOpt.AddMJGenerateImageTask(id, operateBookType) + ) } -export { - MjIpc -} \ No newline at end of file +export { MjIpc } diff --git a/src/main/IPCEvent/promptIpc.js b/src/main/IPCEvent/promptIpc.js index fba6ee7..afb6a52 100644 --- a/src/main/IPCEvent/promptIpc.js +++ b/src/main/IPCEvent/promptIpc.js @@ -1,21 +1,26 @@ -import { ipcMain } from "electron"; +import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' -import { Prompt } from "../Public/Prompt"; -let prompt = new Prompt(); +import { Prompt } from '../Public/Prompt' +let prompt = new Prompt() function PromptIpc() { - // 获取所有的排序选项 - ipcMain.handle(DEFINE_STRING.PROMPT.GET_SORT_OPTIONS, (event) => prompt.GetPromptSortOptions()); + // 获取所有的排序选项 + ipcMain.handle(DEFINE_STRING.PROMPT.GET_SORT_OPTIONS, (event) => prompt.GetPromptSortOptions()) - // 保存提示词排序数据 - ipcMain.handle(DEFINE_STRING.PROMPT.SAVE_PROMPT_SORT_DATA, (event, value) => prompt.SavePromptSort(value)); + // 保存提示词排序数据 + ipcMain.handle(DEFINE_STRING.PROMPT.SAVE_PROMPT_SORT_DATA, (event, value) => + prompt.SavePromptSort(value) + ) - // 获取已经保存的提示词数据 - ipcMain.handle(DEFINE_STRING.PROMPT.GET_PROMPT_SORT_DATA, (event) => prompt.GetPromptSort()) + // 获取已经保存的提示词数据 + ipcMain.handle( + DEFINE_STRING.PROMPT.GET_PROMPT_SORT_DATA, + async (event) => await prompt.GetPromptSort() + ) - // 获取提示词文件数据(txt,指定路径) - ipcMain.handle(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, (event, value) => prompt.OpenPromptFileTxt(value)) + // 获取提示词文件数据(txt,指定路径) + ipcMain.handle(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, (event, value) => + prompt.OpenPromptFileTxt(value) + ) } -export { - PromptIpc -} \ No newline at end of file +export { PromptIpc } diff --git a/src/main/IPCEvent/sdIpc.js b/src/main/IPCEvent/sdIpc.js index 81e41e3..706ec08 100644 --- a/src/main/IPCEvent/sdIpc.js +++ b/src/main/IPCEvent/sdIpc.js @@ -1,24 +1,39 @@ -import { ipcMain } from "electron"; +import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' -import { SD } from "../Public/SD"; -let sd = new SD(global); +import { SD } from '../Public/SD' +import { SDOpt } from '../Service/SD/sd' +let sd = new SD(global) +let sdOpt = new SDOpt() function SdIpc() { - // 获取样式图片的子列表 - ipcMain.handle(DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST, async (event, value) => await sd.GetStyleImageSubList(value)); + // 获取样式图片的子列表 + ipcMain.handle( + DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST, + async (event, value) => await sd.GetStyleImageSubList(value) + ) - // 获取图片样式信息 - ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION, async (event, value) => await sd.GetImageStyleInfomation(value)); + // 获取图片样式信息 + ipcMain.handle( + DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION, + async (event, value) => await sd.GetImageStyleInfomation(value) + ) - // 获取图片样式菜单 - ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu()); + // 获取图片样式菜单 + ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu()) - // 加载当前链接的SD服务数据 - ipcMain.handle(DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, async (event, value) => await sd.LoadSDServiceData(value)); + // 加载当前链接的SD服务数据 + ipcMain.handle( + DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, + async (event, value) => await sd.LoadSDServiceData(value) + ) - // 文生图,单张 - ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value)); + // 文生图,单张 + ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value)) + + // SD合并提示词 + ipcMain.handle( + DEFINE_STRING.SD.SD_MERGE_PROMPT, + async (event, id, mergeType) => await sdOpt.MergePrompt(id, mergeType) + ) } -export { - SdIpc -} \ No newline at end of file +export { SdIpc } diff --git a/src/main/IPCEvent/settingIpc.js b/src/main/IPCEvent/settingIpc.js index 9c20f0f..0e74895 100644 --- a/src/main/IPCEvent/settingIpc.js +++ b/src/main/IPCEvent/settingIpc.js @@ -11,6 +11,8 @@ import { BasicSetting } from '../setting/basicSetting' let basicSetting = new BasicSetting() import { MJSetting } from '../setting/mjSetting' let mjSetting = new MJSetting() +import { Watermark } from '../Service/watermark' +let watermark = new Watermark() async function SettingIpc() { // 获取背景音乐配置列表 @@ -172,46 +174,67 @@ async function SettingIpc() { //#region MJ 设置 // 获取MJ基础设置信息 - ipcMain.handle(DEFINE_STRING.SETTING.GET_MJ_SETTING, async (event, value) => - mjSetting.GetMJSetting(value) + ipcMain.handle( + DEFINE_STRING.SETTING.GET_MJ_SETTING, + async (event, value) => await mjSetting.GetMJSetting(value) ) // 保存MJ的基础设置信息 - ipcMain.handle(DEFINE_STRING.SETTING.UPDATE_MJ_SETTING, async (event, value) => - mjSetting.UpdateMJSetting(value) + ipcMain.handle( + DEFINE_STRING.SETTING.UPDATE_MJ_SETTING, + async (event, value) => await mjSetting.UpdateMJSetting(value) ) // 获取MJ的所有设置 - ipcMain.handle(DEFINE_STRING.SETTING.GET_MJ_SETTING_TREE_DATA, async (event) => - mjSetting.GetMJSettingTreeData() + ipcMain.handle( + DEFINE_STRING.SETTING.GET_MJ_SETTING_TREE_DATA, + async (event) => await mjSetting.GetMJSettingTreeData() ) // 保存MJ的所有设置 - ipcMain.handle(DEFINE_STRING.SETTING.SAVE_MJ_SETTING_TREE_DATA, async (event, value) => - mjSetting.SaveMJSettingTreeData(value) + ipcMain.handle( + DEFINE_STRING.SETTING.SAVE_MJ_SETTING_TREE_DATA, + async (event, value) => await mjSetting.SaveMJSettingTreeData(value) ) // 获取所有的代理MJ信息 - ipcMain.handle(DEFINE_STRING.SETTING.GET_REMOTE_MJ_SETTINGS, async (event) => - mjSetting.GetRemoteMJSettings() + ipcMain.handle( + DEFINE_STRING.SETTING.GET_REMOTE_MJ_SETTINGS, + async (event) => await mjSetting.GetRemoteMJSettings() ) // 创建新的代理MJ信息 - ipcMain.handle(DEFINE_STRING.SETTING.ADD_REMOTE_MJ_SETTING, async (event, value) => - mjSetting.AddRemoteMJSetting(value) + ipcMain.handle( + DEFINE_STRING.SETTING.ADD_REMOTE_MJ_SETTING, + async (event, value) => await mjSetting.AddRemoteMJSetting(value) ) // 修改MJ账号并重连 - ipcMain.handle(DEFINE_STRING.SETTING.UPDATE_REMOTE_MJ_SETTING, async (event, value) => - mjSetting.UpdateRemoteMJSetting(value) + ipcMain.handle( + DEFINE_STRING.SETTING.UPDATE_REMOTE_MJ_SETTING, + async (event, value) => await mjSetting.UpdateRemoteMJSetting(value) ) // 删除指定的MJ账号 - ipcMain.handle(DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING, async (event, value) => - mjSetting.DeleteRemoteMJSetting(value) + ipcMain.handle( + DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING, + async (event, value) => await mjSetting.DeleteRemoteMJSetting(value) ) //#endregion + + //#region 去除水印设置 + + ipcMain.handle( + DEFINE_STRING.SETTING.GET_WATER_MARK_SETTING, + async (event) => await watermark.GetWatermarkSetting() + ) + + ipcMain.handle( + DEFINE_STRING.SETTING.SAVE_WATER_MARK_SETTING, + async (event, value) => await watermark.SaveWatermarkSetting(value) + ) + //#endregion } export { SettingIpc } diff --git a/src/main/IPCEvent/systemIpc.js b/src/main/IPCEvent/systemIpc.js index a1e875f..79f0da8 100644 --- a/src/main/IPCEvent/systemIpc.js +++ b/src/main/IPCEvent/systemIpc.js @@ -1,7 +1,7 @@ import { ipcMain } from "electron"; import { DEFINE_STRING } from '../../define/define_string' import { CheckFileOrDirExist } from "../../define/Tools/file"; -import { errorMessage, successMessage } from "../generalTools"; +import { errorMessage, successMessage } from "../Public/generalTools"; import path from 'path' const { shell } = require('electron') diff --git a/src/main/IPCEvent/translateIpc.js b/src/main/IPCEvent/translateIpc.js index 7c75eda..4fc3720 100644 --- a/src/main/IPCEvent/translateIpc.js +++ b/src/main/IPCEvent/translateIpc.js @@ -1,18 +1,54 @@ -import { ipcMain } from "electron"; +import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' import { Translate } from '../Public/Translate' -let translate = new Translate(global); +let translate = new Translate(global) + +import { TranslateService } from '../Service/Translate/TranslateService' +const translateService = new TranslateService() function TranslateIpc() { - // 监听添加任务到翻译队列的任务 - ipcMain.handle(DEFINE_STRING.TRANSLATE_PROMPT, async (event, value) => await translate.TranslatePrompt(value)); + //#region 要删除的 + // 监听添加任务到翻译队列的任务 + ipcMain.handle( + DEFINE_STRING.TRANSLATE_PROMPT, + async (event, value) => await translate.TranslatePrompt(value) + ) - // 添加立即返回的翻译任务 - ipcMain.handle(DEFINE_STRING.TRANSLATE_RETURN_NOW, async (event, value) => await translate.TranslateReturnNow(value)); + // 添加立即返回的翻译任务 + ipcMain.handle( + DEFINE_STRING.TRANSLATE_RETURN_NOW, + async (event, value) => await translate.TranslateReturnNow(value) + ) - // 添加立即返回的翻译任务到队列中 - ipcMain.handle(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, async (event, value) => await translate.TranslateReturnNowTask(value)); + // 添加立即返回的翻译任务到队列中 + ipcMain.handle( + DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, + async (event, value) => await translate.TranslateReturnNowTask(value) + ) + //#endregion + + // 获取翻译设置 + ipcMain.handle( + DEFINE_STRING.TRANSLATE.GET_TRANSLATE_SETTING, + async () => await translateService.GetTranslateSetting() + ) + + // 重置翻译设置 + ipcMain.handle( + DEFINE_STRING.TRANSLATE.RESET_TRANSLATE_SETTING, + async () => await translateService.ResetTranslateSetting() + ) + + // 保存翻译设置 + ipcMain.handle( + DEFINE_STRING.TRANSLATE.SAVE_TRANSLATE_SETTING, + async (event, value) => await translateService.SaveTranslateSetting(value) + ) + + // 直接返回的翻译任务 + ipcMain.handle( + DEFINE_STRING.TRANSLATE.TRANSLATE_NOW_RETURN, + async (event, value) => await translateService.TranslateNowReturn(value) + ) } -export { - TranslateIpc -} \ No newline at end of file +export { TranslateIpc } diff --git a/src/main/IPCEvent/ttsIpc.js b/src/main/IPCEvent/ttsIpc.js index 95e110d..5bf38f1 100644 --- a/src/main/IPCEvent/ttsIpc.js +++ b/src/main/IPCEvent/ttsIpc.js @@ -1,16 +1,16 @@ import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' import { LOGGER_DEFINE } from '../../define/logger_define' -import { errorMessage } from '../generalTools' -import { TTSSetting } from '../setting/ttsSetting' -const ttsSetting = new TTSSetting() +import { TTS } from '../Service/tts' +const tts = new TTS() export function TTSIpc() { // 获取当前的TTS配置数据 - ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => ttsSetting.GetTTSCOnfig()) + ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => tts.GetTTSCOnfig()) // 保存TTS配置 - ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) => - ttsSetting.SaveTTSConfig(data) - ) + ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) => tts.SaveTTSConfig(data)) + + // 生成音频 + ipcMain.handle(DEFINE_STRING.TTS.GENERATE_AUDIO, async (event, text) => tts.GenerateAudio(text)) } diff --git a/src/main/IPCEvent/writingIpc.js b/src/main/IPCEvent/writingIpc.js index 0d6b189..2d52eb2 100644 --- a/src/main/IPCEvent/writingIpc.js +++ b/src/main/IPCEvent/writingIpc.js @@ -1,6 +1,6 @@ import { ipcMain } from 'electron' import { DEFINE_STRING } from '../../define/define_string' -import { Writing } from '../Task/writing' +import { Writing } from '../Service/writing' let writing = new Writing(global) import { WritingSetting } from '../setting/writeSetting' let writingSetting = new WritingSetting() diff --git a/src/main/Original/MJOriginalImageGenerate.js b/src/main/Original/MJOriginalImageGenerate.js index b47fe06..ca881bf 100644 --- a/src/main/Original/MJOriginalImageGenerate.js +++ b/src/main/Original/MJOriginalImageGenerate.js @@ -9,7 +9,7 @@ import path from 'path' import sharp from 'sharp' import { define } from '../../define/define' import { AwesomeHelp } from 'awesome-js' -import { checkStringValueAddSuffix, errorMessage, successMessage } from '../generalTools' +import { checkStringValueAddSuffix, errorMessage, successMessage } from '../Public/generalTools' import { ImageSetting } from '../../define/setting/imageSetting' import { DiscordAPI } from '../../api/discordApi' import { GPT } from '../Public/GPT' @@ -463,7 +463,7 @@ export class MJOriginalImageGenerate { } let data = { prompt: prompt, - botType: 'MID_JOURNEY', + botType: mjSetting.selectRobot == 'niji' ? 'NIJI_JOURNEY' : 'MID_JOURNEY', accountFilter: { modes: [mj_speed == 'fast' ? 'FAST' : 'RELAX'] } @@ -479,7 +479,7 @@ export class MJOriginalImageGenerate { } let data = { prompt: prompt, - botType: 'MID_JOURNEY', + botType: mjSetting.selectRobot == 'niji' ? 'NIJI_JOURNEY' : 'MID_JOURNEY', accountFilter: { remark: this.global.machineId } diff --git a/src/main/Original/OriginalImageGenerate.js b/src/main/Original/OriginalImageGenerate.js index a34b063..2be2680 100644 --- a/src/main/Original/OriginalImageGenerate.js +++ b/src/main/Original/OriginalImageGenerate.js @@ -1,355 +1,395 @@ - -import { Tools } from "../tools"; -import path from "path"; -import { DEFINE_STRING } from "../../define/define_string"; -import { define } from "../../define/define"; -import { PublicMethod } from "../Public/publicMethod"; -import { SD } from "../Public/SD" -const util = require('util'); -import axios from "axios"; -const sharp = require('sharp'); -const { spawn, exec } = require('child_process'); -const execAsync = util.promisify(exec); -const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符 -let fspromises = require("fs").promises; -import { ImageStyleDefine } from "../../define/iamgeStyleDefine"; - +import { Tools } from '../tools' +import path from 'path' +import { DEFINE_STRING } from '../../define/define_string' +import { define } from '../../define/define' +import { PublicMethod } from '../Public/publicMethod' +import { SD } from '../Public/SD' +const util = require('util') +import axios from 'axios' +const sharp = require('sharp') +const { spawn, exec } = require('child_process') +const execAsync = util.promisify(exec) +const { v4: uuidv4 } = require('uuid') // 引入UUID库来生成唯一标识符 +let fspromises = require('fs').promises +import { ImageStyleDefine } from '../../define/iamgeStyleDefine' export class OriginalImageGenerate { - constructor(global) { - this.global = global; - this.tools = new Tools(); - this.pm = new PublicMethod(global); - this.sd = new SD(global); - } + constructor(global) { + this.global = global + this.tools = new Tools() + this.pm = new PublicMethod(global) + this.sd = new SD(global) + } + /** + * SD原创单张图片生成 + * @param {*} value 传入的参数 0 :原创界面的data数据信息,1:是否需要格式化,2:是否需要全局提示 + */ + async OriginalSDImageGenerate(value) { + try { + let data = value[0] + if (value[1]) { + data = JSON.parse(data) + } + let show_global_message = value[2] + // 判断输出的文件夹路径是不是存在,不存在创建 + let output_crop_path = path.join(this.global.config.project_path, 'tmp/output_crop_1') + // 检查文件是不是存在 + let isE = await this.tools.checkExists(output_crop_path) + if (!isE) { + output_crop_path = path.join(this.global.config.project_path, 'tmp/output_crop_00001') + } + await this.tools.checkFolderExistsOrCreate(output_crop_path) + let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage') + await this.tools.checkFolderExistsOrCreate(SdOriginalImage) + // 获取当前的同用前缀后缀 + let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}])) + let prefix_prompt = config_json.data.prefix_prompt + let suffix_prompt = config_json.data.suffix_prompt + let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE + let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img' + let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')) - /** - * SD原创单张图片生成 - * @param {*} value 传入的参数 0 :原创界面的data数据信息,1:是否需要格式化,2:是否需要全局提示 - */ - async OriginalSDImageGenerate(value) { - try { - let data = value[0]; - if (value[1]) { - data = JSON.parse(data); - } - let show_global_message = value[2]; - // 判断输出的文件夹路径是不是存在,不存在创建 - let output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_1"); - // 检查文件是不是存在 - let isE = await this.tools.checkExists(output_crop_path); - if (!isE) { - output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_00001"); - } - await this.tools.checkFolderExistsOrCreate(output_crop_path); - let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage'); - await this.tools.checkFolderExistsOrCreate(SdOriginalImage); + // 判断当前是不是有开修脸修手 + let ADetailer = { + args: sd_setting.adetailer + } + let seed = sd_setting.setting.seed - // 获取当前的同用前缀后缀 - let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}])); - let prefix_prompt = config_json.data.prefix_prompt; - let suffix_prompt = config_json.data.suffix_prompt; - let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE; - let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img'; - let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); + let style_ids = await this.pm.GetConfigJson(JSON.stringify(['image_style', []]), false) + for (let i = 0; i < data.length; i++) { + const element = data[i] + let adetailer = element.adetailer - // 判断当前是不是有开修脸修手 - let ADetailer = { - args: sd_setting.adetailer - }; - let seed = sd_setting.setting.seed; + let imageJson = JSON.parse( + await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8') + ) + let prompt = sd_setting.webui.prompt + ',' + element.prompt + // 添加前缀 + if (prefix_prompt) { + prompt = prefix_prompt + ',' + prompt + } + // 添加后缀 + if (suffix_prompt) { + prompt = prompt + ',' + suffix_prompt + } + // let prompt = imageJson.webui_config.prompt; + this.global.requestQuene.enqueue( + async () => { + try { + // 开始请求 + let body = { + prompt: prompt, + negative_prompt: imageJson.webui_config.negative_prompt, + seed: seed, + sampler_name: sd_setting.webui.sampler_name, + // 提示词相关性 + cfg_scale: sd_setting.webui.cfg_scale, + width: sd_setting.webui.width, + height: sd_setting.webui.height, + batch_size: sd_setting.setting.batch_size, + n_iter: 1, + steps: imageJson.webui_config.steps, + save_images: false + } - let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false); - for (let i = 0; i < data.length; i++) { - const element = data[i]; - let adetailer = element.adetailer; - - let imageJson = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')); - let prompt = sd_setting.webui.prompt + ',' + element.prompt; - // 添加前缀 - if (prefix_prompt) { - prompt = prefix_prompt + ',' + prompt; + // 判断是不是开启修脸修手 + if (adetailer) { + let ta = { + ADetailer: ADetailer } - // 添加后缀 - if (suffix_prompt) { - prompt = prompt + ',' + suffix_prompt; + body.alwayson_scripts = ta + } + const response = await axios.post(url, body) + let info = JSON.parse(response.data.info) + if (seed == -1) { + seed = info.seed + } + let images = response.data.images + let subImagePath = [] + let out_tmp_image_path = path.join(output_crop_path, `tmp_${element.name}`) + let out_image_path = path.join(output_crop_path, `${element.name}`) + + let input_image = path.join( + this.global.config.project_path, + `tmp/input_crop/${element.name}` + ) + + for (let j = 0; j < images.length; j++) { + const image = images[j] + let imageData = Buffer.from(image.split(',', 1)[0], 'base64') + // 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面) + let image_path = path.join( + this.global.config.project_path, + `data/SdOriginalImage/${element.name.split('.')[0]}_${j}.png` + ) + let tmp_image_path = path.join( + this.global.config.project_path, + `data/SdOriginalImage/tmp_${element.name.split('.')[0]}_${j}.png` + ) + subImagePath.push(image_path) + await sharp(imageData) + .toFile(tmp_image_path) + .then(async () => { + // 生图成功,删除数据 + // 判断原本的图片文件是不是存在,存在删除 + await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path) + }) + .catch((err) => { + throw err + }) + // console.log("文生图成功" + image_path); + + // 将第一个张写出到指定的文件夹中 + if (j == 0) { + await sharp(imageData) + .toFile(out_tmp_image_path) + .then(async () => { + // 生图成功,删除数据 + await this.tools.deletePngAndDeleteExifData( + out_tmp_image_path, + out_image_path + ) + await this.tools.copyFileOrDirectory(out_image_path, input_image) + }) + .catch((err) => { + // console.log(err) + throw err + }) } - // let prompt = imageJson.webui_config.prompt; - this.global.requestQuene.enqueue(async () => { - try { - // 开始请求 - let body = { - "prompt": prompt, - "negative_prompt": imageJson.webui_config.negative_prompt, - "seed": seed, - "sampler_name": sd_setting.webui.sampler_name, - // 提示词相关性 - "cfg_scale": sd_setting.webui.cfg_scale, - "width": sd_setting.webui.width, - "height": sd_setting.webui.height, - "batch_size": sd_setting.setting.batch_size, - "n_iter": 1, - "steps": imageJson.webui_config.steps, - "save_images": false, - } + } + // 将图片的信息写入到config.json文件中 + let index = config_json.data.srt_time_information.findIndex( + (item) => item.id == element.id + ) + if (index < 0) { + throw new Error('没有找到指定的ID,请检查数据') + } + config_json.data.srt_time_information[index].subImagePath = subImagePath + config_json.data.srt_time_information[index].outImagePath = out_image_path + this.global.fileQueue.enqueue(async () => { + await this.pm.SaveConfigJsonProperty([ + config_json.data.srt_time_information, + 'srt_time_information', + false + ]) + }) - // 判断是不是开启修脸修手 - if (adetailer) { - let ta = { - ADetailer: ADetailer - } - body.alwayson_scripts = ta; - } - const response = await axios.post(url, body); - let info = JSON.parse(response.data.info); - if (seed == -1) { - seed = info.seed; - } - let images = response.data.images; - let subImagePath = []; - let out_tmp_image_path = path.join(output_crop_path, `tmp_${element.name}`); - let out_image_path = path.join(output_crop_path, `${element.name}`); - - let input_image = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}`); - - for (let j = 0; j < images.length; j++) { - const image = images[j]; - let imageData = Buffer.from(image.split(",", 1)[0], 'base64'); - // 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面) - let image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/${element.name.split('.')[0]}_${j}.png`); - let tmp_image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/tmp_${element.name.split('.')[0]}_${j}.png`); - subImagePath.push(image_path); - await sharp(imageData) - .toFile(tmp_image_path) - .then(async () => { - // 生图成功,删除数据 - // 判断原本的图片文件是不是存在,存在删除 - await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path); - }).catch(err => { - throw err; - }); - // console.log("文生图成功" + image_path); - - // 将第一个张写出到指定的文件夹中 - if (j == 0) { - await sharp(imageData) - .toFile(out_tmp_image_path) - .then(async () => { - // 生图成功,删除数据 - await this.tools.deletePngAndDeleteExifData(out_tmp_image_path, out_image_path); - await this.tools.copyFileOrDirectory(out_image_path, input_image); - }) - .catch(err => { - // console.log(err) - throw err; - }); - } - } - // 将图片的信息写入到config.json文件中 - let index = config_json.data.srt_time_information.findIndex(item => item.id == element.id); - if (index < 0) { - throw new Error("没有找到指定的ID,请检查数据"); - } - config_json.data.srt_time_information[index].subImagePath = subImagePath; - config_json.data.srt_time_information[index].outImagePath = out_image_path; - this.global.fileQueue.enqueue(async () => { - await this.pm.SaveConfigJsonProperty([config_json.data.srt_time_information, "srt_time_information", false]); - }); - - // 返回数据,用于前台刷新,返回图片数据 - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, { - code: 1, - id: element.id, - data: { - subImagePath: subImagePath, - outImagePath: out_image_path - } - }); - - - } catch (error) { - throw error; - } - - }, `${batch}_${element.name}`, batch); + // 返回数据,用于前台刷新,返回图片数据 + this.global.newWindow[0].win.webContents.send( + DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, + { + code: 1, + id: element.id, + data: { + subImagePath: subImagePath, + outImagePath: out_image_path + } + } + ) + } catch (error) { + throw error } + }, + `${batch}_${element.name}`, + batch + ) + } - this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { - if (failedTasks.length > 0) { - let message = ` + this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` 生图任务都已完成。 但是以下任务执行失败: ` - failedTasks.forEach(({ taskId, error }) => { - message += `${taskId}-, \n 错误信息: ${error}` + '\n'; - }); + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n' + }) - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 0, - message: message - }) - } else { - if (show_global_message) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 1, - message: "所有生图任务完成" - }) - } - } - }); - - - return { - code: 1, - } - - } catch (error) { - return { - code: 0, - message: error.toString() - } + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (show_global_message) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: '所有生图任务完成' + }) + } } + }) + + return { + code: 1 + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - /** - * 自动保存数据到json文件 - * @param {*} value 自动保存数据到json文件 - */ - async AutoSaveDataJson(value) { - try { - // 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀 - value = JSON.parse(value); - let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_SAVE_DATA_JSON; - for (let i = 0; i < value.length; i++) { - const element = value[i]; - // 将修改文件的的方法添加到修改文件队列中 + /** + * 自动保存数据到json文件 + * @param {*} value 自动保存数据到json文件 + */ + async AutoSaveDataJson(value) { + try { + // 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀 + value = JSON.parse(value) + let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_SAVE_DATA_JSON + for (let i = 0; i < value.length; i++) { + const element = value[i] + // 将修改文件的的方法添加到修改文件队列中 - this.global.fileQueue.enqueue(async () => { - try { - if (element.prompt_json) { - let old_json = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')); - old_json.webui_config.prompt = element.prompt; - // old_json.adetailer = element.adetailer; - old_json.chinese_prompt = element.chinese_prompt; - // 前缀提示词 - old_json.prefix_prompt = element.prefix_prompt; - // 后缀提示词 - old_json.suffix_prompt = element.suffix_prompt; - old_json.adetailer = element.adetailer; - old_json.prompt = element.prompt; - await fspromises.writeFile(path.normalize(element.prompt_json), JSON.stringify(old_json)); - } - } catch (error) { - throw new Error(error); - } - }, `${batch}_${element.id}`, batch); - - // 判断是不是有图片。判断图片是不是符合格式(有些格式是file:// 开头的, 以时间结尾(都要删除)) - // 判断是不是有图片 - let file_regex = /^file:\/\//; - if (element.outImagePath && file_regex.test(element.outImagePath)) { - // 删除 "file://" 开头 - element.outImagePath = decodeURI(element.outImagePath); - // 判断element.outImagePath是不是不是以file://开头的,是的话,删除 - if (element.outImagePath.startsWith("file://")) { - element.outImagePath = element.outImagePath.substring(7); - } - element.outImagePath = element.outImagePath.replace(/\?time=.*$/, ''); - // 判断element.outImagePath是不是以/开头的,是的话,删除 - if (element.outImagePath.startsWith("/")) { - element.outImagePath = element.outImagePath.substring(1); - } - } - if (element.subImagePath && element.subImagePath.length > 0) { - for (let j = 0; j < element.subImagePath.length; j++) { - if (file_regex.test(element.subImagePath[j])) { - element.subImagePath[j] = decodeURI(element.subImagePath[j]); - element.subImagePath[j] = element.subImagePath[j].replace(/^file:\/\//, '').replace(/\?time=.*$/, ''); - } - } - } + this.global.fileQueue.enqueue( + async () => { + try { + if (element.prompt_json) { + let old_json = JSON.parse( + await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8') + ) + old_json.webui_config.prompt = element.prompt + // old_json.adetailer = element.adetailer; + old_json.chinese_prompt = element.chinese_prompt + // 前缀提示词 + old_json.prefix_prompt = element.prefix_prompt + // 后缀提示词 + old_json.suffix_prompt = element.suffix_prompt + old_json.adetailer = element.adetailer + old_json.prompt = element.prompt + await fspromises.writeFile( + path.normalize(element.prompt_json), + JSON.stringify(old_json) + ) + } + } catch (error) { + throw new Error(error) } + }, + `${batch}_${element.id}`, + batch + ) - await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "srt_time_information", value, false); - - return { - code: 1 - } - } catch (error) { - return { - code: 0, - message: error.toString() - } + // 判断是不是有图片。判断图片是不是符合格式(有些格式是file:// 开头的, 以时间结尾(都要删除)) + // 判断是不是有图片 + let file_regex = /^file:\/\// + if (element.outImagePath && file_regex.test(element.outImagePath)) { + // 删除 "file://" 开头 + element.outImagePath = decodeURI(element.outImagePath) + // 判断element.outImagePath是不是不是以file://开头的,是的话,删除 + if (element.outImagePath.startsWith('file://')) { + element.outImagePath = element.outImagePath.substring(7) + } + element.outImagePath = element.outImagePath.replace(/\?time=.*$/, '') + // 判断element.outImagePath是不是以/开头的,是的话,删除 + if (element.outImagePath.startsWith('/')) { + element.outImagePath = element.outImagePath.substring(1) + } } - } - - - /** - * 将反推的图片的信息添加到一个json文件中 - */ - async OriginalAddWebuiJson(value) { - try { - let data = JSON.parse(value); - // 判断文件夹是不是存在 - let imput_crop_path = path.join(this.global.config.project_path, "tmp/input_crop"); - let isExist = await this.tools.checkExists(imput_crop_path); - if (!isExist) { - await fspromises.mkdir(imput_crop_path, { recursive: true }) - } - - // 判断当前的数据是不是相同 - // 读取所有txt文件 - let promptJson = await this.tools.getFilesWithExtensions(path.join(global.config.project_path, 'tmp/input_crop'), '.json'); - - // json 已经存在,不做后续处理 - if (data.length == promptJson.length) { - return { - code: 1, - data: path.join(this.global.config.project_path, "tmp/input_crop") - } - } - let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); - for (let i = 0; i < data.length; i++) { - const element = data[i]; - let name = String(element.no).padStart(5, '0') + ".png"; - // console.log(txtpath) - let obj = {} - obj.model = sd_config.setting.type; - obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img'; - obj.name = name; - obj.webui_config = { - sampler_name: sd_config.webui.sampler_name, - prompt: "", - negative_prompt: sd_config.webui.negative_prompt, - batch_size: 1, - steps: sd_config.webui.steps, - cfg_scale: sd_config.webui.cfg_scale, - denoising_strength: sd_config.webui.denoising_strength, - width: sd_config.webui.width, - height: sd_config.webui.height, - seed: sd_config.setting.seed, - init_images: path.normalize(path.join(this.global.config.project_path, "tmp/input_crop/" + name)), - } - obj.adetailer = sd_config.webui.adetailer; - - let file_path = path.join(this.global.config.project_path, "tmp/input_crop/" + name + '.json'); - // 写入 - await fspromises.writeFile(file_path, JSON.stringify(obj)); - } - - return { - code: 1, - data: path.join(this.global.config.project_path, "tmp/input_crop") - } - - } catch (error) { - return { - code: 0, - message: error.toString() + if (element.subImagePath && element.subImagePath.length > 0) { + for (let j = 0; j < element.subImagePath.length; j++) { + if (file_regex.test(element.subImagePath[j])) { + element.subImagePath[j] = decodeURI(element.subImagePath[j]) + element.subImagePath[j] = element.subImagePath[j] + .replace(/^file:\/\//, '') + .replace(/\?time=.*$/, '') } + } } - } + } -} \ No newline at end of file + await this.tools.writeJsonFilePropertyValue( + path.join(this.global.config.project_path, 'scripts/config.json'), + 'srt_time_information', + value, + false + ) + + return { + code: 1 + } + } catch (error) { + return { + code: 0, + message: error.toString() + } + } + } + + /** + * 将反推的图片的信息添加到一个json文件中 + */ + async OriginalAddWebuiJson(value) { + try { + let data = JSON.parse(value) + // 判断文件夹是不是存在 + let imput_crop_path = path.join(this.global.config.project_path, 'tmp/input_crop') + let isExist = await this.tools.checkExists(imput_crop_path) + if (!isExist) { + await fspromises.mkdir(imput_crop_path, { recursive: true }) + } + + // 判断当前的数据是不是相同 + // 读取所有txt文件 + let promptJson = await this.tools.getFilesWithExtensions( + path.join(global.config.project_path, 'tmp/input_crop'), + '.json' + ) + + // json 已经存在,不做后续处理 + if (data.length == promptJson.length) { + return { + code: 1, + data: path.join(this.global.config.project_path, 'tmp/input_crop') + } + } + let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')) + for (let i = 0; i < data.length; i++) { + const element = data[i] + let name = String(element.no).padStart(5, '0') + '.png' + // console.log(txtpath) + let obj = {} + obj.model = sd_config.setting.type + obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img' + obj.name = name + obj.webui_config = { + sampler_name: sd_config.webui.sampler_name, + prompt: '', + negative_prompt: sd_config.webui.negative_prompt, + batch_size: 1, + steps: sd_config.webui.steps, + cfg_scale: sd_config.webui.cfg_scale, + denoising_strength: sd_config.webui.denoising_strength, + width: sd_config.webui.width, + height: sd_config.webui.height, + seed: sd_config.setting.seed, + init_images: path.normalize( + path.join(this.global.config.project_path, 'tmp/input_crop/' + name) + ) + } + obj.adetailer = sd_config.webui.adetailer + + let file_path = path.join( + this.global.config.project_path, + 'tmp/input_crop/' + name + '.json' + ) + // 写入 + await fspromises.writeFile(file_path, JSON.stringify(obj)) + } + + return { + code: 1, + data: path.join(this.global.config.project_path, 'tmp/input_crop') + } + } catch (error) { + return { + code: 0, + message: error.toString() + } + } + } +} diff --git a/src/main/Original/TagCustomize.js b/src/main/Original/TagCustomize.ts similarity index 94% rename from src/main/Original/TagCustomize.js rename to src/main/Original/TagCustomize.ts index cb37102..c34c645 100644 --- a/src/main/Original/TagCustomize.js +++ b/src/main/Original/TagCustomize.ts @@ -1,5 +1,7 @@ import { TagDefine } from "../../define/tagDefine"; export class TagCustomize { + global: any; + tagDefine: TagDefine; constructor(global) { this.global = global; this.tagDefine = new TagDefine(global); diff --git a/src/main/Public/GPT.js b/src/main/Public/GPT.js index f2c0c31..912e508 100644 --- a/src/main/Public/GPT.js +++ b/src/main/Public/GPT.js @@ -5,7 +5,7 @@ import { define } from "../../define/define"; let fspromises = require("fs").promises; import { gptDefine } from "../../define/gptDefine"; import { apiUrl } from "../../define/api/apiUrlDefine"; -import { successMessage } from "../generalTools"; +import { successMessage } from "../Public/generalTools"; export class GPT { constructor(global) { @@ -282,8 +282,7 @@ export class GPT { url: gpt_url, headers: { 'Authorization': `Bearer ${gpt_key}`, - 'Content-Type': 'application/json', - "Accept": "application/json" + 'Content-Type': 'application/json' }, data: JSON.stringify(data) }; diff --git a/src/main/Public/Image.js b/src/main/Public/Image.js index 4f3ee3d..04f42f3 100644 --- a/src/main/Public/Image.js +++ b/src/main/Public/Image.js @@ -1,335 +1,378 @@ -import { errorMessage, successMessage } from "../generalTools"; -import path, { resolve } from "path"; -import { Tools } from "../tools"; -import fs from "fs"; -import { ImageSetting } from "../../define/setting/imageSetting"; -import { isEmpty } from "lodash"; -import { basicApi } from "../../api/apiBasic"; -import sharp from 'sharp'; -import { file, image } from "../../define/Tools"; -import { define } from "../../define/define"; +import { errorMessage, successMessage } from './generalTools' +import path from 'path' +import { Tools } from '../tools' +import fs from 'fs' +import { ImageSetting } from '../../define/setting/imageSetting' +import { isEmpty } from 'lodash' +import { basicApi } from '../../api/apiBasic' +import { file, image } from '../../define/Tools' +import { define } from '../../define/define' import { spawn } from 'child_process' -import { LOGGER_DEFINE } from "../../define/logger_define"; -import { DEFINE_STRING } from "../../define/define_string"; +import { LOGGER_DEFINE } from '../../define/logger_define' +import { DEFINE_STRING } from '../../define/define_string' export class Image { - constructor(global) { - this.global = global; - this.tools = new Tools(); - } + constructor(global) { + this.global = global + this.tools = new Tools() + } - // 将指定的文件夹复制到四个文件夹中 - async OneSplitFour(value) { - try { - value = JSON.parse(value); - let count = value[1]; - let data = value[0]; - // 先创建输出文件 - if (count <= 1) { - throw new Error("可选择的图片的数量必须大于1"); - } - for (let i = 1; i < count; i++) { - let out_folder = path.join(this.global.config.project_path, `tmp/output_crop_0000${i + 1}`); - // 判断当前的文件夹是不是存在,存在删除 - let isH = await this.tools.checkExists(out_folder); - if (isH) { - await this.tools.deleteFileOrDirectory(out_folder); - } - await this.tools.checkFolderExistsOrCreate(out_folder) - } - for (let i = 0; i < data.length; i++) { - const element = data[i]; - let subImagePath = element.subImagePath; - for (let j = 1; j < count; j++) { - let out_file = path.join(this.global.config.project_path, `tmp/output_crop_0000${j + 1}/${element.name}`); - if (subImagePath[j] && subImagePath[j].startsWith("file")) { - subImagePath[j] = subImagePath[j].replace("file://", ""); - subImagePath[j] = subImagePath[j].replace(/\?time=.*$/, ''); - } - await this.tools.copyFileOrDirectory(subImagePath[j], out_file); - } - } - return successMessage("拆分成功"); - - } catch (error) { - return errorMessage(error.message); + // 将指定的文件夹复制到四个文件夹中 + async OneSplitFour(value) { + try { + value = JSON.parse(value) + let count = value[1] + let data = value[0] + // 先创建输出文件 + if (count <= 1) { + throw new Error('可选择的图片的数量必须大于1') + } + for (let i = 1; i < count; i++) { + let out_folder = path.join(this.global.config.project_path, `tmp/output_crop_0000${i + 1}`) + // 判断当前的文件夹是不是存在,存在删除 + let isH = await this.tools.checkExists(out_folder) + if (isH) { + await this.tools.deleteFileOrDirectory(out_folder) } - } - - /** - * 将base64转换为文件 - * @param {*} value - * @returns - */ - async Base64ToFile(value) { - try { - value = JSON.parse(value); - let base64 = value[0]; - let out_file_str = value[1]; - let base64Data = base64.replace(/^data:image\/\w+;base64,/, ""); - let dataBuffer = Buffer.from(base64Data, 'base64'); - let out_file = path.join(this.global.config.project_path, out_file_str); - let out_folder = path.dirname(out_file); - await this.tools.checkFolderExistsOrCreate(out_folder); - - await fs.promises.writeFile(out_file, dataBuffer); - // await this.tools.writeArrayToFile(dataBuffer, out_file); - return successMessage(out_file, "base64保存到本地图片成功"); - } catch (error) { - return errorMessage("base64保存到本地图片失败,失败原因如下:" + error.message); + await this.tools.checkFolderExistsOrCreate(out_folder) + } + for (let i = 0; i < data.length; i++) { + const element = data[i] + let subImagePath = element.subImagePath + for (let j = 1; j < count; j++) { + let out_file = path.join( + this.global.config.project_path, + `tmp/output_crop_0000${j + 1}/${element.name}` + ) + if (subImagePath[j] && subImagePath[j].startsWith('file')) { + subImagePath[j] = subImagePath[j].replace('file://', '') + subImagePath[j] = subImagePath[j].replace(/\?time=.*$/, '') + } + await this.tools.copyFileOrDirectory(subImagePath[j], out_file) } + } + return successMessage('拆分成功') + } catch (error) { + return errorMessage(error.message) } + } - /** - * 图片处理,去除水印 - * @param {*} value - * @returns - */ - async ProcessImage(value) { - return new Promise(async (resolve, reject) => { - try { - value = JSON.parse(value); - value.out_file = value.out_file ? value.out_file : `data/mask/temp/${new Date().getTime()}.png`; - // 判断当前使用的是什么 - let mask_setting = (await ImageSetting.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mask_setting", false, {}]))).data; - let isRemote = mask_setting.isRemote ? mask_setting.isRemote : false; - let urls = mask_setting.localUrl ? mask_setting.localUrl + "api/v1/inpaint" : ""; + /** + * 将base64转换为文件 + * @param {*} value + * @returns + */ + async Base64ToFile(value) { + try { + value = JSON.parse(value) + let base64 = value[0] + let out_file_str = value[1] + let base64Data = base64.replace(/^data:image\/\w+;base64,/, '') + let dataBuffer = Buffer.from(base64Data, 'base64') + let out_file = path.join(this.global.config.project_path, out_file_str) + let out_folder = path.dirname(out_file) + await this.tools.checkFolderExistsOrCreate(out_folder) + await fs.promises.writeFile(out_file, dataBuffer) + // await this.tools.writeArrayToFile(dataBuffer, out_file); + return successMessage(out_file, 'base64保存到本地图片成功') + } catch (error) { + return errorMessage('base64保存到本地图片失败,失败原因如下:' + error.message) + } + } - if (isRemote && isEmpty(urls)) { - throw new Error("使用iopaint图片处理,但是没有配置图片处理地址"); - } - if (!isRemote && isEmpty(value.out_file)) { - throw new Error("水印处理,使用软件直接处理类型为file,需要指定输出的文件地址"); - } + // TODO 这个方法后面还要改,现在有问题(直接重写,后面这个重构掉,先重写一个) + /** + * 图片处理,去除水印 + * @param {*} value + * @returns + */ + async ProcessImage(value) { + return new Promise(async (resolve, reject) => { + try { + value = JSON.parse(value) + value.out_file = value.out_file + ? value.out_file + : `data/mask/temp/${new Date().getTime()}.png` + // 判断当前使用的是什么 + let mask_setting = ( + await ImageSetting.GetDefineConfigJsonByProperty( + JSON.stringify(['img_base', 'mask_setting', false, {}]) + ) + ).data + let isRemote = mask_setting.isRemote ? mask_setting.isRemote : false + let urls = mask_setting.localUrl ? mask_setting.localUrl + 'api/v1/inpaint' : '' - let out_file; - if (!isEmpty(value.out_file)) { - out_file = path.join(this.global.config.project_path, value.out_file); - let out_folder = path.dirname(out_file); - await this.tools.checkFolderExistsOrCreate(out_folder); - } + if (isRemote && isEmpty(urls)) { + throw new Error('使用iopaint图片处理,但是没有配置图片处理地址') + } + if (!isRemote && isEmpty(value.out_file)) { + throw new Error('水印处理,使用软件直接处理类型为file,需要指定输出的文件地址') + } - let res; - if (isRemote) { - let headers = { - "accept": '*/*', - 'accept-language': 'zh-CN,zh;q=0.9', - 'content-type': 'application/json' - } - let data = { - "image": value.image, - "mask": value.mask, - "ldm_steps": 30, - "ldm_sampler": "ddim", - "zits_wireframe": true, - "cv2_flag": "INPAINT_NS", - "cv2_radius": 5, - "hd_strategy": "Crop", - "hd_strategy_crop_triger_size": 640, - "hd_strategy_crop_margin": 128, - "hd_trategy_resize_imit": 2048 * 5, - "prompt": "", - "negative_prompt": "out of frame, lowres, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, disfigured, gross proportions, malformed limbs, watermark, signature", - "use_croper": false, - "croper_x": 284, - "croper_y": 284, - "croper_height": 512, - "croper_width": 512, - "use_extender": false, - "extender_x": 0, - "extender_y": 0, - "extender_height": 1080, - "extender_width": 1080, - "sd_mask_blur": 12, - "sd_strength": 1, - "sd_steps": 50, - "sd_guidance_scale": 7.5, - "sd_sampler": "DPM++ 2M", - "sd_seed": -1, - "sd_match_histograms": false, - "sd_lcm_lora": false, - "paint_by_example_example_image": null, - "p2p_image_guidance_scale": 1.5, - "enable_controlnet": false, - "controlnet_conditioning_scale": 0.4, - "controlnet_method": "", - "enable_brushnet": false, - "brushnet_method": "random_mask", - "brushnet_conditioning_scale": 1, - "enable_powerpaint_v2": false, - "powerpaint_task": "text-guided" - }; - res = await basicApi.post(urls, data, headers); - this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `iopaint去除水印请求成功`); - if (value.type == 'arrayBuffer') { - resolve(successMessage(res.data, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); - } else if (value.type == "file") { - let buffer = Buffer.from(res.data); - await fs.promises.writeFile(out_file, buffer) - resolve(successMessage(out_file, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); - } + let out_file + if (!isEmpty(value.out_file)) { + out_file = path.join(this.global.config.project_path, value.out_file) + let out_folder = path.dirname(out_file) + await this.tools.checkFolderExistsOrCreate(out_folder) + } - } else { - let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`); - // 就是判断指定的文件和文件夹是不是存在 - let has_exe = await file.CheckFileOrDirExist(lama_script); - if (!has_exe) { - throw new Error("图片水印处理组件不存在,请看教程自行下载"); - } - let has_model = await file.CheckFileOrDirExist(path.resolve(define.scripts_path, 'lama/model/big-lama.pt')) - if (!has_model) { - throw new Error("图片水印处理的模型不存在,请看教程自行下载") - } + let res + if (isRemote) { + let headers = { + accept: '*/*', + 'accept-language': 'zh-CN,zh;q=0.9', + 'content-type': 'application/json' + } + let data = { + image: value.image, + mask: value.mask, + ldm_steps: 30, + ldm_sampler: 'ddim', + zits_wireframe: true, + cv2_flag: 'INPAINT_NS', + cv2_radius: 5, + hd_strategy: 'Crop', + hd_strategy_crop_triger_size: 640, + hd_strategy_crop_margin: 128, + hd_trategy_resize_imit: 2048 * 5, + prompt: '', + negative_prompt: + 'out of frame, lowres, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, disfigured, gross proportions, malformed limbs, watermark, signature', + use_croper: false, + croper_x: 284, + croper_y: 284, + croper_height: 512, + croper_width: 512, + use_extender: false, + extender_x: 0, + extender_y: 0, + extender_height: 1080, + extender_width: 1080, + sd_mask_blur: 12, + sd_strength: 1, + sd_steps: 50, + sd_guidance_scale: 7.5, + sd_sampler: 'DPM++ 2M', + sd_seed: -1, + sd_match_histograms: false, + sd_lcm_lora: false, + paint_by_example_example_image: null, + p2p_image_guidance_scale: 1.5, + enable_controlnet: false, + controlnet_conditioning_scale: 0.4, + controlnet_method: '', + enable_brushnet: false, + brushnet_method: 'random_mask', + brushnet_conditioning_scale: 1, + enable_powerpaint_v2: false, + powerpaint_task: 'text-guided' + } + res = await basicApi.post(urls, data, headers) + this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `iopaint去除水印请求成功`) + if (value.type == 'arrayBuffer') { + resolve(successMessage(res.data, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK)) + } else if (value.type == 'file') { + let buffer = Buffer.from(res.data) + await fs.promises.writeFile(out_file, buffer) + resolve(successMessage(out_file, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK)) + } + } else { + let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`) + // 就是判断指定的文件和文件夹是不是存在 + let has_exe = await file.CheckFileOrDirExist(lama_script) + if (!has_exe) { + throw new Error('图片水印处理组件不存在,请看教程自行下载') + } + let has_model = await file.CheckFileOrDirExist( + path.resolve(define.scripts_path, 'lama/model/big-lama.pt') + ) + if (!has_model) { + throw new Error('图片水印处理的模型不存在,请看教程自行下载') + } - this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `开始使用lama去除水印,开始调用lama程序`); - // 先将对应的base64文件写道本地 - let image_path = await this.Base64ToFile(JSON.stringify([value.image, `data/mask/temp/${new Date().getTime()}.png`])) - let mask_path = await this.Base64ToFile(JSON.stringify([value.mask, `data/mask/mask_temp_${new Date().getTime()}.png`])) - if (image_path.code == 0) { - throw new Error(image_path.message) - } - if (mask_path.code == 0) { - throw new Error(mask_path.message) - } + this.global.logger.info( + LOGGER_DEFINE.REMOVE_WATERMARK, + `开始使用lama去除水印,开始调用lama程序` + ) + // 先将对应的base64文件写道本地 + let image_path = await this.Base64ToFile( + JSON.stringify([value.image, `data/mask/temp/${new Date().getTime()}.png`]) + ) + let mask_path = await this.Base64ToFile( + JSON.stringify([value.mask, `data/mask/mask_temp_${new Date().getTime()}.png`]) + ) + if (image_path.code == 0) { + throw new Error(image_path.message) + } + if (mask_path.code == 0) { + throw new Error(mask_path.message) + } - let child = spawn(lama_script, ['-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' }); - // let child = spawn('python', [lama_script, '-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' }); - child.on('error', (error) => { - reject(error.toString()) - return - }) + let child = spawn(lama_script, ['-l', image_path.data, mask_path.data, out_file], { + encoding: 'utf-8' + }) + // let child = spawn('python', [lama_script, '-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' }); + child.on('error', (error) => { + reject(error.toString()) + return + }) - child.stdout.on('data', (data) => { - console.log(data.toString()) - }) + child.stdout.on('data', (data) => { + console.log(data.toString()) + }) - child.stderr.on('data', (data) => { - reject(data.toString()) - return - }) + child.stderr.on('data', (data) => { + reject(data.toString()) + return + }) - child.on('close', async (data) => { - if (data != 0) { - this.global.logger.error(LOGGER_DEFINE.REMOVE_WATERMARK, `lama去除水印失败,错误码:${data.toString()}`); - reject("lama去除水印错误。请看日志详细信息!") - return - } - // 判断是不是有输出文件 - let has_out = await file.CheckFileOrDirExist(out_file); - if (!has_out) { - reject("lama去除水印失败,没有输出文件") - return - } - - if (value.type == 'arrayBuffer') { - // 读取导出的文件 - let res_data = await fs.promises.readFile(out_file); - resolve(successMessage(res_data, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); - } else if (value.type == "file") { - resolve(successMessage(out_file, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); - } - }) - } - } catch (error) { - reject("图片处理失败,失败原因如下:" + error.message) + child.on('close', async (data) => { + if (data != 0) { + this.global.logger.error( + LOGGER_DEFINE.REMOVE_WATERMARK, + `lama去除水印失败,错误码:${data.toString()}` + ) + reject('lama去除水印错误。请看日志详细信息!') + return } + // 判断是不是有输出文件 + let has_out = await file.CheckFileOrDirExist(out_file) + if (!has_out) { + reject('lama去除水印失败,没有输出文件') + return + } + + if (value.type == 'arrayBuffer') { + // 读取导出的文件 + let res_data = await fs.promises.readFile(out_file) + resolve(successMessage(res_data, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK)) + } else if (value.type == 'file') { + resolve(successMessage(out_file, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK)) + } + }) + } + } catch (error) { + reject('图片处理失败,失败原因如下:' + error.message) + } + }) + } + + // TODO 该方法后面需删除,使用ProcessImage方法 + // 批量处理所有的图片,去除水印 + async BatchProcessImage(value) { + try { + let input_folder = value + input_folder = path.resolve(this.global.config.project_path, input_folder) + if (!(await this.tools.checkExists(input_folder))) { + throw new Error('输入的文件夹不存在') + } + + let new_input_folder = path.join( + this.global.config.project_path, + `tmp/bak/${path.basename(input_folder)}` + ) + + // 在备份之前判断,旧的文件是不是存在,里面是不是有图片,没有图片,直接删除,在备份过去,存在直接开始下面的请求 + let has_files = await file.CheckFileOrDirExist(new_input_folder) + if (has_files) { + let files = await file.GetFilesWithExtensions(new_input_folder, ['.png']) + if (files.length <= 0) { + // 删除指定的文件夹 + await fs.promises.rm(new_input_folder, { recursive: true }) + await file.BackupFileOrFolder(input_folder, new_input_folder) + // 创建新的input_folder + await fs.promises.mkdir(input_folder, { recursive: true }) + } + } else { + await file.BackupFileOrFolder(input_folder, new_input_folder) + // 创建新的input_folder + await fs.promises.mkdir(input_folder, { recursive: true }) + } + // 开始备份 + + // 获取蒙板 + let mask_setting = ( + await ImageSetting.GetDefineConfigJsonByProperty( + JSON.stringify(['img_base', 'mask_setting', false, {}]) + ) + ).data + if (mask_setting.isRemote && isEmpty(mask_setting.localUrl)) { + throw new Error('使用iopaint图片处理,但是没有配置图片处理地址') + } + if (isEmpty(mask_setting.mask_path)) { + throw new Error('没有配置蒙板的路径') + } + + // 获取文件夹里面所有的图片文件 + // let png_files = await this.tools.getFilesWithExtensions(input_folder, '.png'); + let png_files = await file.GetFilesWithExtensions(new_input_folder, ['.png']) + if (png_files.length == 0) { + throw new Error('没有找到任何的抽帧图片文件') + } + + // 获取图片的总数,将数据返回前端,更新进度条 + this.global.newWindow[0].win.webContents.send( + DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, + successMessage({ + total: png_files.length, + current: 0 }) - } + ) - // 批量处理所有的图片,去除水印 - async BatchProcessImage(value) { - try { - let input_folder = value; - input_folder = path.resolve(this.global.config.project_path, input_folder) - if (!(await this.tools.checkExists(input_folder))) { - throw new Error("输入的文件夹不存在"); - } + // 默认所有的的宽高都是一样的,获取第一张图片的宽高 + let first_image = png_files[0] + let first_image_size = await image.GetImageSize(first_image) - let new_input_folder = path.join(this.global.config.project_path, `tmp/bak/${path.basename(input_folder)}`); + // 重新设置蒙板的宽高,和图片的宽高一样 + let mask_base = await image.ResizeImage( + mask_setting.mask_path, + first_image_size.width, + first_image_size.height, + 'base64' + ) + mask_base = `data:image/png;base64,${mask_base}` - // 在备份之前判断,旧的文件是不是存在,里面是不是有图片,没有图片,直接删除,在备份过去,存在直接开始下面的请求 - let has_files = await file.CheckFileOrDirExist(new_input_folder); - if (has_files) { - let files = await file.GetFilesWithExtensions(new_input_folder, ['.png']); - if (files.length <= 0) { - // 删除指定的文件夹 - await fs.promises.rm(new_input_folder, { recursive: true }); - await file.BackupFileOrFolder(input_folder, new_input_folder); - // 创建新的input_folder - await fs.promises.mkdir(input_folder, { recursive: true }) - } - } else { - await file.BackupFileOrFolder(input_folder, new_input_folder); - // 创建新的input_folder - await fs.promises.mkdir(input_folder, { recursive: true }) - } - // 开始备份 + // 开始处理所有的图片 + for (let i = 0; i < png_files.length; i++) { + const element = png_files[i] + // 获取指定的图片,并且转换为base64 + let image_base = await fs.promises.readFile(element) + image_base = image_base.toString('base64') + image_base = `data:image/png;base64,${image_base}` + // 开始处理图片 + let res = await this.ProcessImage( + JSON.stringify({ + image: image_base, + mask: mask_base, + type: 'file', + out_file: `tmp/input_crop/${path.basename(element)}` + }) + ) - // 获取蒙板 - let mask_setting = (await ImageSetting.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mask_setting", false, {}]))).data; - if (mask_setting.isRemote && isEmpty(mask_setting.localUrl)) { - throw new Error("使用iopaint图片处理,但是没有配置图片处理地址"); - } - if (isEmpty(mask_setting.mask_path)) { - throw new Error("没有配置蒙板的路径"); - } - - // 获取文件夹里面所有的图片文件 - // let png_files = await this.tools.getFilesWithExtensions(input_folder, '.png'); - let png_files = await file.GetFilesWithExtensions(new_input_folder, ['.png']); - if (png_files.length == 0) { - throw new Error("没有找到任何的抽帧图片文件"); - } - - // 获取图片的总数,将数据返回前端,更新进度条 - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, successMessage({ - total: png_files.length, - current: 0 - })) - - // 默认所有的的宽高都是一样的,获取第一张图片的宽高 - let first_image = png_files[0]; - let first_image_size = await image.GetImageSize(first_image); - - // 重新设置蒙板的宽高,和图片的宽高一样 - let mask_base = await image.ResizeImage(mask_setting.mask_path, first_image_size.width, first_image_size.height, 'base64'); - mask_base = `data:image/png;base64,${mask_base}`; - - // 开始处理所有的图片 - for (let i = 0; i < png_files.length; i++) { - const element = png_files[i]; - // 获取指定的图片,并且转换为base64 - let image_base = await fs.promises.readFile(element); - image_base = image_base.toString('base64'); - image_base = `data:image/png;base64,${image_base}`; - - // 开始处理图片 - let res = await this.ProcessImage(JSON.stringify({ - image: image_base, - mask: mask_base, - type: "file", - out_file: `tmp/input_crop/${path.basename(element)}` - })) - - if (res.code == 0) { - throw new Error(res.message); - } - - // 删除之前的 - await this.tools.deleteFileOrDirectory(element) - await this.tools.delay(1000) - // 当前图片处理成功,将信息返回前端,更新进度条 - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, successMessage({ - total: png_files.length, - current: i + 1 - })) - } - - return successMessage("所有的图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK); - - } catch (error) { - return errorMessage("图片处理失败,失败原因如下:" + error.message, LOGGER_DEFINE.REMOVE_WATERMARK); + if (res.code == 0) { + throw new Error(res.message) } + + // 删除之前的 + await this.tools.deleteFileOrDirectory(element) + await this.tools.delay(1000) + // 当前图片处理成功,将信息返回前端,更新进度条 + this.global.newWindow[0].win.webContents.send( + DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, + successMessage({ + total: png_files.length, + current: i + 1 + }) + ) + } + + return successMessage('所有的图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK) + } catch (error) { + return errorMessage( + '图片处理失败,失败原因如下:' + error.message, + LOGGER_DEFINE.REMOVE_WATERMARK + ) } -} \ No newline at end of file + } +} diff --git a/src/main/Public/Prompt.js b/src/main/Public/Prompt.js index e66b0a3..9e06e93 100644 --- a/src/main/Public/Prompt.js +++ b/src/main/Public/Prompt.js @@ -1,4 +1,4 @@ -import { errorMessage, successMessage } from "../generalTools"; +import { errorMessage, successMessage } from "../Public/generalTools"; import { LOGGER_DEFINE } from '../../define/logger_define' import { Setting } from "../setting/setting"; import { isEmpty } from "lodash"; diff --git a/src/main/Public/SD.js b/src/main/Public/SD.js index b2b7aff..aa6754d 100644 --- a/src/main/Public/SD.js +++ b/src/main/Public/SD.js @@ -9,7 +9,7 @@ const sharp = require('sharp'); import { SdSettingDefine } from "../../define/setting/sdSettingDefine"; import { PublicMethod } from "./publicMethod"; import { Tools } from "../tools"; -import { errorMessage, successMessage } from "../generalTools"; +import { errorMessage, successMessage } from "../Public/generalTools"; import { SdApi } from "../../api/sdApi"; const { v4: uuidv4 } = require('uuid'); diff --git a/src/main/Public/Translate.js b/src/main/Public/Translate.js index efe2235..617d70f 100644 --- a/src/main/Public/Translate.js +++ b/src/main/Public/Translate.js @@ -1,1125 +1,1203 @@ -import { DEFINE_STRING } from "../../define/define_string"; -const tencentcloud = require("tencentcloud-sdk-nodejs") -import { define } from "../../define/define"; -import { MD5 } from "crypto-js"; -import axios from "axios"; -import path from "path"; -import { Tools } from "../tools"; -const alimt20181012 = require('@alicloud/alimt20181012'); -const OpenApi = require('@alicloud/openapi-client'); -const Util = require('@alicloud/tea-util'); -let fspromises = require("fs").promises; +import { DEFINE_STRING } from '../../define/define_string' +const tencentcloud = require('tencentcloud-sdk-nodejs') +import { define } from '../../define/define' +import { MD5 } from 'crypto-js' +import axios from 'axios' +import path from 'path' +import { Tools } from '../tools' +const alimt20181012 = require('@alicloud/alimt20181012') +const OpenApi = require('@alicloud/openapi-client') +const Util = require('@alicloud/tea-util') +let fspromises = require('fs').promises +import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' +import { isEmpty } from 'lodash' +import {ValidateJson} from '../../define/Tools/validate' -let { - Signer -} = require('@volcengine/openapi'); +let { Signer } = require('@volcengine/openapi') export class Translate { - constructor(global) { - this.global = global; - this.tools = new Tools(); + constructor(global) { + this.global = global + this.tools = new Tools() + } + + /** + * 初始化翻译设置 + */ + async InitTranslate() { + if (!this.softwareService) { + this.softwareService = await SoftwareService.getInstance() } + // 获取翻译设置 + if (!this.translationBusiness || !this.translationAppId || !this.translationSecret) { + let translateSetting = this.softwareService.GetSoftWarePropertyData('translationSetting') + if (isEmpty(translateSetting)) { + throw new Error('翻译设置为空,请先设置') + } + let tryParse = ValidateJson(translateSetting) + if (!tryParse) { + throw new Error('翻译设置的格式错误,请重置后重新添加') + } + let translateSettingData = JSON.parse(translateSetting) + let selectModel = translateSettingData.selectModel + let translateIndex = translateSettingData.translates.findIndex( + (item) => item.name == selectModel + ) + if (translateIndex < 0) { + throw new Error('没有找到对应的翻译API设置') + } + let translateData = translateSettingData.translates[translateIndex] + for (const key in translateData) { + if (!translateData[key]) { + throw new Error(`翻译设置中的 ${key} 不能为空`) + } + } + this.translationBusiness = translateData.translation_business + this.translationAppId = translateData.translation_app_id + this.translationSecret = translateData.translation_secret + } + } - /** - * 将当前的翻译任务添加到队列中 - * @param {*} value - * 0 第 0 个参数是要翻译的数据(数组) - * 1 第 1 个参数是源语言 - * 2 第 2 个参数是目标语言 - * 3 第 3 个参数是否分割(默认不分割false) - * 4 第 4 个参数是要不要全局弹窗提示 - * @returns - */ - async TranslateReturnNowTask(value) { - try { + /** + * 将当前的翻译任务添加到队列中 + * @param {*} value + * 0 第 0 个参数是要翻译的数据(数组) + * 1 第 1 个参数是源语言 + * 2 第 2 个参数是目标语言 + * 3 第 3 个参数是否分割(默认不分割false) + * 4 第 4 个参数是要不要全局弹窗提示 + * @returns + */ + async TranslateReturnNowTask(value) { + try { + await this.InitTranslate() + value = JSON.parse(value) + let data = value[0] + let to = value[2] + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_RETURN_NOW_TASK + for (let i = 0; i < data.length; i++) { + const element = data[i] + // 添加任务到队列 + this.global.requestQuene.enqueue( + async () => { + try { + let res = await this.TranslateReturnNow([ + element.gpt_prompt, + value[1], + to, + value[3], + value[4] + ]) + if (res.code != 1) { + throw new Error(res.message) + } + let res_p = null - value = JSON.parse(value); - let data = value[0]; - let to = value[2]; - let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_RETURN_NOW_TASK; - for (let i = 0; i < data.length; i++) { - const element = data[i]; - // 添加任务到队列 - this.global.requestQuene.enqueue(async () => { - try { - let res = await this.TranslateReturnNow([element.gpt_prompt, value[1], to, value[3], value[4]]); - if (res.code != 1) { - throw new Error(res.message); - } - let res_p = null; + if (!value[3]) { + if (to == 'zh') { + res_p = res.data.map((item) => item.dst).join(',') + } else { + res_p = res.data.map((item) => item.src).join(',') + } + } else { + res_p = res.data + } - if (!value[3]) { - if (to == "zh") { - res_p = res.data.map(item => item.dst).join(","); - } else { - res_p = res.data.map(item => item.src).join(","); - } - } else { - res_p = res.data; - } + // 修改chinese_prompt + this.global.fileQueue.enqueue(async () => { + let json_path = path.join( + this.global.config.project_path, + `tmp/input_crop/${element.name}.json` + ) + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')) + if (!value[3]) { + prompt_json.gpt_prompt = res_p + } else { + prompt_json.chinese_prompt = res_p + } + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)) + }) - // 修改chinese_prompt - this.global.fileQueue.enqueue(async () => { - let json_path = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}.json`); - let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); - if (!value[3]) { - prompt_json.gpt_prompt = res_p; - } else { - prompt_json.chinese_prompt = res_p; - } - await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); - }) - - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { - code: 1, - to: to, - rowId: element.id, - data: res_p, - }) - - - } catch (error) { - throw error; - } - - }, `${batch}_${element.name}`, batch) + this.global.newWindow[0].win.webContents.send( + DEFINE_STRING.TRANSLATE_RETURN_REFRESH, + { + code: 1, + to: to, + rowId: element.id, + data: res_p + } + ) + } catch (error) { + throw error } - // 监听总批次完成 - this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { - if (failedTasks.length > 0) { - let message = ` + }, + `${batch}_${element.name}`, + batch + ) + } + // 监听总批次完成 + this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` 翻译任务都已完成。 但是以下任务执行失败: ` - failedTasks.forEach(({ taskId, error }) => { - message += `${taskId}-, \n 错误信息: ${error}` + '\n'; - }); + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n' + }) - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 0, - message: message - }) - } else { - if (value[4]) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 1, - message: "翻译任务完成" - }) - } - } + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: '翻译任务完成' }) - return { - code: 1, - message: "翻译任务已加入队列任务中" - } - } catch (error) { - return { - code: 0, - message: "翻译任务出错,错误信息: " + error.toString() - } + } } + }) + return { + code: 1, + message: '翻译任务已加入队列任务中' + } + } catch (error) { + return { + code: 0, + message: '翻译任务出错,错误信息: ' + error.toString() + } } + } - /** - * - * @param {*} value 0:当前要翻译的字符串 - * 1:源语言 - * 2:目标语言 - * 3:是否拆分(以逗号拆分) - * [tags,'zh','en',false] - */ - async TranslateReturnNow(value) { - try { - // 百度翻译 - if (this.global.config.translation_business.includes("baidu")) { - return await this.TranslateReturnNowBaidu(value); - } else if (this.global.config.translation_business.includes("volcengine")) { - // 火山引擎 - return await this.TranslateReturnNowVolcengine(value); - } else if (this.global.config.translation_business.includes("tencent")) { - // 腾讯翻译 - return await this.TranslateReturnNowTencent(value); - } else if (this.global.config.translation_business.includes("aliyun")) { - return await this.TranslateReturnNowAliyun(value); - } - - } catch (error) { - return { - code: 0, - message: error.toString() - } - } + /** + * + * @param {*} value 0:当前要翻译的字符串 + * 1:源语言 + * 2:目标语言 + * 3:是否拆分(以逗号拆分) + * [tags,'zh','en',false] + */ + async TranslateReturnNow(value) { + try { + await this.InitTranslate() + // 百度翻译 + if (this.translationBusiness.includes('baidu')) { + return await this.TranslateReturnNowBaidu(value) + } else if (this.translationBusiness.includes('volcengine')) { + // 火山引擎 + return await this.TranslateReturnNowVolcengine(value) + } else if (this.translationBusiness.includes('tencent')) { + // 腾讯翻译 + return await this.TranslateReturnNowTencent(value) + } else if (this.translationBusiness.includes('aliyun')) { + return await this.TranslateReturnNowAliyun(value) + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - /** - * 添加翻译任务到队列中 - * @param { - * translateData :要翻译的数据 - * from : 源语言 - * to : 目标语言 - * window.id : 显示的窗体的ID - * isShow : 是不是提示 - * [translateData, from, to, window.id,isShow] - * } value - */ - async TranslatePrompt(value) { - try { - value[0] = JSON.parse(value[0]) - // baidu翻译 - if (this.global.config.translation_business.includes("baidu")) { - return await this.TranslatePromptBaidu(value); - } else if (this.global.config.translation_business.includes("volcengine")) { - // 火山引擎 - return await this.TranslatePromptVolcengine(value); - } else if (this.global.config.translation_business.includes("tencent")) { - // 腾讯翻译 - return await this.TranslatePromptTencent(value); - } else if (this.global.config.translation_business.includes("aliyun")) { - // 阿里云翻译 - return await this.TranslatePromptAliyun(value); - } - - } catch (error) { - return { - code: 0, - message: error.toString() - } - } + /** + * 添加翻译任务到队列中 + * @param { + * translateData :要翻译的数据 + * from : 源语言 + * to : 目标语言 + * window.id : 显示的窗体的ID + * isShow : 是不是提示 + * [translateData, from, to, window.id,isShow] + * } value + */ + async TranslatePrompt(value) { + try { + await this.InitTranslate() + value[0] = JSON.parse(value[0]) + // baidu翻译 + if (this.translationBusiness.includes('baidu')) { + return await this.TranslatePromptBaidu(value) + } else if (this.translationBusiness.includes('volcengine')) { + // 火山引擎 + return await this.TranslatePromptVolcengine(value) + } else if (this.translationBusiness.includes('tencent')) { + // 腾讯翻译 + return await this.TranslatePromptTencent(value) + } else if (this.translationBusiness.includes('aliyun')) { + // 阿里云翻译 + return await this.TranslatePromptAliyun(value) + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - /** - * 阿里云翻译写入队列中 - * @param {*} value - */ - async TranslatePromptAliyun(value) { - try { - let win = this.global.newWindow.filter(item => item.id == value[3])[0]; - if (!win) { - win = this.global.newWindow[0]; + /** + * 阿里云翻译写入队列中 + * @param {*} value + */ + async TranslatePromptAliyun(value) { + try { + let win = this.global.newWindow.filter((item) => item.id == value[3])[0] + if (!win) { + win = this.global.newWindow[0] + } + let translateData = value[0] + let from = value[1] + let to = value[2] + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT + + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i] + this.global.requestQuene.enqueue( + async () => { + if (translateData.length > 5) { + await this.tools.delay(2000) } - let translateData = value[0]; - let from = value[1]; - let to = value[2]; - let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; - - for (let i = 0; i < translateData.length; i++) { - const element = translateData[i]; - this.global.requestQuene.enqueue(async () => { - if (translateData.length > 5) { - await this.tools.delay(2000); - } - let arr_data = []; - if (to == "zh") { - let tmp_data = element.prompt; - arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(","); - arr_data = arr_data.filter(item => item != '' && item != null); - } else if (to == "en") { - for (let j = 0; j < element.chinese_prompt.length; j++) { - const item = element.chinese_prompt[j]; - if (item != "" && item != null) { - arr_data.push(item.dst); - } - } - } - // 如果为空(直接返回) - if (arr_data.length <= 0) { - return; - } - - let req_data = {}; - for (let j = 0; j < arr_data.length; j++) { - const element = arr_data[j]; - req_data[j.toString()] = element; - } - - let config = new OpenApi.Config({ - accessKeyId: this.global.config.translation_app_id, - accessKeySecret: this.global.config.translation_secret, - }); - config.endpoint = `mt.cn-hangzhou.aliyuncs.com`; - - let client = new alimt20181012.default(config); - - let getBatchTranslateRequest = new alimt20181012.GetBatchTranslateRequest({ - apiType: 'translate_standard', - scene: 'general', - sourceLanguage: from, - targetLanguage: to, - formatType: 'text', - sourceText: JSON.stringify(req_data), - }); - let runtime = new Util.RuntimeOptions({}); - - // 复制代码运行请自行打印 API 的返回值 - let res = await client.getBatchTranslateWithOptions(getBatchTranslateRequest, runtime); - console.log(res); - - // 处理返回的数据 - // 检出返回的数据和输入的数据是不是一样的 - let translateList = res.body.translatedList; - if (translateList.length != arr_data.length) { - throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); - } - - let res_data = []; - // { - // "src": "blush", - // "dst": "脸红" - // } - if (to == "zh") { - for (let j = 0; j < arr_data.length; j++) { - const item = arr_data[j]; - let res_tmp = translateList.find(item => item.index == j); - let obj = { - src: item, - dst: res_tmp.translated, - } - res_data.push(obj); - } - } else if (to == 'en') { - for (let j = 0; j < arr_data.length; j++) { - const item = arr_data[j]; - // 获取指定的index的返回数据 - let res_tmp = translateList.find(item => item.index == j); - let obj = { - src: res_tmp.translated, - dst: item - } - res_data.push(obj); - } - } - // 数据返回。写入本地配置文件 - // 修改chinese_prompt - this.global.fileQueue.enqueue(async () => { - let json_path = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}.json`); - let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); - prompt_json.chinese_prompt = res_data; - await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); - }) - win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { - code: 1, - to: to, - rowId: element.id, - data: res_data, - }) - - }, `${batch}_${element.name}`, batch) - } - // 监听总批次完成 - this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { - if (failedTasks.length > 0) { - let message = ` - 翻译任务都已完成。 - 但是以下任务执行失败: - ` - failedTasks.forEach(({ taskId, error }) => { - message += `${taskId}-, \n 错误信息: ${error}` + '\n'; - }); - - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 0, - message: message - }) - } else { - if (value[4]) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 1, - message: "批次翻译任务完成" - }) - } + let arr_data = [] + if (to == 'zh') { + let tmp_data = element.prompt + arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(',') + arr_data = arr_data.filter((item) => item != '' && item != null) + } else if (to == 'en') { + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j] + if (item != '' && item != null) { + arr_data.push(item.dst) } - }) - return { - code: 1, - message: "翻译任务已加入队列任务中" + } + } + // 如果为空(直接返回) + if (arr_data.length <= 0) { + return } - } catch (error) { - throw error; - } - } - - /** - * 阿里云翻译实时返回 - * @param {*} value - */ - async TranslateReturnNowAliyun(value) { - try { - // 判断该当前的翻译API - let from = value[1]; - let to = value[2]; - let ts_d = value[0].replaceAll("_", " ").replaceAll(',', ","); - let req_data = {}; - let req_count = 0; - let req_arr = []; - if (value[3]) { - let tmp_arr = ts_d.split(','); - for (let i = 0; i < tmp_arr.length; i++) { - const element = tmp_arr[i]; - if (element != '' && element != null) { - req_data[i.toString()] = element; - req_arr.push(element); - } - req_count += 1; - } - } else { - req_data["0"] = ts_d; - req_count = 1; - req_arr.push(ts_d); - } - if (req_count <= 0) { - throw new Error("没有传入数据"); + let req_data = {} + for (let j = 0; j < arr_data.length; j++) { + const element = arr_data[j] + req_data[j.toString()] = element } let config = new OpenApi.Config({ - accessKeyId: this.global.config.translation_app_id, - accessKeySecret: this.global.config.translation_secret, - }); - config.endpoint = `mt.cn-hangzhou.aliyuncs.com`; + accessKeyId: this.translationAppId, + accessKeySecret: this.translationSecret + }) + config.endpoint = `mt.cn-hangzhou.aliyuncs.com` - let client = new alimt20181012.default(config); + let client = new alimt20181012.default(config) let getBatchTranslateRequest = new alimt20181012.GetBatchTranslateRequest({ - apiType: 'translate_standard', - scene: 'general', - sourceLanguage: from, - targetLanguage: to, - formatType: 'text', - sourceText: JSON.stringify(req_data), - }); - let runtime = new Util.RuntimeOptions({}); + apiType: 'translate_standard', + scene: 'general', + sourceLanguage: from, + targetLanguage: to, + formatType: 'text', + sourceText: JSON.stringify(req_data) + }) + let runtime = new Util.RuntimeOptions({}) // 复制代码运行请自行打印 API 的返回值 - let res = await client.getBatchTranslateWithOptions(getBatchTranslateRequest, runtime); - console.log(res); + let res = await client.getBatchTranslateWithOptions(getBatchTranslateRequest, runtime) + console.log(res) // 处理返回的数据 // 检出返回的数据和输入的数据是不是一样的 - let translateList = res.body.translatedList; - if (translateList.length != req_count) { - throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); - } - // { - // "src": "blush", - // "dst": "脸红" - // } - // 数据处理 - let res_data = []; - for (let j = 0; j < req_arr.length; j++) { - const item = req_arr[j]; - let res_tmp = translateList.find(item => item.index == j); - if (to == "zh") { - let obj = { - src: item, - dst: res_tmp.translated - } - res_data.push(obj); - } else if (to == "en") { - let obj = { - src: res_tmp.translated, - dst: item - } - res_data.push(obj); - } - } - - // 直接返回数据 - return { - code: 1, - to: to, - data: res_data - } - } catch (error) { - throw error; - } - } - - /** - * 腾讯翻译实时返回 - * @param {*} value - */ - async TranslateReturnNowTencent(value) { - try { - // 判断该当前的翻译API - let from = value[1]; - let to = value[2]; - let ts_d = value[0].replaceAll("_", " ").replaceAll(',', ","); - let req_data = []; - if (value[3]) { - req_data = ts_d.split(','); - } else { - req_data.push(ts_d) - } - req_data = req_data.filter(item => item != "" && item != null); - if (req_data.length <= 0) { - throw new Error("没有传入数据"); - } - const CvmClient = tencentcloud.tmt.v20180321.Client; - const client = new CvmClient({ - credential: { - secretId: this.global.config.translation_app_id, - secretKey: this.global.config.translation_secret - }, - // 产品地域 - region: "ap-shanghai", - // 可选配置实例 - profile: { - signMethod: "TC3-HMAC-SHA256", // 签名方法 - httpProfile: { - reqMethod: "POST", // 请求方法 - reqTimeout: 30, // 请求超时时间,默认60s - }, - }, - }) - - let res = await client.TextTranslateBatch({ - SourceTextList: req_data, - Source: from, - Target: to, - ProjectId: 0 - }); - console.log(res); - - // 处理返回的数据 - // 检出返回的数据和输入的数据是不是一样的 - let translateList = res.TargetTextList; - if (translateList.length != req_data.length) { - throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); - } - // { - // "src": "blush", - // "dst": "脸红" - // } - // 数据处理 - let res_data = []; - for (let j = 0; j < req_data.length; j++) { - const item = req_data[j]; - if (to == "zh") { - let obj = { - src: item, - dst: translateList[j] - } - res_data.push(obj); - } else if (to == "en") { - let obj = { - src: translateList[j], - dst: item - } - res_data.push(obj); - } - } - - // 直接返回数据 - return { - code: 1, - to: to, - data: res_data - } - } catch (error) { - throw error; - } - } - - /** - * 腾讯翻译将翻译的消息写入到队列中 - * @param {*} value - */ - async TranslatePromptTencent(value) { - try { - let win = this.global.newWindow.filter(item => item.id == value[3])[0]; - if (!win) { - win = this.global.newWindow[0]; - } - let translateData = value[0]; - let from = value[1]; - let to = value[2]; - let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; - let secretId = this.global.config.translation_app_id; - let secretKey = this.global.config.translation_secret; - const CvmClient = tencentcloud.tmt.v20180321.Client - const client = new CvmClient({ - credential: { - secretId: secretId, - secretKey: secretKey - }, - region: "ap-shanghai", - profile: { - signMethod: "TC3-HMAC-SHA256", // 签名方法 - httpProfile: { - reqMethod: "POST", // 请求方法 - reqTimeout: 30, // 请求超时时间,默认60s - }, - }, - }) - - for (let i = 0; i < translateData.length; i++) { - const element = translateData[i]; - this.global.requestQuene.enqueue(async () => { - if (translateData.length > 5) { - await this.tools.delay(2000); - } - let arr_data = []; - if (to == "zh") { - let tmp_data = element.prompt; - arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(","); - arr_data = arr_data.filter(item => item != '' && item != null); - } else if (to == "en") { - for (let j = 0; j < element.chinese_prompt.length; j++) { - const item = element.chinese_prompt[j]; - if (item != "" && item != null) { - arr_data.push(item.dst); - } - } - } - // 如果为空(直接返回) - if (arr_data.length <= 0) { - return; - } - // 请求数据 - let req_data = { - Source: from, - Target: to, - SourceTextList: arr_data, - ProjectId: 0 - } - - let res = await client.TextTranslateBatch(req_data); - console.log(res); - - // 处理返回的数据 - // 检出返回的数据和输入的数据是不是一样的 - let translateList = res.TargetTextList; - if (translateList.length != arr_data.length) { - throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); - } - - let res_data = []; - // { - // "src": "blush", - // "dst": "脸红" - // } - if (to == "zh") { - for (let j = 0; j < arr_data.length; j++) { - const item = arr_data[j]; - let obj = { - src: item, - dst: translateList[j] - } - res_data.push(obj); - } - } else if (to == 'en') { - for (let j = 0; j < arr_data.length; j++) { - const item = arr_data[j]; - let obj = { - src: translateList[j], - dst: item - } - res_data.push(obj); - } - } - // 数据返回。写入本地配置文件 - // 修改chinese_prompt - this.global.fileQueue.enqueue(async () => { - let json_path = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}.json`); - let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); - prompt_json.chinese_prompt = res_data; - await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); - }) - win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { - code: 1, - to: to, - rowId: element.id, - data: res_data, - }) - - }, `${batch}_${element.name}`, batch) - } - // 监听总批次完成 - this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { - if (failedTasks.length > 0) { - let message = ` - 翻译任务都已完成。 - 但是以下任务执行失败: - ` - failedTasks.forEach(({ taskId, error }) => { - message += `${taskId}-, \n 错误信息: ${error}` + '\n'; - }); - - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 0, - message: message - }) - } else { - if (value[4]) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 1, - message: "批次翻译任务完成" - }) - } - } - }) - return { - code: 1, - message: "翻译任务已加入队列任务中" - } - - } catch (error) { - throw error; - } - } - - /** - * 火山引擎翻译实时返回 - * @param {*} value - */ - async TranslateReturnNowVolcengine(value) { - try { - // 判断该当前的翻译API - let from = value[1]; - let to = value[2]; - let ts_d = value[0].replaceAll("_", " ").replaceAll(',', ","); - let req_data = []; - if (value[3]) { - req_data = ts_d.split(','); - } else { - req_data.push(ts_d) - } - if (req_data.length <= 0) { - throw new Error("没有传入数据"); - } - let signer = await this.GetVolcengineSinger(); - - let config = { - method: 'post', - maxBodyLength: Infinity, - url: `${this.global.config.translation_business}${signer}`, - headers: { - 'Content-Type': 'application/json' - }, - data: JSON.stringify({ - SourceLanguage: from, - TargetLanguage: to, - TextList: req_data - }) - }; - let res = await axios.request(config); - if (res.status != 200) { - throw new Error("请求错误。请检查网络"); - } - // 判断是不是有返回错误 - if (res.data.ResponseMetadata && res.data.ResponseMetadata.Error) { - let err = res.data.ResponseMetadata.Error; - throw new Error(`错误码: ${err.Code} 错误编号:${err.CodeN} 错误详细信息:${err.Message}`); - } - - // 处理返回的数据 - // 检出返回的数据和输入的数据是不是一样的 - let translateList = res.data.TranslationList; - if (translateList.length != req_data.length) { - throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); - } - // { - // "src": "blush", - // "dst": "脸红" - // } - // 数据处理 - let res_data = []; - for (let j = 0; j < req_data.length; j++) { - const item = req_data[j]; - if (to == "zh") { - let obj = { - src: item, - dst: translateList[j].Translation - } - res_data.push(obj); - } else if (to == "en") { - let obj = { - src: translateList[j].Translation, - dst: item - } - res_data.push(obj); - } - } - - // 直接返回数据 - return { - code: 1, - to: to, - data: res_data - } - } catch (error) { - throw error; - } - } - - /** - * 火山引擎翻译所有数据队列返回 - * @param {*} value - */ - async TranslatePromptVolcengine(value) { - try { - let win = this.global.newWindow.filter(item => item.id == value[3])[0]; - if (!win) { - win = this.global.newWindow[0]; - } - let signer = await this.GetVolcengineSinger(); - let translateData = value[0]; - let from = value[1]; - let to = value[2]; - let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; - for (let i = 0; i < translateData.length; i++) { - const element = translateData[i]; - this.global.requestQuene.enqueue(async () => { - if (translateData.length > 5) { - await this.tools.delay(2000); - } - let arr_data = []; - if (to == "zh") { - let tmp_data = element.prompt; - arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(","); - arr_data = arr_data.filter(item => item != '' && item != null); - } else if (to == "en") { - for (let j = 0; j < element.chinese_prompt.length; j++) { - const item = element.chinese_prompt[j]; - if (item != "" && item != null) { - arr_data.push(item.dst); - } - } - } - // 如果为空(直接返回) - if (arr_data.length <= 0) { - return; - } - // 开始请求 - let req_data = JSON.stringify({ - SourceLanguage: from, - TargetLanguage: to, - TextList: arr_data - }) - let config = { - method: "post", - maxBodyLength: Infinity, - url: `${this.global.config.translation_business}${signer}`, - headers: { - 'Content-Type': 'application/json' - }, - data: req_data - } - let res = await axios.request(config); - console.log(res); - if (res.status != 200) { - throw new Error("请求状态码错误。请检查网络"); - } - - // 判断是不是有返回错误 - if (res.data.ResponseMetadata && res.data.ResponseMetadata.Error) { - let err = res.data.ResponseMetadata.Error; - throw new Error(`错误码: ${err.Code} 错误编号:${err.CodeN} 错误详细信息:${err.Message}`); - } - - // 处理返回的数据 - // 检出返回的数据和输入的数据是不是一样的 - let translateList = res.data.TranslationList; - if (translateList.length != arr_data.length) { - throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); - } - - let res_data = []; - // { - // "src": "blush", - // "dst": "脸红" - // } - if (to == "zh") { - for (let j = 0; j < arr_data.length; j++) { - const item = arr_data[j]; - let obj = { - src: item, - dst: translateList[j].Translation - } - res_data.push(obj); - } - } else if (to == 'en') { - for (let j = 0; j < arr_data.length; j++) { - const item = arr_data[j]; - let obj = { - src: translateList[j].Translation, - dst: item - } - res_data.push(obj); - } - } - // 数据返回。写入本地配置文件 - // 修改chinese_prompt - this.global.fileQueue.enqueue(async () => { - let json_path = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}.json`); - let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); - prompt_json.chinese_prompt = res_data; - await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); - }) - win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { - code: 1, - to: to, - rowId: element.id, - data: res_data, - }) - - }, `${batch}_${element.name}`, batch) - } - // 监听总批次完成 - this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { - if (failedTasks.length > 0) { - let message = ` - 翻译任务都已完成。 - 但是以下任务执行失败: - ` - failedTasks.forEach(({ taskId, error }) => { - message += `${taskId}-, \n 错误信息: ${error}` + '\n'; - }); - - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 0, - message: message - }) - } else { - if (value[4]) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 1, - message: "批次翻译任务完成" - }) - } - } - }) - return { - code: 1, - message: "翻译任务已加入队列任务中" - } - - } catch (error) { - throw error; - } - } - - /** - * 获取火山引擎请求的签名 - */ - async GetVolcengineSinger() { - try { - const openApiRequestData = { - method: "POST", - region: "cn-north-1", - params: { - Action: "TranslateText", - Version: "2020-06-01", - }, - Service: "translate" - } - - const credentials = { - accessKeyId: this.global.config.translation_app_id, - secretKey: this.global.config.translation_secret - } - - const signer = new Signer(openApiRequestData, "translate"); - - // 最终经过加签的 HTTP Query Params - const signedQueryString = signer.getSignUrl(credentials); - console.log(signedQueryString) - return signedQueryString; - - } catch (error) { - throw error; - } - } - - /** - * 百度引擎翻译翻译所有数据 - * @param {} value - * @returns - */ - async TranslatePromptBaidu(value) { - try { - let win = this.global.newWindow.filter(item => item.id == value[3])[0]; - if (!win) { - win = this.global.newWindow[0]; - } - let translateData = value[0]; - let from = value[1]; - let to = value[2]; - let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; - let appId = this.global.config.translation_app_id; - - // 添加一个频次判断是不是演示 - - for (let i = 0; i < translateData.length; i++) { - const element = translateData[i]; - this.global.requestQuene.enqueue(async () => { - try { - if (translateData.length > 5) { - await this.tools.delay(2000); - } - let ts_d = ""; - if (to == "zh") { - ts_d = element.prompt.replaceAll("_", " ").replaceAll(',', ",").replaceAll(',', "\n"); - } else if (to == "en") { - let tmp_arr = []; - // 中文转英文。重新拼接一下 - for (let j = 0; j < element.chinese_prompt.length; j++) { - const item = element.chinese_prompt[j]; - tmp_arr.push(item.dst); - } - ts_d = tmp_arr.join('\n'); - } - let salt = Date.now(); - let sign = MD5(`${this.global.config.translation_app_id}${ts_d}${salt}${this.global.config.translation_secret}`).toString(); - let res = await axios.get(this.global.config.translation_business, { - params: { - q: ts_d, - appid: appId, - salt: salt, - from: from, - to: to, - sign: sign - } - }); - if (res.status != 200) { - throw new Error("请求错误。请检查网络"); - } - // 判断是不是有错误码 - if (res.data.error_code) { - throw new Error(res.data.error_msg); - } - - let res_data = [] - // 将所有的数据协会到本地(然后发送消息到前台界面) - if (res.data.to == "zh") { - res_data = res.data.trans_result - } else { - // 直接在这边处理(前端不用处理) - for (let i = 0; i < res.data.trans_result.length; i++) { - const element = res.data.trans_result[i]; - let obj = { - src: element.dst, - dst: element.src - }; - res_data.push(obj); - } - } - - // 修改chinese_prompt - this.global.fileQueue.enqueue(async () => { - let json_path = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}.json`); - let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); - prompt_json.chinese_prompt = res_data; - await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); - }) - - win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { - code: 1, - to: to, - rowId: element.id, - data: res_data, - }) - - } catch (error) { - throw error; - } - }, `${batch}_${element.name}`, batch); - - } - // 监听总批次完成 - this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { - if (failedTasks.length > 0) { - let message = ` - 翻译任务都已完成。 - 但是以下任务执行失败: - ` - failedTasks.forEach(({ taskId, error }) => { - message += `${taskId}-, \n 错误信息: ${error}` + '\n'; - }); - - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 0, - message: message - }) - } else { - if (value[4]) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { - code: 1, - message: "批次翻译任务完成" - }) - } - } - }) - return { - code: 1, - message: "翻译任务已加入队列任务中" - } - } catch (error) { - throw error; - } - } - - /** - * 百度翻译引擎翻译单个数据。立即返回 - * @param {*} value - * @returns - */ - async TranslateReturnNowBaidu(value) { - try { - // 判断该当前的翻译API - let from = value[1]; - let to = value[2]; - let appId = this.global.config.translation_app_id; - let ts_d = value[0].replaceAll("_", " ").replaceAll(',', ","); - if (value[3]) { - ts_d = ts_d.replaceAll(',', "\n"); - } - let salt = Date.now(); - - let sign = MD5(`${this.global.config.translation_app_id}${ts_d}${salt}${this.global.config.translation_secret}`).toString(); - let res = await axios.get(this.global.config.translation_business, { - params: { - q: ts_d, - appid: appId, - salt: salt, - from: from, - to: to, - sign: sign - } - }); - if (res.status != 200) { - throw new Error("请求错误。请检查网络"); - } - // 判断是不是有错误码 - if (res.data.error_code) { - throw new Error(res.data.error_msg); + let translateList = res.body.translatedList + if (translateList.length != arr_data.length) { + throw new Error('请求的数据长度和返回的数据长度不一致。请重试') } let res_data = [] - // 将所有的数据协会到本地(然后发送消息到前台界面) - if (res.data.to == "zh") { + // { + // "src": "blush", + // "dst": "脸红" + // } + if (to == 'zh') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j] + let res_tmp = translateList.find((item) => item.index == j) + let obj = { + src: item, + dst: res_tmp.translated + } + res_data.push(obj) + } + } else if (to == 'en') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j] + // 获取指定的index的返回数据 + let res_tmp = translateList.find((item) => item.index == j) + let obj = { + src: res_tmp.translated, + dst: item + } + res_data.push(obj) + } + } + // 数据返回。写入本地配置文件 + // 修改chinese_prompt + this.global.fileQueue.enqueue(async () => { + let json_path = path.join( + this.global.config.project_path, + `tmp/input_crop/${element.name}.json` + ) + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')) + prompt_json.chinese_prompt = res_data + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)) + }) + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data + }) + }, + `${batch}_${element.name}`, + batch + ) + } + // 监听总批次完成 + this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n' + }) + + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: '批次翻译任务完成' + }) + } + } + }) + return { + code: 1, + message: '翻译任务已加入队列任务中' + } + } catch (error) { + throw error + } + } + + /** + * 阿里云翻译实时返回 + * @param {*} value + */ + async TranslateReturnNowAliyun(value) { + try { + // 判断该当前的翻译API + let from = value[1] + let to = value[2] + let ts_d = value[0].replaceAll('_', ' ').replaceAll(',', ',') + let req_data = {} + let req_count = 0 + let req_arr = [] + if (value[3]) { + let tmp_arr = ts_d.split(',') + for (let i = 0; i < tmp_arr.length; i++) { + const element = tmp_arr[i] + if (element != '' && element != null) { + req_data[i.toString()] = element + req_arr.push(element) + } + req_count += 1 + } + } else { + req_data['0'] = ts_d + req_count = 1 + req_arr.push(ts_d) + } + if (req_count <= 0) { + throw new Error('没有传入数据') + } + + let config = new OpenApi.Config({ + accessKeyId: this.translationAppId, + accessKeySecret: this.translationSecret + }) + config.endpoint = `mt.cn-hangzhou.aliyuncs.com` + + let client = new alimt20181012.default(config) + + let getBatchTranslateRequest = new alimt20181012.GetBatchTranslateRequest({ + apiType: 'translate_standard', + scene: 'general', + sourceLanguage: from, + targetLanguage: to, + formatType: 'text', + sourceText: JSON.stringify(req_data) + }) + let runtime = new Util.RuntimeOptions({}) + + // 复制代码运行请自行打印 API 的返回值 + let res = await client.getBatchTranslateWithOptions(getBatchTranslateRequest, runtime) + console.log(res) + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.body.translatedList + if (translateList.length != req_count) { + throw new Error('请求的数据长度和返回的数据长度不一致。请重试') + } + // { + // "src": "blush", + // "dst": "脸红" + // } + // 数据处理 + let res_data = [] + for (let j = 0; j < req_arr.length; j++) { + const item = req_arr[j] + let res_tmp = translateList.find((item) => item.index == j) + if (to == 'zh') { + let obj = { + src: item, + dst: res_tmp.translated + } + res_data.push(obj) + } else if (to == 'en') { + let obj = { + src: res_tmp.translated, + dst: item + } + res_data.push(obj) + } + } + + // 直接返回数据 + return { + code: 1, + to: to, + data: res_data + } + } catch (error) { + throw error + } + } + + /** + * 腾讯翻译实时返回 + * @param {*} value + */ + async TranslateReturnNowTencent(value) { + try { + // 判断该当前的翻译API + let from = value[1] + let to = value[2] + let ts_d = value[0].replaceAll('_', ' ').replaceAll(',', ',') + let req_data = [] + if (value[3]) { + req_data = ts_d.split(',') + } else { + req_data.push(ts_d) + } + req_data = req_data.filter((item) => item != '' && item != null) + if (req_data.length <= 0) { + throw new Error('没有传入数据') + } + const CvmClient = tencentcloud.tmt.v20180321.Client + const client = new CvmClient({ + credential: { + secretId: this.translationAppId, + secretKey: this.translationSecret + }, + // 产品地域 + region: 'ap-shanghai', + // 可选配置实例 + profile: { + signMethod: 'TC3-HMAC-SHA256', // 签名方法 + httpProfile: { + reqMethod: 'POST', // 请求方法 + reqTimeout: 30 // 请求超时时间,默认60s + } + } + }) + + let res = await client.TextTranslateBatch({ + SourceTextList: req_data, + Source: from, + Target: to, + ProjectId: 0 + }) + console.log(res) + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.TargetTextList + if (translateList.length != req_data.length) { + throw new Error('请求的数据长度和返回的数据长度不一致。请重试') + } + // { + // "src": "blush", + // "dst": "脸红" + // } + // 数据处理 + let res_data = [] + for (let j = 0; j < req_data.length; j++) { + const item = req_data[j] + if (to == 'zh') { + let obj = { + src: item, + dst: translateList[j] + } + res_data.push(obj) + } else if (to == 'en') { + let obj = { + src: translateList[j], + dst: item + } + res_data.push(obj) + } + } + + // 直接返回数据 + return { + code: 1, + to: to, + data: res_data + } + } catch (error) { + throw error + } + } + + /** + * 腾讯翻译将翻译的消息写入到队列中 + * @param {*} value + */ + async TranslatePromptTencent(value) { + try { + let win = this.global.newWindow.filter((item) => item.id == value[3])[0] + if (!win) { + win = this.global.newWindow[0] + } + let translateData = value[0] + let from = value[1] + let to = value[2] + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT + let secretId = this.translationAppId + let secretKey = this.translationSecret + const CvmClient = tencentcloud.tmt.v20180321.Client + const client = new CvmClient({ + credential: { + secretId: secretId, + secretKey: secretKey + }, + region: 'ap-shanghai', + profile: { + signMethod: 'TC3-HMAC-SHA256', // 签名方法 + httpProfile: { + reqMethod: 'POST', // 请求方法 + reqTimeout: 30 // 请求超时时间,默认60s + } + } + }) + + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i] + this.global.requestQuene.enqueue( + async () => { + if (translateData.length > 5) { + await this.tools.delay(2000) + } + let arr_data = [] + if (to == 'zh') { + let tmp_data = element.prompt + arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(',') + arr_data = arr_data.filter((item) => item != '' && item != null) + } else if (to == 'en') { + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j] + if (item != '' && item != null) { + arr_data.push(item.dst) + } + } + } + // 如果为空(直接返回) + if (arr_data.length <= 0) { + return + } + // 请求数据 + let req_data = { + Source: from, + Target: to, + SourceTextList: arr_data, + ProjectId: 0 + } + + let res = await client.TextTranslateBatch(req_data) + console.log(res) + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.TargetTextList + if (translateList.length != arr_data.length) { + throw new Error('请求的数据长度和返回的数据长度不一致。请重试') + } + + let res_data = [] + // { + // "src": "blush", + // "dst": "脸红" + // } + if (to == 'zh') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j] + let obj = { + src: item, + dst: translateList[j] + } + res_data.push(obj) + } + } else if (to == 'en') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j] + let obj = { + src: translateList[j], + dst: item + } + res_data.push(obj) + } + } + // 数据返回。写入本地配置文件 + // 修改chinese_prompt + this.global.fileQueue.enqueue(async () => { + let json_path = path.join( + this.global.config.project_path, + `tmp/input_crop/${element.name}.json` + ) + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')) + prompt_json.chinese_prompt = res_data + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)) + }) + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data + }) + }, + `${batch}_${element.name}`, + batch + ) + } + // 监听总批次完成 + this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n' + }) + + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: '批次翻译任务完成' + }) + } + } + }) + return { + code: 1, + message: '翻译任务已加入队列任务中' + } + } catch (error) { + throw error + } + } + + /** + * 火山引擎翻译实时返回 + * @param {*} value + */ + async TranslateReturnNowVolcengine(value) { + try { + // 判断该当前的翻译API + let from = value[1] + let to = value[2] + let ts_d = value[0].replaceAll('_', ' ').replaceAll(',', ',') + let req_data = [] + if (value[3]) { + req_data = ts_d.split(',') + } else { + req_data.push(ts_d) + } + if (req_data.length <= 0) { + throw new Error('没有传入数据') + } + let signer = await this.GetVolcengineSinger() + + let config = { + method: 'post', + maxBodyLength: Infinity, + url: `${this.translationBusiness}${signer}`, + headers: { + 'Content-Type': 'application/json' + }, + data: JSON.stringify({ + SourceLanguage: from, + TargetLanguage: to, + TextList: req_data + }) + } + let res = await axios.request(config) + if (res.status != 200) { + throw new Error('请求错误。请检查网络') + } + // 判断是不是有返回错误 + if (res.data.ResponseMetadata && res.data.ResponseMetadata.Error) { + let err = res.data.ResponseMetadata.Error + throw new Error( + `错误码: ${err.Code} 错误编号:${err.CodeN} 错误详细信息:${err.Message}` + ) + } + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.data.TranslationList + if (translateList.length != req_data.length) { + throw new Error('请求的数据长度和返回的数据长度不一致。请重试') + } + // { + // "src": "blush", + // "dst": "脸红" + // } + // 数据处理 + let res_data = [] + for (let j = 0; j < req_data.length; j++) { + const item = req_data[j] + if (to == 'zh') { + let obj = { + src: item, + dst: translateList[j].Translation + } + res_data.push(obj) + } else if (to == 'en') { + let obj = { + src: translateList[j].Translation, + dst: item + } + res_data.push(obj) + } + } + + // 直接返回数据 + return { + code: 1, + to: to, + data: res_data + } + } catch (error) { + throw error + } + } + + /** + * 火山引擎翻译所有数据队列返回 + * @param {*} value + */ + async TranslatePromptVolcengine(value) { + try { + let win = this.global.newWindow.filter((item) => item.id == value[3])[0] + if (!win) { + win = this.global.newWindow[0] + } + let signer = await this.GetVolcengineSinger() + let translateData = value[0] + let from = value[1] + let to = value[2] + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i] + this.global.requestQuene.enqueue( + async () => { + if (translateData.length > 5) { + await this.tools.delay(2000) + } + let arr_data = [] + if (to == 'zh') { + let tmp_data = element.prompt + arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(',') + arr_data = arr_data.filter((item) => item != '' && item != null) + } else if (to == 'en') { + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j] + if (item != '' && item != null) { + arr_data.push(item.dst) + } + } + } + // 如果为空(直接返回) + if (arr_data.length <= 0) { + return + } + // 开始请求 + let req_data = JSON.stringify({ + SourceLanguage: from, + TargetLanguage: to, + TextList: arr_data + }) + let config = { + method: 'post', + maxBodyLength: Infinity, + url: `${this.translationBusiness}${signer}`, + headers: { + 'Content-Type': 'application/json' + }, + data: req_data + } + let res = await axios.request(config) + console.log(res) + if (res.status != 200) { + throw new Error('请求状态码错误。请检查网络') + } + + // 判断是不是有返回错误 + if (res.data.ResponseMetadata && res.data.ResponseMetadata.Error) { + let err = res.data.ResponseMetadata.Error + throw new Error( + `错误码: ${err.Code} 错误编号:${err.CodeN} 错误详细信息:${err.Message}` + ) + } + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.data.TranslationList + if (translateList.length != arr_data.length) { + throw new Error('请求的数据长度和返回的数据长度不一致。请重试') + } + + let res_data = [] + // { + // "src": "blush", + // "dst": "脸红" + // } + if (to == 'zh') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j] + let obj = { + src: item, + dst: translateList[j].Translation + } + res_data.push(obj) + } + } else if (to == 'en') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j] + let obj = { + src: translateList[j].Translation, + dst: item + } + res_data.push(obj) + } + } + // 数据返回。写入本地配置文件 + // 修改chinese_prompt + this.global.fileQueue.enqueue(async () => { + let json_path = path.join( + this.global.config.project_path, + `tmp/input_crop/${element.name}.json` + ) + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')) + prompt_json.chinese_prompt = res_data + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)) + }) + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data + }) + }, + `${batch}_${element.name}`, + batch + ) + } + // 监听总批次完成 + this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n' + }) + + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: '批次翻译任务完成' + }) + } + } + }) + return { + code: 1, + message: '翻译任务已加入队列任务中' + } + } catch (error) { + throw error + } + } + + /** + * 获取火山引擎请求的签名 + */ + async GetVolcengineSinger() { + try { + const openApiRequestData = { + method: 'POST', + region: 'cn-north-1', + params: { + Action: 'TranslateText', + Version: '2020-06-01' + }, + Service: 'translate' + } + + const credentials = { + accessKeyId: this.translationAppId, + secretKey: this.translationSecret + } + + const signer = new Signer(openApiRequestData, 'translate') + + // 最终经过加签的 HTTP Query Params + const signedQueryString = signer.getSignUrl(credentials) + console.log(signedQueryString) + return signedQueryString + } catch (error) { + throw error + } + } + + /** + * 百度引擎翻译翻译所有数据 + * @param {} value + * @returns + */ + async TranslatePromptBaidu(value) { + try { + let win = this.global.newWindow.filter((item) => item.id == value[3])[0] + if (!win) { + win = this.global.newWindow[0] + } + let translateData = value[0] + let from = value[1] + let to = value[2] + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT + let appId = this.translationAppId + + // 添加一个频次判断是不是演示 + + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i] + this.global.requestQuene.enqueue( + async () => { + try { + if (translateData.length > 5) { + await this.tools.delay(2000) + } + let ts_d = '' + if (to == 'zh') { + ts_d = element.prompt + .replaceAll('_', ' ') + .replaceAll(',', ',') + .replaceAll(',', '\n') + } else if (to == 'en') { + let tmp_arr = [] + // 中文转英文。重新拼接一下 + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j] + tmp_arr.push(item.dst) + } + ts_d = tmp_arr.join('\n') + } + let salt = Date.now() + let sign = MD5( + `${this.translationAppId}${ts_d}${salt}${this.translationSecret}` + ).toString() + let res = await axios.get(this.translationBusiness, { + params: { + q: ts_d, + appid: appId, + salt: salt, + from: from, + to: to, + sign: sign + } + }) + if (res.status != 200) { + throw new Error('请求错误。请检查网络') + } + // 判断是不是有错误码 + if (res.data.error_code) { + throw new Error(res.data.error_msg) + } + + let res_data = [] + // 将所有的数据协会到本地(然后发送消息到前台界面) + if (res.data.to == 'zh') { res_data = res.data.trans_result - } else { + } else { // 直接在这边处理(前端不用处理) for (let i = 0; i < res.data.trans_result.length; i++) { - const element = res.data.trans_result[i]; - let obj = { - src: element.dst, - dst: element.src - }; - res_data.push(obj); + const element = res.data.trans_result[i] + let obj = { + src: element.dst, + dst: element.src + } + res_data.push(obj) } - } + } - // 直接返回数据 - return { + // 修改chinese_prompt + this.global.fileQueue.enqueue(async () => { + let json_path = path.join( + this.global.config.project_path, + `tmp/input_crop/${element.name}.json` + ) + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')) + prompt_json.chinese_prompt = res_data + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)) + }) + + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { code: 1, to: to, + rowId: element.id, data: res_data + }) + } catch (error) { + throw error } - } catch (error) { - throw error; - } - } + }, + `${batch}_${element.name}`, + batch + ) + } + // 监听总批次完成 + this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n' + }) -} \ No newline at end of file + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: '批次翻译任务完成' + }) + } + } + }) + return { + code: 1, + message: '翻译任务已加入队列任务中' + } + } catch (error) { + throw error + } + } + + /** + * 百度翻译引擎翻译单个数据。立即返回 + * @param {*} value + * @returns + */ + async TranslateReturnNowBaidu(value) { + try { + // 判断该当前的翻译API + let from = value[1] + let to = value[2] + let appId = this.translationAppId + let ts_d = value[0].replaceAll('_', ' ').replaceAll(',', ',') + if (value[3]) { + ts_d = ts_d.replaceAll(',', '\n') + } + let salt = Date.now() + + let sign = MD5(`${this.translationAppId}${ts_d}${salt}${this.translationSecret}`).toString() + let res = await axios.get(this.translationBusiness, { + params: { + q: ts_d, + appid: appId, + salt: salt, + from: from, + to: to, + sign: sign + } + }) + if (res.status != 200) { + throw new Error('请求错误。请检查网络') + } + // 判断是不是有错误码 + if (res.data.error_code) { + throw new Error(res.data.error_msg) + } + + let res_data = [] + // 将所有的数据协会到本地(然后发送消息到前台界面) + if (res.data.to == 'zh') { + res_data = res.data.trans_result + } else { + // 直接在这边处理(前端不用处理) + for (let i = 0; i < res.data.trans_result.length; i++) { + const element = res.data.trans_result[i] + let obj = { + src: element.dst, + dst: element.src + } + res_data.push(obj) + } + } + + // 直接返回数据 + return { + code: 1, + to: to, + data: res_data + } + } catch (error) { + throw error + } + } +} diff --git a/src/main/Public/chineseSimilarity.ts b/src/main/Public/chineseSimilarity.ts new file mode 100644 index 0000000..cd0474c --- /dev/null +++ b/src/main/Public/chineseSimilarity.ts @@ -0,0 +1,69 @@ +// import jieba from 'jieba-js'; +// import natural from 'natural'; + +// // 示例中文文本数组 +// const texts: string[] = [ +// "这是一个示例文本。", +// "这是一个示例文本。", +// "这是一个不同的文本。", +// "另一个文本示例。", +// "又一个不同的文本。" +// ]; + +// // 删除完全重复的文本 +// const uniqueTexts = Array.from(new Set(texts)); + +// // 定义相似度阈值,通常 0.8 是一个常用的阈值,表示 80% 的相似度 +// const SIMILARITY_THRESHOLD = 0.8; + +// // 分词函数 +// async function tokenize(text: string): Promise { +// return await jieba.cut(text); +// } + +// // 计算两个文本的余弦相似度 +// async function cosineSimilarity(text1: string, text2: string): Promise { +// const tfidf = new natural.TfIdf(); +// tfidf.addDocument((await tokenize(text1)).join(' ')); +// tfidf.addDocument((await tokenize(text2)).join(' ')); + +// const vector1 = tfidf.documents[0] as Record; +// const vector2 = tfidf.documents[1] as Record; + +// const allTerms = new Set([...Object.keys(vector1), ...Object.keys(vector2)]); +// const vector1Array = Array.from(allTerms).map(term => vector1[term] || 0); +// const vector2Array = Array.from(allTerms).map(term => vector2[term] || 0); + +// const dotProduct = vector1Array.reduce((sum, value, index) => sum + value * vector2Array[index], 0); +// const magnitude1 = Math.sqrt(vector1Array.reduce((sum, value) => sum + value * value, 0)); +// const magnitude2 = Math.sqrt(vector2Array.reduce((sum, value) => sum + value * value, 0)); + +// return dotProduct / (magnitude1 * magnitude2); +// } + +// // 检查相似度并去重 +// export async function RemoveSimilarTexts(texts: string[]): Promise { +// const result: string[] = []; + +// for (let i = 0; i < texts.length; i++) { +// let isSimilar = false; +// for (let j = 0; j < result.length; j++) { +// const similarity = await cosineSimilarity(texts[i], result[j]); +// if (similarity >= SIMILARITY_THRESHOLD) { +// isSimilar = true; +// break; +// } +// } +// if (!isSimilar) { +// result.push(texts[i]); +// } +// } + +// return result; +// } + +// // const finalTexts = await removeSimilarTexts(uniqueTexts); + +// // console.log("原始文本:", texts); +// // console.log("唯一文本:", uniqueTexts); +// // console.log("去除相似后的最终文本:", finalTexts); diff --git a/src/main/Public/clipDraft.js b/src/main/Public/clipDraft.js index 8a0b543..4b62870 100644 --- a/src/main/Public/clipDraft.js +++ b/src/main/Public/clipDraft.js @@ -11,7 +11,7 @@ import { cloneDeep } from "lodash"; const compressing = require("compressing"); export class ClipDraft { - constructor(global, value) { + constructor(global, value, draftName = null) { this.speedId = null; this.canvasesId = null; this.soundChannelId = null; @@ -33,10 +33,11 @@ export class ClipDraft { this.global = global; this.value = value; this.pm = new PublicMethod(global); + this.draft_name = draftName; } async InitData() { - this.draft_name = this.global.config.project_name + '_' + this.value[0]; + this.draft_name = this.draft_name ? this.draft_name : this.global.config.project_name + '_' + this.value[0]; let draft_path = path.join(this.global.config.draft_path, this.draft_name); await fspromises.rm(draft_path, { recursive: true, force: true }); await compressing.zip.uncompress(define.draft_temp_path, path.join(this.global.config.draft_path, this.global.config.project_name + '_' + this.value[0])); @@ -124,9 +125,6 @@ export class ClipDraft { materialVideoTmpJson.id = materialId; // 获取输入的图片宽高 - // let image = await Jimp.read(imagePath); - // let width = image.bitmap.width; - // let height = image.bitmap.height; materialVideoTmpJson.width = 1000; materialVideoTmpJson.height = 1000; diff --git a/src/main/Public/generalTools.ts b/src/main/Public/generalTools.ts new file mode 100644 index 0000000..a7b25aa --- /dev/null +++ b/src/main/Public/generalTools.ts @@ -0,0 +1,114 @@ +import { GeneralResponse } from '../../model/generalResponse' + +/** + * 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的后缀 + * @param {*} value 要检查的字符串 + * @param {*} suffix 要添加的后缀 + * @returns + */ +function checkStringValueAddSuffix(value: string, suffix: string): string { + if (value && value !== null && value !== undefined && value !== '') { + return value + suffix + } else { + return '' + } +} + +/** + * 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的前缀 + * @param {*} value 要检查的值 + * @param {*} prefix 要添加的前缀 + * @returns + */ + +function checkStringValueAddPrefix(value: string, prefix: string): string { + if (value && value !== null && value !== undefined && value !== '') { + return prefix + value + } else { + return '' + } +} + +/** + * 新建一个函数,判断传入的字符串的值是不是存在,存在的话删除后面指定数量的字符 + * @param {*} value 要删除的字符串 + * @param {*} suffix 删除的后缀 + * @returns + */ +function checkStringValueDeleteSuffix(value: string, suffix: string): string { + // 增加一个判断,当前删除的数量是不是大于字符串的长度 + if (value && value !== null && value !== undefined && value !== '') { + if (suffix.length > value.length) { + return '' + } else { + return value.slice(0, value.length - suffix.length) + } + } else { + return '' + } +} + +/** + * 新建一个函数,判断传入的字符串的值是不是存在,存在的话删除前面指定数量的字符 + * @param {*} value 操作的字符串 + * @param {*} prefix 要删除的字符串 + * @returns + */ +function checkStringValueDeletePrefix(value: string, prefix: string): string { + // 增加一个判断,当前删除的数量是不是大于字符串的长度 + if (value && value !== null && value !== undefined && value !== '') { + if (prefix.length > value.length) { + return '' + } else { + return value.slice(prefix.length) + } + } else { + return '' + } +} +/** + * 返回成功的消息,包含code,data,message + * @param {*} data 返回的数据 + * @param {*} message 成功消息 + * @param {*} service 消息日志类型 + * @returns + */ +function successMessage( + data?: any, + message?: string, + service?: string +): GeneralResponse.SuccessItem { + if (service) { + global.logger.info(service, message ? message : '成功返回数据') + } + return { + code: 1, + data: data, + message: message + } +} + +/** + * 返回失败的消息, + * @param {*} message 错误信息 + * @param {*} service 错误日志类型 + * @returns + */ +function errorMessage(message: string, service?: string): GeneralResponse.ErrorItem { + if (service) { + global.logger.error(service, message ? message : '未知报错,没有捕获的错误') + } + return { + code: 0, + message: message + } +} + +export { + checkStringValueAddSuffix, + checkStringValueAddPrefix, + checkStringValueDeletePrefix, + checkStringValueDeleteSuffix, + successMessage, + errorMessage +} diff --git a/src/main/ReverseManage/Book/BooKBasic.js b/src/main/ReverseManage/Book/BooKBasic.js deleted file mode 100644 index 01fd020..0000000 --- a/src/main/ReverseManage/Book/BooKBasic.js +++ /dev/null @@ -1,56 +0,0 @@ -import { BookType } from '../../../define/enum/bookEnum' -import { errorMessage, successMessage } from '../../generalTools' -import { BookService } from '../../../define/db/service/Book/bookService' -import { BookTaskService } from '../../../define/db/service/Book/bookTaskService' -const { v4: uuidv4 } = require('uuid') -import { define } from '../../../define/define' -import path from 'path' -import { CheckFolderExistsOrCreate } from '../../../define/Tools/file' - -export class BookBasic { - constructor() {} - - /** - * 新增或者是修小说数据 - * @param {*} book 小说信息 - * @returns - */ - async AddOrModifyBook(book) { - try { - if (book == null) { - return errorMessage('小说数据为空,无法修改') - } - // 处理一下数据,处理文件地址(删除前缀,转换为默认地址) - // 当前的小说的名字是不是在数据库中以存在 - let _bookService = await BookService.getInstance() - let res = await _bookService.AddOrModifyBook(book) - return res - } catch (error) { - return errorMessage( - '修改数据错误,错误信息如下:' + error.message, - 'BookBasic_AddOrModifyBook' - ) - } - } - - // 小说类型返回 - GetBookType() { - return successMessage( - [ - { - label: 'SD反推', - value: BookType.SD_REVERSE - }, - { - label: 'MJ反推', - value: BookType.MJ_REVERSE - }, - { - label: '原创', - value: BookType.ORIGINAL - } - ], - '获取小说类型成功' - ) - } -} diff --git a/src/main/ReverseManage/Book/ReverseBook.js b/src/main/ReverseManage/Book/ReverseBook.js deleted file mode 100644 index 35099ff..0000000 --- a/src/main/ReverseManage/Book/ReverseBook.js +++ /dev/null @@ -1,120 +0,0 @@ -import { successMessage, errorMessage } from '../../generalTools.js' -import { BookBasic } from './BooKBasic.js' -import { BookService } from '../../../define/db/service/Book/bookService' -import { BookTaskService } from '../../../define/db/service/Book/bookTaskService' -import { define } from '../../../define/define.js' -import path from 'path' -import { BasicReverse } from '../../Task/basicReverse.js' - -/** - * 一键反推的相关操作 - */ -export class ReverseBook extends BookBasic { - constructor() { - super() - this.basicReverse = new BasicReverse() - } - - //#region 小说相关操作 - - /** - * 获取当前的小说数据 - * @param {*} bookQuery - */ - async GetBookData(bookQuery) { - try { - let _bookService = await BookService.getInstance() - // 添加小说 - let res = _bookService.GetBookData(bookQuery) - if (res.code == 0) { - throw new Error(res.message) - } - return res - } catch (error) { - return errorMessage(error.message, 'ReverseBook_GetBookData') - } - } - - //#endregion - - //#region 小说批次任务相关操作 - - /** - * 获取小说的任务列表 - * @param {*} bookTaskCondition 查询任务列表的条件 - */ - async GetBookTaskData(bookTaskCondition) { - try { - let _bookTaskService = await BookTaskService.getInstance() - let res = await _bookTaskService.GetBookTaskData(bookTaskCondition) - if (res.code == 0) { - throw new Error(res.message) - } - return res - } catch (error) { - return errorMessage( - '获取小说对应批次错误,错误信息入校:' + error.message, - 'ReverseBook_GetBookTaskData' - ) - } - } - - //#endregion - - //#region 一键全自动 - - /** - * 全自动任务(这边是任务入口,都是在这边调用) - * @param {*} value - * @returns - */ - async AutoAction(bookId) { - try { - // 在一键全自动之前,当前小说对应的批次任务中的所有的子任务都改为fail,然后再执行 - // 获取对应的小说小说数据,找到对应的小说视频地址 - // let _bookService = await BookService.getInstance() - // let _bookTaskService = await BookTaskService.getInstance() - - // let bookData = _bookService.GetBookDataById(bookId) - // if (bookData.data == null) { - // throw new Error('没有找到对应的小说数据,请检查bookId是否正确') - // } - - // // 获取小说对应的批次任务数据,默认初始化为第一个 - // let bookTaskRes = _bookTaskService.GetBookTaskData({ - // bookId: bookId, - // name: 'output_00001' - // }) - // if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) { - // throw new Error('没有找到对应的小说批次任务数据,请检查bookId是否正确') - // } - - // // 获取小说的视频地址 - // let book = bookData.data - // let bookTask = bookTaskRes.data.bookTasks[0] - - // // 将当前小说对应的批次任务中的所有的子任务都改为fail - // let updateTaskRes = _bookTaskService.UpdetedBookTaskToFail(bookId, bookTask.id) - - // // 添加分镜任务 后面就会全自动的开始执行 - // let res = await this.basicReverse.AddFrameDataTask(bookId) - - // 添加分割视频任务 - // let res = await this.basicReverse.AddCutVideoDataTask(bookId) - - // 添加音频分离任务 - // let res = await this.basicReverse.AddSplitAudioDataTask(bookId) - - // 添加图片抽帧任务 - let res = await this.basicReverse.AddGetFrameTask(bookId) - - if (res.code == 0) { - throw new Error(res.message) - } - } catch (error) { - return errorMessage(error.message, 'ReverseBook_AutoAction') - } - } - - //#endregion -} diff --git a/src/main/ReverseManage/imageGenerate.js b/src/main/ReverseManage/imageGenerate.js index 448df49..444b116 100644 --- a/src/main/ReverseManage/imageGenerate.js +++ b/src/main/ReverseManage/imageGenerate.js @@ -13,7 +13,7 @@ const execAsync = util.promisify(exec); const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符 let fspromises = require("fs").promises; import { ImageSetting } from "../../define/setting/imageSetting"; -import { errorMessage } from "../generalTools"; +import { errorMessage } from "../Public/generalTools"; import { LOGGER_DEFINE } from "../../define/logger_define"; diff --git a/src/main/Service/Book/BooKBasic.ts b/src/main/Service/Book/BooKBasic.ts new file mode 100644 index 0000000..e5aea30 --- /dev/null +++ b/src/main/Service/Book/BooKBasic.ts @@ -0,0 +1,283 @@ +import { BookType, OperateBookType, TagDefineType } from '../../../define/enum/bookEnum' +import { errorMessage, successMessage } from '../../Public/generalTools' +import { BookService } from '../../../define/db/service/Book/bookService' +import { BookTaskService } from '../../../define/db/service/Book/bookTaskService' +import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService' +import { CopyImageType } from '../../../define/enum/bookEnum' +const { v4: uuidv4 } = require('uuid') +import { define } from '../../../define/define' +import path from 'path' +import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../define/Tools/file' +import { Book } from '../../../model/book' +import { GeneralResponse } from '../../../model/generalResponse' +import { cloneDeep, isEmpty } from 'lodash' + +export class BookBasic { + constructor() { } + bookTaskService: BookTaskService + bookTaskDetailService: BookTaskDetailService + + async InitService() { + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance() + } + } + + //#region 小说相关操作 + /** + * 新增或者是修小说数据 + * @param {*} book 小说信息 + * @returns + */ + async AddOrModifyBook(book) { + try { + if (book == null) { + return errorMessage('小说数据为空,无法修改') + } + // 处理一下数据,处理文件地址(删除前缀,转换为默认地址) + // 当前的小说的名字是不是在数据库中以存在 + let _bookService = await BookService.getInstance() + let res = await _bookService.AddOrModifyBook(book) + return res + } catch (error) { + return errorMessage( + '修改数据错误,错误信息如下:' + error.message, + 'BookBasic_AddOrModifyBook' + ) + } + } + + // 小说类型返回 + GetBookType() { + return successMessage( + [ + { + label: 'SD反推', + value: BookType.SD_REVERSE + }, + { + label: 'MJ反推', + value: BookType.MJ_REVERSE + }, + { + label: '原创', + value: BookType.ORIGINAL + } + ], + '获取小说类型成功' + ) + } + + //#endregion + + //#region 小说批次任务相关操作 + + + async OneToFourBookTask(bookTaskId: string) { + try { + console.log(bookTaskId) + await this.InitService(); + let copyCount = 100 + let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId) + if (bookTask == null) { + throw new Error("没有找到对应的数小说任务,请检查数据") + } + // 获取所有的出图中最少的 + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookTaskId: bookTaskId + }).data as Book.SelectBookTaskDetail[] + if (bookTaskDetail == null || bookTaskDetail.length <= 0) { + throw new Error("没有对应的小说分镜任务,请先添加分镜任务") + } + + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + if (isEmpty(element.subImagePath)) { + throw new Error("检测到图片没有出完,请先检查出图") + } + if (element.subImagePath == null || element.subImagePath.length <= 0) { + throw new Error("检测到图片没有出完,请先检查出图") + } + if (element.subImagePath.length < copyCount) { + copyCount = element.subImagePath.length + } + } + if (copyCount <= 0) { + throw new Error("批次设置错误,无法进行一拆四") + } + // 开始复制 + let res = await this.CopyNewBookTask(bookTask, bookTaskDetail, copyCount - 1, CopyImageType.ONE) + if (res.code == 0) { + throw new Error(res.message) + } + return successMessage(res.data, "一拆四成功", "BookBasic_OneToFourBookTask") + } catch (error) { + return errorMessage("一拆四失败,失败信息如下:" + error.message, "BookBasic_OneToFourBookTask") + } + } + + /** + * 复制一个小说批次任务,创建新的小说批次任务 + * @param oldBookTaskId + * @param copyCount 复制的数量 + * @param isCopyImage 是否复制图片 + */ + async CopyNewBookTask(sourceBookTask: Book.SelectBookTask, sourceBookTaskDetail: Book.SelectBookTaskDetail[], copyCount: number, copyImageType: CopyImageType) { + try { + await this.InitService(); + let addBookTask = [] as Book.SelectBookTask[] + let addBookTaskDetail = [] as Book.SelectBookTaskDetail[] + + // 先处理文件夹的创建,包括小说任务的和小说任务分镜的 + for (let i = 0; i < copyCount; i++) { + let maxNo = this.bookTaskService.realm + .objects('BookTask') + .filtered('bookId = $0', sourceBookTask.bookId) + .max('no') + let no = maxNo == null ? 1 : Number(maxNo) + 1 + i + let name = 'output_0000' + no + let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`) + await CheckFolderExistsOrCreate(imageFolder) + // 创建对应的文件夹 + let addOneBookTask = { + id: uuidv4(), + bookId: sourceBookTask.bookId, + no: no, + name: name, + generateVideoPath: sourceBookTask.generateVideoPath, + srtPath: sourceBookTask.srtPath, + audioPath: sourceBookTask.audioPath, + draftSrtStyle: sourceBookTask.draftSrtStyle, + backgroundMusic: sourceBookTask.backgroundMusic, + friendlyReminder: sourceBookTask.friendlyReminder, + imageFolder: path.relative(define.project_path, imageFolder), + status: sourceBookTask.status, + errorMsg: sourceBookTask.errorMsg, + updateTime: new Date(), + createTime: new Date(), + isAuto: sourceBookTask.isAuto, + imageStyle: sourceBookTask.imageStyle, + autoAnalyzeCharacter: sourceBookTask.autoAnalyzeCharacter, + customizeImageStyle: sourceBookTask.customizeImageStyle, + videoConfig: sourceBookTask.videoConfig, + prefixPrompt: sourceBookTask.prefixPrompt, + suffixPrompt: sourceBookTask.suffixPrompt, + version: sourceBookTask.version, + imageCategory: sourceBookTask.imageCategory, + } as Book.SelectBookTask + + addBookTask.push(addOneBookTask) + + for (let j = 0; j < sourceBookTaskDetail.length; j++) { + const element = sourceBookTaskDetail[j]; + + let outImagePath = undefined as string + let subImagePath = [] as string[] + + if (copyImageType == CopyImageType.ALL) { // 直接全部复制 + outImagePath = element.outImagePath + subImagePath = element.subImagePath + } else if (copyImageType == CopyImageType.ONE) { // 只复制对应的 + let oldImage = element.subImagePath[i + 1] + outImagePath = path.join(imageFolder, path.basename(element.outImagePath)) + await CopyFileOrFolder(oldImage, outImagePath) + + subImagePath = [] + } + else if (copyImageType == CopyImageType.NONE) { + outImagePath = undefined + subImagePath = [] + } else { + throw new Error("无效的图片复制类型") + } + if (outImagePath) { + // 单独处理一下显示的图片 + let imageBaseName = path.basename(element.outImagePath); + let newImageBaseName = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}/${imageBaseName}`) + await CopyFileOrFolder(outImagePath, newImageBaseName) + } + // 处理SD设置 + let sdConifg = undefined + if (element.sdConifg) { + let sdConifg = cloneDeep(element.sdConifg) + if (sdConifg.webuiConfig) { + let tempSdConfig = cloneDeep(sdConifg.webuiConfig); + tempSdConfig.id = uuidv4() + sdConifg.webuiConfig = tempSdConfig + } + } + + let reverseId = uuidv4() + // 处理反推数据 + let reverseMessage = [] as Book.ReversePrompt[] + if (element.reversePrompt && element.reversePrompt.length > 0) { + reverseMessage = cloneDeep(element.reversePrompt) + for (let k = 0; k < reverseMessage.length; k++) { + reverseMessage[k].id = uuidv4() + reverseMessage[k].bookTaskDetailId = reverseId + } + } + + let addOneBookTaskDetail = { + id: reverseId, + no: element.no, + name: element.name, + bookId: sourceBookTask.bookId, + bookTaskId: addOneBookTask.id, + videoPath: path.relative(define.project_path, element.videoPath), + word: element.word, + oldImage: path.relative(define.project_path, element.oldImage), + afterGpt: element.afterGpt, + startTime: element.startTime, + endTime: element.endTime, + timeLimit: element.timeLimit, + subValue: element.subValue, + characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [], + gptPrompt: element.gptPrompt, + outImagePath: path.relative(define.project_path, outImagePath), + subImagePath: subImagePath || [], + prompt: element.prompt, + adetailer: element.adetailer, + sdConifg: sdConifg, + createTime: new Date(), + updateTime: new Date(), + audioPath: element.audioPath, + subtitlePosition: element.subtitlePosition, + status: element.status, + reversePrompt: reverseMessage, + imageLock: element.imageLock + } as Book.SelectBookTaskDetail + addBookTaskDetail.push(addOneBookTaskDetail) + } + } + + // 数据处理完毕,开始新增数据 + // 将所有的复制才做,全部放在一个事务中 + this.bookTaskService.transaction(() => { + for (let i = 0; i < addBookTask.length; i++) { + const element = addBookTask[i]; + this.bookTaskService.realm.create('BookTask', element) + } + for (let i = 0; i < addBookTaskDetail.length; i++) { + const element = addBookTaskDetail[i]; + this.bookTaskDetailService.realm.create('BookTaskDetail', element) + } + }) + // 全部创建完成 + // 查找到数据,然后全部返回 + let returnBookTask = this.bookTaskService.GetBookTaskData({ + bookId: sourceBookTask.bookId + }).data as Book.SelectBookTask[] + + return successMessage(returnBookTask, "复制小说任务成功", "BookBasic_CopyNewBookTask") + } catch (error) { + console.log(error) + throw error + } + } + + //#endregion +} \ No newline at end of file diff --git a/src/main/Service/Book/ReverseBook.ts b/src/main/Service/Book/ReverseBook.ts new file mode 100644 index 0000000..a3c26d1 --- /dev/null +++ b/src/main/Service/Book/ReverseBook.ts @@ -0,0 +1,588 @@ +import { successMessage, errorMessage } from '../../Public/generalTools' +import { BookBasic } from './BooKBasic' +import { BookService } from '../../../define/db/service/Book/bookService' +import { BookTaskService } from '../../../define/db/service/Book/bookTaskService' +import { define } from '../../../define/define.js' +import fs from 'fs' +import { DEFINE_STRING } from "../../../define/define_string"; +import path from 'path' +import { BasicReverse } from './basicReverse' +import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService' +import { TaskScheduler } from "../../Service/taskScheduler" +import { Book } from '../../../model/book' +import { LoggerStatus, OtherData, ResponseMessageType } from '../../../define/enum/softwareEnum' +import { GeneralResponse } from '../../../model/generalResponse' +import { cloneDeep, isEmpty } from 'lodash' +import { Subtitle } from '../../Service/subtitle' +import { Watermark } from '../watermark' +import { SubtitleSavePositionType } from '../../../define/enum/waterMarkAndSubtitle' +import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, GetFilesWithExtensions } from '../../../define/Tools/file' +import { ValidateJson } from '../../../define/Tools/validate' +import { GetImageBase64, ProcessImage } from '../../../define/Tools/image' +import { BookBackTaskType, BookType, TagDefineType, TaskExecuteType } from '../../../define/enum/bookEnum' +import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService' +import { MJOpt } from '../MJ/mj' +import { TagDefine } from '../../../define/tagDefine' +import { ImageStyleDefine } from '../../../define/iamgeStyleDefine' + + +/** + * 一键反推的相关操作 + */ +export class ReverseBook extends BookBasic { + basicReverse: BasicReverse + bookTaskService: BookTaskService + taskScheduler: TaskScheduler + bookService: BookService + bookTaskDetailService: BookTaskDetailService + bookBackTaskListService: BookBackTaskListService + mjOpt: MJOpt = new MJOpt() + tagDefine: TagDefine + subtitle: Subtitle + watermark: Watermark + + constructor() { + super() + this.basicReverse = new BasicReverse() + this.tagDefine = new TagDefine() + } + + async InitService() { + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + if (!this.taskScheduler) { + this.taskScheduler = new TaskScheduler() + } + if (!this.bookService) { + this.bookService = await BookService.getInstance() + } + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance() + } + if (!this.subtitle) { + this.subtitle = new Subtitle() + } + if (!this.watermark) { + this.watermark = new Watermark() + } + if (!this.bookBackTaskListService) { + this.bookBackTaskListService = await BookBackTaskListService.getInstance() + } + } + + // 主动返回前端的消息 + sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) { + global.newWindow[0].win.webContents.send(message_name, data) + } + + //#region 小说相关操作 + + /** + * 获取当前的小说数据 + * @param {*} bookQuery + */ + async GetBookData(bookQuery) { + try { + let _bookService = await BookService.getInstance() + // 添加小说 + let res = _bookService.GetBookData(bookQuery) + if (res.code == 0) { + throw new Error(res.message) + } + return res + } catch (error) { + return errorMessage(error.message, 'ReverseBook_GetBookData') + } + } + + //#endregion + + //#region 小说批次任务相关操作 + + /** + * 获取小说的任务列表 + * @param {*} bookTaskCondition 查询任务列表的条件 + */ + async GetBookTaskData(bookTaskCondition): Promise { + try { + await this.InitService() + let _bookTaskService = await BookTaskService.getInstance() + let res = _bookTaskService.GetBookTaskData(bookTaskCondition) + if (res.code == 0) { + throw new Error(res.message) + } + + // //TODO 这个后面是不是将所有的数据都是用数据库 + // // 这边加载自定义风格 + // let styleLists = await this.tagDefine.getTagDataByTypeAndProperty('dynamic', "style_tags"); + // if (styleLists.code == 0) { + // throw new Error('获取自定义风格失败') + // } + + // let styleTags = styleLists.data as Book.BookStyle[]; + // // 异步操作获取风格信息 + // for (let index = 0; index < res.data.bookTasks.length; index++) { + // const element = res.data.bookTasks[index]; + // let styleList = [] as Book.BookStyle[] | Book.DefineBookStyle[]; + // // if (element.imageStyle.length > 0) { // 软件自带的风格 + // // let infoRes = ImageStyleDefine.getImageStyleInfomation(JSON.stringify(element.imageStyle)); + // // if (infoRes.code == 0) { + // // throw new Error('获取风格失败'); + // // } + // // styleList = infoRes.data; + // // } + // if (element.customizeImageStyle.length > 0) { // 自定义的风格 + // element.customizeImageStyle.forEach((item) => { + // let style = styleTags.find((tag) => tag.key == item); + // if (style) { + // styleList.push(style); + // } + // }); + // } + // res.data.bookTasks[index].styleList = styleList; + // } + return successMessage(res.data, '获取小说任务成功', 'ReverseBook_GetBookTaskData') + } catch (error) { + return errorMessage( + '获取小说对应批次错误,错误信息入校:' + error.message, + 'ReverseBook_GetBookTaskData' + ) + } + } + + /** + * 获取小说的所有的任务详情 + * @param bookTaskId + */ + async GetBookTaskDetail(bookTaskId: string) { + try { + await this.InitService() + let _bookTaskDetailService = await BookTaskDetailService.getInstance() + let res = _bookTaskDetailService.GetBookTaskData({ bookTaskId: bookTaskId }) + if (res.code == 0) { + throw new Error(res.message) + } + return res + } catch (error) { + return errorMessage( + '获取小说对应批次错误,错误信息入校:' + error.message, + 'ReverseBook_GetBookTaskData' + ) + } + } + + //#endregion + + //#region 一键全自动 + + /** + * 全自动任务(这边是任务入口,都是在这边调用) + * @param {*} value + * @returns + */ + async AutoAction(bookId) { + try { + // 在一键全自动之前,当前小说对应的批次任务中的所有的子任务都改为fail,然后再执行 + // 获取对应的小说小说数据,找到对应的小说视频地址 + // let _bookService = await BookService.getInstance() + // let _bookTaskService = await BookTaskService.getInstance() + + // let bookData = _bookService.GetBookDataById(bookId) + // if (bookData.data == null) { + // throw new Error('没有找到对应的小说数据,请检查bookId是否正确') + // } + + // // 获取小说对应的批次任务数据,默认初始化为第一个 + // let bookTaskRes = _bookTaskService.GetBookTaskData({ + // bookId: bookId, + // name: 'output_00001' + // }) + // if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) { + // throw new Error('没有找到对应的小说批次任务数据,请检查bookId是否正确') + // } + + // // 获取小说的视频地址 + // let book = bookData.data + // let bookTask = bookTaskRes.data.bookTasks[0] + + // // 将当前小说对应的批次任务中的所有的子任务都改为fail + // let updateTaskRes = _bookTaskService.UpdetedBookTaskToFail(bookId, bookTask.id) + + // // 添加分镜任务 后面就会全自动的开始执行 + // let res = await this.basicReverse.AddFrameDataTask(bookId) + + // 添加分割视频任务 + // let res = await this.basicReverse.AddCutVideoDataTask(bookId) + + // 添加音频分离任务 + // let res = await this.basicReverse.AddSplitAudioDataTask(bookId) + + // 添加图片抽帧任务 + let res = await this.basicReverse.AddGetFrameTask(bookId) + + if (res.code == 0) { + throw new Error(res.message) + } + } catch (error) { + return errorMessage(error.message, 'ReverseBook_AutoAction') + } + } + + async GetBookAndTask(bookId: string, bookTaskName: string) { + let book = this.bookService.GetBookDataById(bookId) + if (book == null) { + throw new Error("查找小说数据失败"); + } + // 获取小说对应的批次任务数据,默认初始化为第一个 + let bookTaskRes = await this.bookTaskService.GetBookTaskData({ + bookId: bookId, + name: bookTaskName + }) + if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) { + let msg = "没有找到对应的小说批次任务数据" + this.taskScheduler.AddLogToDB(bookId, book.type, msg, OtherData.DEFAULT, LoggerStatus.FAIL) + throw new Error(msg) + } + return { book: book as Book.SelectBook, bookTask: bookTaskRes.data.bookTasks[0] as Book.SelectBookTask } + } + /** + * 开始分镜任务 + */ + async ComputeStoryboard(bookId: string): Promise { + try { + await this.InitService() + let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001') + let res = await this.basicReverse.ComputeStoryboardFunc(bookId, bookTask.id); + // 分镜成功直接返回 + return successMessage(null, res, "ReverseBook_ComputeStoryboard") + + } catch (error) { + return errorMessage("分镜计算失败,失败信息如下 :" + error.message, 'ReverseBook_ComputeStoryboard') + } + } + + /** + * 开始进行分镜,切割视频并抽帧 + * @param bookId + */ + async Framing(bookId: string): Promise { + try { + await this.InitService() + let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001') + + // 获取所有的分镜数据 + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookId: bookId, + bookTaskId: bookTask.id + }) + if (bookTaskDetail.data.length <= 0) { + // 传入的分镜数据为空,需要重新获取 + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + `没有传入分镜数据,请先进行分镜计算`, + OtherData.DEFAULT, + LoggerStatus.DOING + ) + throw new Error("没有传入分镜数据,请先进行分镜计算"); + } + + let bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[] + + + for (let i = 0; i < bookTaskDetails.length; i++) { + const item = bookTaskDetails[i]; + let res = await this.basicReverse.FrameDataToCutVideoData(item); + } + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + "所有的视频裁剪完成,开始抽帧", + bookTask.id, + LoggerStatus.SUCCESS + ) + + bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookId: bookId, + bookTaskId: bookTask.id + }) + + bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[] + for (let i = 0; i < bookTaskDetails.length; i++) { + const item = bookTaskDetails[i]; + let res = await this.basicReverse.FrameFunc(book, item); + } + // 分镜成功直接返回 + return successMessage(null, "所有的视频裁剪,抽帧完成", "ReverseBook_Framing") + + } catch (error) { + return errorMessage("开始切割视频并抽帧失败,失败信息如下:" + error.message, 'ReverseBook_Framing') + } + } + + /** + * 开始执行分镜任务 + */ + async GetCopywriting(bookId: string): Promise { + try { + await this.InitService() + let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001') + if (isEmpty(book.subtitlePosition)) { + throw new Error("请先设置小说的字幕位置") + } + // 获取所有的分镜数据 + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookId: bookId, + bookTaskId: bookTask.id + }) + if (bookTaskDetail.data.length <= 0) { + // 传入的分镜数据为空,需要重新获取 + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + `没有传入分镜数据,请先进行分镜计算`, + OtherData.DEFAULT, + LoggerStatus.DOING + ) + throw new Error("没有传入分镜数据,请先进行分镜计算"); + } + + let bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[] + for (let i = 0; i < bookTaskDetails.length; i++) { + const item = bookTaskDetails[i]; + + let res = await this.subtitle.GetVideoFrameText({ + id: item.id, + videoPath: item.videoPath, + type: SubtitleSavePositionType.STORYBOARD_VIDEO, + subtitlePosition: book.subtitlePosition + }) + if (res.code == 0) { + throw new Error(res.message) + } + // 修改数据 + this.bookTaskDetailService.UpdateBookTaskDetail(item.id, { + word: res.data, + afterGpt: res.data + }); + + // let res = await this.basicReverse.GetCopywritingFunc(book, item); + // 将当前的数据实时返回,前端进行修改 + this.sendReturnMessage({ + code: 1, + id: item.id, + type: ResponseMessageType.GET_TEXT, + data: res.data // 返回识别到的文案 + }) + + // 添加日志 + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + `${item.name} 识别文案成功`, + bookTask.id, + LoggerStatus.SUCCESS + ) + } + return successMessage(null, "识别是所有文案成功", "ReverseBook_GetCopywriting") + } catch (error) { + return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting') + } + } + + + /** + * 执行去除水印操作 + * @param bookId 小说ID + */ + async RemoveWatermark(bookId) { + try { + await this.InitService() + let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001') + let subtitlePosition = book.subtitlePosition; + if (isEmpty(subtitlePosition)) { + throw new Error("当前没有文案位置的内容,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置") + } + if (!ValidateJson(subtitlePosition)) { + throw new Error("文案位置格式有误,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置") + } + let subtitlePositionParse = JSON.parse(subtitlePosition) + if (subtitlePositionParse.length <= 0) { + throw new Error("文案位置格式有误,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置") + } + let inputImageFolder = path.resolve(define.project_path, `${book.id}/tmp/input`) + if (!(await CheckFileOrDirExist(inputImageFolder))) { + throw new Error("输出文件夹不存在,请先进行抽帧等操作") + } + let iamgePaths = await GetFilesWithExtensions(inputImageFolder, ['.png']) + if (iamgePaths.length <= 0) { + throw new Error("没有检查到抽帧图片,请先进行抽帧等操作") + } + + // 处理位置生成蒙板 + let inputImage = iamgePaths[0] + let outImagePath = path.resolve(define.project_path, `${book.id}/data/mask/mask_temp_${new Date().getTime()}.png`) + await CheckFolderExistsOrCreate(path.dirname(outImagePath)); + await ProcessImage(inputImage, outImagePath, { + x: subtitlePositionParse[0].startX, + y: subtitlePositionParse[0].startY, + width: subtitlePositionParse[0].width, + height: subtitlePositionParse[0].height + }) + + if (!(await CheckFileOrDirExist(outImagePath))) { + throw new Error("生成蒙板失败") + } + + // 开始去除水印 + let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({ + bookId: bookId, + bookTaskId: bookTask.id + }).data as Book.SelectBookTaskDetail[] + + for (let i = 0; i < bookTaskDetails.length; i++) { + const element = bookTaskDetails[i]; + let bookInputImage = element.oldImage; + let name = path.basename(bookInputImage) + let bak = path.resolve(define.project_path, `${book.id}/tmp/input/bak/${name}`); + // 做个备份吧 + await CopyFileOrFolder(bookInputImage, bak) + await fs.promises.unlink(bookInputImage) // 删除原来的图片 + // 开始调用去除水印的方法 + let imageBase64 = await GetImageBase64(bak) + let maskBase64 = await GetImageBase64(outImagePath) + + let res = await this.watermark.ProcessImage({ + imageBase64: imageBase64, + maskBase64: maskBase64, + type: 'file', + inputFilePath: bak, + maskPath: outImagePath, + outFilePath: bookInputImage + }) + + // 去水印执行完毕 + this.sendReturnMessage({ + code: 1, + id: element.id, + type: ResponseMessageType.REMOVE_WATERMARK, + data: res + }, DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN) + + this.taskScheduler.AddLogToDB(bookId, book.type, `${element.name} 去除水印完成`, element.bookTaskId, LoggerStatus.SUCCESS) + } + + // 全部完毕 + return successMessage(null, "全部图片去除水印完成", "ReverseBook_RemoveWatermark") + } catch (error) { + return errorMessage("去除水印失败,错误信息如下:" + error.message, "ReverseBook_RemoveWatermark") + } + } + //#endregion + + //#region 反推相关任务 + + /** + * 添加单句生图任务 + * @param bookTaskDetailId 小说单个分镜的ID + * @param type 反推的类型 + * @returns + */ + async AddReversePromptTask(bookTaskDetailIds: string[], type: BookType): Promise { + try { + await this.InitService() + for (let index = 0; index < bookTaskDetailIds.length; index++) { + const bookTaskDetailId = bookTaskDetailIds[index]; + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId) + if (bookTaskDetail == null) { + throw new Error("没有找到对应的分镜数据") + } + let book = this.bookService.GetBookDataById(bookTaskDetail.bookId) + // 是不是又额外的类型,没有的话试用小说的类型 + let task_type = undefined as BookBackTaskType + if (!type) { + type = book.type + } + switch (type) { + case BookType.MJ_REVERSE: + task_type = BookBackTaskType.MJ_REVERSE + break; + case BookType.SD_REVERSE: + task_type = BookBackTaskType.SD_REVERSE + break; + default: + throw new Error("暂不支持的推理类型") + } + let taskRes = await this.bookBackTaskListService.AddBookBackTask(book.id, task_type, TaskExecuteType.AUTO, bookTaskDetail.bookTaskId, bookTaskDetail.id + ); + if (taskRes.code == 0) { + throw new Error(taskRes.message) + } + // 添加返回日志 + await this.taskScheduler.AddLogToDB(book.id, book.type, `添加 ${task_type} 反推任务成功`, bookTaskDetail.bookTaskId, LoggerStatus.SUCCESS) + } + return successMessage(null, "添加反推任务成功", "ReverseBook_AddReversePromptTask") + } catch (error) { + return errorMessage("添加单个反推失败,错误信息如下:" + error.message, "ReverseBook_SingleReversePrompt") + } + } + + // TODO SD反推,待重写 + async SDReversePrompt(task: TaskModal.Task): Promise { + return successMessage(null, "", "ReverseBook_SDReversePrompt") + } + + /** + * 执行单句推理的任务 + * @param task 任务处理 + */ + async SingleReversePrompt(task: TaskModal.Task): Promise { + try { + // 开始处理 + let res = undefined + switch (task.type) { + case BookBackTaskType.MJ_REVERSE: + res = await this.mjOpt.MJImage2Text(task); + break + case BookBackTaskType.SD_REVERSE: + res = await this.SDReversePrompt(task); + break + default: + throw new Error("未知的任务类型") + } + // 正常返回信息处理 + return successMessage(null, `${task.name} _ ${task.id} 反推任务完成`, 'ReverseBook_SingleReversePrompt') + } catch (error) { + // 进行错误信息处理 + return errorMessage("单个反推失败,错误信息如下:" + error.message, "ReverseBook_SingleReversePrompt") + } + } + + + /** + * 删除指定的提示词数据 + * @param bookTaskDetailIds 要删除的提示词ID + */ + async RemoveReverseData(bookTaskDetailIds: string[]) { + try { + await this.InitService() + // 开始删除 + if (bookTaskDetailIds.length <= 0) { + throw new Error("没有传入要删除的数据") + } + + for (let i = 0; i < bookTaskDetailIds.length; i++) { + const element = bookTaskDetailIds[i]; + this.bookTaskDetailService.DeleteBookTaskDetailReversePromptById(element) + } + + // 全部删除完毕 + return successMessage(null, "删除反推数据成功", "ReverseBook_RemoveReverseData") + } catch (error) { + return errorMessage("删除反推数据失败,错误信息如下:" + error.message, "ReverseBook_RemoveReverseData") + } + } + + //#endregion +} diff --git a/src/main/Task/basicReverse.js b/src/main/Service/Book/basicReverse.ts similarity index 58% rename from src/main/Task/basicReverse.js rename to src/main/Service/Book/basicReverse.ts index fea6c74..44df9b1 100644 --- a/src/main/Task/basicReverse.js +++ b/src/main/Service/Book/basicReverse.ts @@ -3,24 +3,26 @@ import fs from 'fs' const util = require('util') const { exec } = require('child_process') const execAsync = util.promisify(exec) -import { define } from '../../define/define' -import { BookService } from '../../define/db/service/Book/bookService' -import { TaskScheduler } from './taskScheduler' -import { LoggerStatus, LoggerType, OtherData } from '../../define/enum/softwareEnum' -import { errorMessage, successMessage } from '../generalTools' -import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file' -import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService' -import { BookBackTaskListService } from '../../define/db/service/Book/bookBackTaskListService' -import { BookTaskService } from '../../define/db/service/Book/bookTaskService' +import { define } from '../../../define/define' +import { BookService } from '../../../define/db/service/Book/bookService' +import { TaskScheduler } from '../taskScheduler' +import { LoggerStatus, LoggerType, OtherData } from '../../../define/enum/softwareEnum' +import { errorMessage, successMessage } from '../../Public/generalTools' +import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../../define/Tools/file' +import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService' +import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService' +import { BookTaskService } from '../../../define/db/service/Book/bookTaskService' import { isEmpty, set } from 'lodash' -import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../define/Tools/time' -import { FfmpegOptions } from './ffmpegOptions' +import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../../define/Tools/time' +import { FfmpegOptions } from '../ffmpegOptions' import { BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType -} from '../../define/enum/bookEnum' +} from '../../../define/enum/bookEnum' +import { Book } from '../../../model/book' +import { GeneralResponse } from '../../../model/generalResponse' const fspromises = fs.promises @@ -28,6 +30,14 @@ const fspromises = fs.promises * 后台执行的任务函数,直接调用改函数即可,抽帧,分镜,提取字幕等 */ export class BasicReverse { + bookService: BookService + bookTaskService: BookTaskService + bookTaskDetailService: BookTaskDetailService + bookBackTaskListService: BookBackTaskListService + + taskScheduler: TaskScheduler + ffmpegOptions: FfmpegOptions + constructor() { this.taskScheduler = new TaskScheduler() this.ffmpegOptions = new FfmpegOptions() @@ -75,10 +85,11 @@ export class BasicReverse { } // 获取小说对应的批次任务数据,默认初始化为第一个 - let bookTaskRes = this.bookTaskService.GetBookTaskData({ + let bookTaskRes = await this.bookTaskService.GetBookTaskData({ bookId: bookId, name: 'output_00001' }) + if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) { throw new Error('没有找到对应的小说批次任务数据,请检查bookId是否正确') } @@ -107,11 +118,133 @@ export class BasicReverse { ) return successMessage(null, '添加分镜任务成功', 'BasicReverse_AddFrameDataTask') } catch (error) { - return errorMessage( - '添加分镜任务失败,错误信息如下: ' + error.message, - 'BasicReverse_AddFrameDataTask' + throw error + } + } + + /** + * 执行分镜任务 + * @param bookId 小说ID + * @param bookTaskId 小说人物ID,只能是第一个 + * @returns + */ + async ComputeStoryboardFunc(bookId: string, bookTaskId: string): Promise { + await this.InitService() + let book = this.bookService.GetBookDataById(bookId) + if (book == null) { + throw new Error('没有找到对应的小说数据') + } + + this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD) + + // 分镜之前,删除之前的老数据 + let deleteBookTaskRes = this.bookTaskDetailService.DeleteBookTaskDetail({ + bookId: bookId, + bookTaskId: bookTaskId + }) + + let oldVideoPath = book.oldVideoPath + let frameJson = oldVideoPath + '.json' + + let sensitivity = 30 + // 开始之前,推送日志 + let log_content = `开始进行分镜操作,视频地址:${oldVideoPath},敏感度:${sensitivity},正在调用程序进行处理` + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + log_content, + OtherData.DEFAULT, + LoggerStatus.DOING + ) + + // 小说进行分镜(python进行,将结果写道一个json里面) + // 使用异步的方法调用一个python程序,然后写入到指定的json文件中k + let command = `"${path.join( + define.scripts_path, + 'Lai.exe' + )}" "-ka" "${oldVideoPath}" "${frameJson}" "${sensitivity}"` + const output = await execAsync(command, { + maxBuffer: 1024 * 1024 * 10, + encoding: 'utf-8' + }) + // 有错误输出 + if (output.stderr != '') { + let error_msg = `分镜成功,但有警告提示:${output.stderr}` + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + error_msg, + OtherData.DEFAULT, + LoggerStatus.FAIL ) } + // 分镜成功,处理输出 + let josnIsExist = await CheckFileOrDirExist(frameJson) + if (!josnIsExist) { + let error_message = `分镜失败,没有找到对应的分镜输出文件:${frameJson}` + this.bookTaskService.UpdateBookTaskStatus( + bookTaskId, + BookTaskStatus.STORYBOARD_FAIL, + error_message + ) + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + error_message, + OtherData.DEFAULT, + LoggerStatus.FAIL + ) + throw new Error(error_message) + } + + let frameJsonData = JSON.parse(await fspromises.readFile(frameJson, 'utf-8')) + if (frameJsonData.length <= 0) { + let error_msg = `分镜失败,没有找到对应的分镜数据` + this.bookTaskService.UpdateBookTaskStatus( + bookTaskId, + BookTaskStatus.STORYBOARD_FAIL, + error_msg + ) + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + error_msg, + OtherData.DEFAULT, + LoggerStatus.FAIL + ) + throw new Error(error_msg) + } + // 循环写入小说人物详细数据 + for (let i = 0; i < frameJsonData.length; i++) { + let dataArray = frameJsonData[i] + let bookTaskDetail = { + bookId: bookId, + bookTaskId: bookTaskId, + startTime: 0, + endTime: 0 + } as Book.SelectBookTaskDetail + + // 将字符串转换为number + bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0]) + bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1]) + bookTaskDetail.status = BookTaskStatus.STORYBOARD_DONE // 分镜完成 + + let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail) + if (res.code == 0) { + throw new Error(res.message) + } + } + + this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD_DONE) + // 分镜成功,推送日志 + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + `分镜成功,分镜数据如下:${frameJsonData}`, + OtherData.DEFAULT, + LoggerStatus.SUCCESS + ) + return `分镜成功,分镜信息在 ${frameJson}` } /** @@ -130,121 +263,11 @@ export class BasicReverse { let bookId = task.bookId let bookTaskId = task.bookTaskId - let bookRes = this.bookService.GetBookDataById(bookId) - if (bookRes.data == null) { - throw new Error('没有找到对应的小说数据') - } - let book = bookRes.data + let frameRes = await this.ComputeStoryboardFunc(bookId, bookTaskId) + return successMessage(frameRes) - this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD) - - // 分镜之前,删除之前的老数据 - let deleteBookTaskRes = this.bookTaskDetailService.DeleteBookTaskDetail({ - bookId: bookId, - bookTaskId: bookTaskId - }) - - let oldVideoPath = book.oldVideoPath - let frameJson = oldVideoPath + '.json' - - let sensitivity = 30 - // 开始之前,推送日志 - let log_content = `开始进行分镜操作,视频地址:${oldVideoPath},敏感度:${sensitivity},正在调用程序进行处理` - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - log_content, - OtherData.DEFAULT, - LoggerStatus.DOING - ) - - // 小说进行分镜(python进行,将结果写道一个json里面) - // 使用异步的方法调用一个python程序,然后写入到指定的json文件中k - let command = `"${path.join( - define.scripts_path, - 'Lai.exe' - )}" "-ka" "${oldVideoPath}" "${frameJson}" "${sensitivity}"` - const output = await execAsync(command, { - maxBuffer: 1024 * 1024 * 10, - encoding: 'utf-8' - }) - // 有错误输出 - if (output.stderr != '') { - let error_msg = `分镜成功,但有警告提示:${output.stderr}` - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - error_msg, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - } - // 分镜成功,处理输出 - let josnIsExist = await CheckFileOrDirExist(frameJson) - if (!josnIsExist) { - let error_message = `分镜失败,没有找到对应的分镜输出文件:${frameJson}` - this.bookTaskService.UpdateBookTaskStatus( - bookTaskId, - BookTaskStatus.STORYBOARD_FAIL, - error_message - ) - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - error_message, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - throw new Error(error_message) - } - - let frameJsonData = JSON.parse(await fspromises.readFile(frameJson, 'utf-8')) - if (frameJsonData.length <= 0) { - let error_msg = `分镜失败,没有找到对应的分镜数据` - this.bookTaskService.UpdateBookTaskStatus( - bookTaskId, - BookTaskStatus.STORYBOARD_FAIL, - error_msg - ) - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - error_msg, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - throw new Error(error_msg) - } - // 循环写入小说人物详细数据 - for (let i = 0; i < frameJsonData.length; i++) { - let dataArray = frameJsonData[i] - let bookTaskDetail = { - bookId: bookId, - bookTaskId: bookTaskId - } - - // 将字符串转换为number - bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0]) - bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1]) - - let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail) - if (res.code == 0) { - throw new Error(res.message) - } - } - - this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.STORYBOARD_DONE) - // 分镜成功,推送日志 - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - `分镜成功,分镜数据如下:${frameJsonData}`, - OtherData.DEFAULT, - LoggerStatus.SUCCESS - ) - return successMessage(null, `分镜成功,分镜信息在 ${frameJson}`, 'BasicReverse_GetFrameData') } catch (error) { - return errorMessage(error.message, 'BasicReverse_GetFrameData') + throw error } } @@ -264,16 +287,14 @@ export class BasicReverse { await this.InitService() // 判断小说是不是存在 - let bookRes = this.bookService.GetBookDataById(bookId) + let book = this.bookService.GetBookDataById(bookId) - if (bookRes == null) { + if (book == null) { throw new Error('没有找到对应的小说数据') } - let book = bookRes.data - // 找到对应的小说ID和对应的小说批次任务ID,判断是不是有分镜数据 - let bookTaskRes = this.bookTaskService.GetBookTaskData({ + let bookTaskRes = await this.bookTaskService.GetBookTaskData({ bookId: bookId, name: 'output_00001' }) @@ -296,9 +317,9 @@ export class BasicReverse { OtherData.DEFAULT, LoggerStatus.DOING ) - let frameRes = this.GetFrameData(bookId) + let frameRes = await this.GetFrameData(bookId) if (frameRes.code == 0) { - throw new Error((await frameRes).message) + throw new Error(frameRes.message) } } @@ -351,104 +372,93 @@ export class BasicReverse { ) return successMessage(null, '添加视频裁剪任务成功', 'BasicReverse_AddCutVideoDataTask') } catch (error) { - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - error.message, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - return errorMessage( - '添加视频裁剪任务失败,错误信息如下: ' + error.message, - 'BasicReverse_CutVideoData' - ) + throw error } } + /** + * 裁剪视频操作 + * @param bookTaskDetailId 小说详细的分镜ID + * @returns + */ + async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail): Promise { + await this.InitService() + let startTime = bookTaskDetail.startTime + let endTime = bookTaskDetail.endTime + + let book = this.bookService.GetBookDataById(bookTaskDetail.bookId) + if (book == null) { + throw new Error('没有找到对应的小说数据') + } + + if (startTime == null || endTime == null) { + this.bookTaskService.UpdateBookTaskStatus( + bookTaskDetail.bookTaskId, + BookTaskStatus.SPLIT_FAIL, + '开始时间和结束时间不能为空' + ) + throw new Error('开始时间和结束时间不能为空') + } + let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`) + + let res = await this.ffmpegOptions.FfmpegCutVideo( + startTime, + endTime, + book.oldVideoPath, + outVideoFile + ) + if (res.code == 0) { + this.bookTaskService.UpdateBookTaskStatus( + bookTaskDetail.bookTaskId, + BookTaskStatus.SPLIT_FAIL, + res.message + ) + throw new Error(res.message) + } + + // 视频裁剪完成,要将裁剪后的视频地址写入到数据库中 + this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, { + videoPath: path.relative(define.project_path, outVideoFile) + }) + + // 小改小说批次的状态 + this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.SPLIT_DONE) + // 结束,分镜完毕,推送日志,返回成功 + await this.taskScheduler.AddLogToDB( + bookTaskDetail.bookId, + book.type, + `${bookTaskDetail.name}_视频裁剪完成`, + OtherData.DEFAULT, + LoggerStatus.SUCCESS + ) + return `${bookTaskDetail.name}_视频裁剪完成`; + } + /** * 开始执行指定的任务 * @param {*} task * @returns */ - async CutVideoData(task) { + async CutVideoData(task: TaskModal.Task): Promise { try { // 视频分割任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID,没有直接报错 if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) { throw new Error('分镜任务,bookId, bookTaskId, bookTaskDetailId不能为空') } await this.InitService() - let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById( + + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById( task.bookTaskDetailId ) - if (bookTaskDetailRes.code == 0) { - throw new Error(bookTaskDetailRes.message) - } - if (bookTaskDetailRes.data == null) { + if (bookTaskDetail == null) { throw new Error('没有找到对应的分镜数据') } - // 开始执行裁剪视频 - let bookTaskDetail = bookTaskDetailRes.data - let startTime = bookTaskDetail.startTime - let endTime = bookTaskDetail.endTime - let bookRes = this.bookService.GetBookDataById(task.bookId) - if (bookRes.data == null) { - throw new Error('没有找到对应的小说数据') - } - let book = bookRes.data + let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail); - if (startTime == null || endTime == null) { - this.bookTaskService.UpdateBookTaskStatus( - task.bookTaskId, - BookTaskStatus.SPLIT_FAIL, - '开始时间和结束时间不能为空' - ) - throw new Error('开始时间和结束时间不能为空') - } - let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`) - - let res = await this.ffmpegOptions.FfmpegCutVideo( - startTime, - endTime, - book.oldVideoPath, - outVideoFile - ) - if (res.code == 0) { - this.bookTaskService.UpdateBookTaskStatus( - task.bookTaskId, - BookTaskStatus.SPLIT_FAIL, - res.message - ) - throw new Error(res.message) - } - - // 视频裁剪完成,要将裁剪后的视频地址写入到数据库中 - this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, { - videoPath: path.relative(define.project_path, outVideoFile) - }) - // 小改小说批次的状态 - this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.SPLIT_DONE) - // 结束,分镜完毕,推送日志,返回成功 - await this.taskScheduler.AddLogToDB( - task.bookId, - book.type, - `${task.name}_视频裁剪完成`, - OtherData.DEFAULT, - LoggerStatus.SUCCESS - ) - return successMessage(null, `${task.name}_视频裁剪完成`, 'BasicReverse_CutVideoData') + return successMessage(null, `${task.name}_视频裁剪完成`, "BasicReverse_CutVideoData"); } catch (error) { - await this.taskScheduler.AddLogToDB( - task.bookId, - task.type, - error.message, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - return errorMessage( - '裁剪视频失败,错误信息如下: ' + error.message, - 'BasicReverse_CutVideoData' - ) + throw error } } @@ -465,17 +475,16 @@ export class BasicReverse { async AddSplitAudioDataTask(bookId, bookTaskId = null) { try { await this.InitService() - let bookRes = this.bookService.GetBookDataById(bookId) - if (bookRes == null) { + let book = this.bookService.GetBookDataById(bookId) + if (book == null) { throw new Error('没有找到对应的小说数据') } - let book = bookRes.data let bookTaskRes if (bookTaskId != null) { - bookTaskRes = this.bookTaskService.GetBookTaskData({ id: bookTaskId }) + bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId }) } else { - bookTaskRes = this.bookTaskService.GetBookTaskData({ + bookTaskRes = await this.bookTaskService.GetBookTaskData({ bookId: bookId, name: 'output_00001' }) @@ -532,10 +541,7 @@ export class BasicReverse { } return successMessage(null, `添加所有音频分离任务成功`, 'BasicReverse_AddSplitAudioDataTask') } catch (error) { - return errorMessage( - '添加音频分离任务失败,错误信息如下: ' + error.message, - 'BasicReverse_AddSplitAudioDataTask' - ) + throw error } } @@ -551,19 +557,17 @@ export class BasicReverse { throw new Error('分镜任务,bookId, bookTaskId, bookTaskDetailId不能为空') } await this.InitService() - let bookRes = this.bookService.GetBookDataById(task.bookId) - if (bookRes.data == null) { + let book = this.bookService.GetBookDataById(task.bookId) + if (book == null) { throw new Error('没有找到对应的小说数据') } - let book = bookRes.data - let bookTaskDetails = this.bookTaskDetailService.GetBookTaskDetailDataById( + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById( task.bookTaskDetailId ) - if (bookTaskDetails.data == null) { + if (bookTaskDetail == null) { throw new Error('没有找到对应的分镜数据') } - let bookTaskDetail = bookTaskDetails.data let videoPath = bookTaskDetail.videoPath let audioPath = path.join(book.bookFolderPath, `data/audio/${bookTaskDetail.name}.wav`) await CheckFolderExistsOrCreate(path.dirname(audioPath)) @@ -599,15 +603,7 @@ export class BasicReverse { 'BasicReverse_SplitAudioData' ) } catch (error) { - let error_message = `分离音频失败,错误信息如下:${error.message}` - await this.taskScheduler.AddLogToDB( - task.bookId, - task.type, - error_message, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - return errorMessage(error_message, 'BasicReverse_SplitAudioData') + throw error } } @@ -620,17 +616,16 @@ export class BasicReverse { try { // 开始添加任务 await this.InitService() - let bookRes = this.bookService.GetBookDataById(bookId) - if (bookRes.data == null) { + let book = this.bookService.GetBookDataById(bookId) + if (book == null) { throw new Error('没有找到对应的小说数据') } - let book = bookRes.data let bookTaskRes if (bookTaskId != null) { - bookTaskRes = this.bookTaskService.GetBookTaskData({ id: bookTaskId }) + bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId }) } else { - bookTaskRes = this.bookTaskService.GetBookTaskData({ + bookTaskRes = await this.bookTaskService.GetBookTaskData({ bookId: bookId, name: 'output_00001' }) @@ -671,13 +666,53 @@ export class BasicReverse { } return successMessage(null, '添加所有抽帧任务成功', 'BasicReverse_AddGetFrameTask') } catch (error) { - return errorMessage( - '添加抽帧任务失败,错误信息如下: ' + error.message, - 'BasicReverse_AddGetFrameTask' - ) + throw error } } + /** + * 对分镜人物进行抽帧 + * @param book 小说对象 + * @param bookTaskDetail 小说对应的详细信息的对象 + * @returns + */ + async FrameFunc(book: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail): Promise { + let videoPath = bookTaskDetail.videoPath + let outputFramePath = path.join( + book.bookFolderPath, + `tmp/input/${bookTaskDetail.name}.png` + ) + let frameTime = (bookTaskDetail.endTime - bookTaskDetail.startTime) / 2 + + let res = await this.ffmpegOptions.FfmpegGetFrame(frameTime, videoPath, outputFramePath) + if (res.code == 0) { + let errorMessage = `抽帧失败,错误信息如下:${res.message}` + this.bookTaskService.UpdateBookTaskStatus( + bookTaskDetail.bookTaskId, + BookTaskStatus.FRAME_FAIL, + errorMessage + ) + throw new Error(errorMessage) + } + + // 抽帧成功,将抽帧的地址写入到数据库中 + this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, { + oldImage: path.relative(define.project_path, outputFramePath) + }) + + // 推送成功消息 + await this.taskScheduler.AddLogToDB( + book.id, + book.type, + `${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`, + OtherData.DEFAULT, + LoggerStatus.SUCCESS + ) + // 修改状态为抽帧成功 + this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.FRAME_DONE) + return `${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}` + } + /** * 开始执行抽帧任务 * @param {*} task 执行的任务 @@ -689,67 +724,25 @@ export class BasicReverse { throw new Error('分镜任务,bookId, bookTaskId, bookTaskDetailId不能为空') } await this.InitService() - let bookRes = this.bookService.GetBookDataById(task.bookId) - if (bookRes.data == null) { + let book = this.bookService.GetBookDataById(task.bookId) + if (book == null) { throw new Error('没有找到对应的小说数据') } - let book = bookRes.data - let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById( + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById( task.bookTaskDetailId ) - if (bookTaskDetailRes.data == null) { + if (bookTaskDetail == null) { throw new Error('没有找到对应的分镜数据') } - let bookTaskDetail = bookTaskDetailRes.data - let videoPath = bookTaskDetail.videoPath - let outputFramePath = path.join( - book.bookFolderPath, - `data/tmp/input/${bookTaskDetail.name}.png` - ) - let frameTime = (bookTaskDetail.endTime - bookTaskDetail.startTime) / 2 + let res = await this.FrameFunc(book, bookTaskDetail); - let res = await this.ffmpegOptions.FfmpegGetFrame(frameTime, videoPath, outputFramePath) - if (res.code == 0) { - let errorMessage = `抽帧失败,错误信息如下:${res.message}` - this.bookTaskService.UpdateBookTaskStatus( - task.bookTaskId, - BookTaskStatus.FRAME_FAIL, - errorMessage - ) - throw new Error(errorMessage) - } - - // 抽帧成功,将抽帧的地址写入到数据库中 - this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, { - oldImage: path.relative(define.project_path, outputFramePath) - }) - - // 推送成功消息 - await this.taskScheduler.AddLogToDB( - task.bookId, - book.type, - `${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`, - OtherData.DEFAULT, - LoggerStatus.SUCCESS - ) - // 修改状态为抽帧成功 - this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.FRAME_DONE) - return successMessage(null, '抽帧成功', 'BasicReverse_GetFrame') + return successMessage(null, res, 'BasicReverse_GetFrame') } catch (error) { - let errorMessage = `抽帧失败,错误信息如下:${error.message}` - await this.taskScheduler.AddLogToDB( - task.bookId, - task.type, - errorMessage, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - return errorMessage('抽帧失败,错误信息如下: ' + error.message, 'BasicReverse_GetFrame') + throw error } } - //#endregion //#region 提取字幕相关操作 @@ -769,9 +762,9 @@ export class BasicReverse { } let bookTask if (bookTaskId != null) { - bookTaskId = this.bookTaskService.GetBookTaskData({ id: bookTaskId }) + bookTaskId = await this.bookTaskService.GetBookTaskData({ id: bookTaskId }) } else { - bookTask = this.bookTaskService.GetBookTaskData({ + bookTask = await this.bookTaskService.GetBookTaskData({ bookId: bookId, name: 'output_00001' }) @@ -794,7 +787,7 @@ export class BasicReverse { const element = bookTaskDetails.data[i] let taskRes = await this.bookBackTaskListService.AddBookBackTask( bookId, - book.type, + BookBackTaskType.RECOGNIZE, TaskExecuteType.AUTO, bookTask.id, element.id @@ -817,13 +810,61 @@ export class BasicReverse { 'BasicReverse_AddExtractSubtitlesDataTask ' ) } catch (error) { - return errorMessage( - '添加提取字幕任务失败,错误信息如下: ' + error.message, - 'BasicReverse_AddExtractSubtitlesDataTask' - ) + throw error } } + async GetCopywritingFunc(book: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail): Promise { + let txt = '' + // 开始提取,调用本地的服务识别字幕 + let isWisper = true + // 判断是不是用本地的wisper服务 + if (isWisper) { + // 开始调用wisper + // 使用异步的方法调用一个python程序,然后写入到指定的json文件中k + let out_dir = path.dirname(bookTaskDetail.videoPath) + + let command = `"${path.join(define.scripts_path, 'Lai.exe')}" "-t" "${out_dir}" "${bookTaskDetail.audioPath + }" "${bookTaskDetail.name}"` + const output = await execAsync(command, { + maxBuffer: 1024 * 1024 * 10, + encoding: 'utf-8' + }) + // 有错误输出 + if (output.stderr != '') { + let error_msg = `提取字幕成功,但有警告提示:${output.stderr}` + await this.taskScheduler.AddLogToDB( + book.id, + book.type, + error_msg, + OtherData.DEFAULT, + LoggerStatus.FAIL + ) + } + + // 没有的话,去读取对应的字幕文件 + let outTxtPath = path.join(out_dir, `${bookTaskDetail.name}.txt`) + txt = await fspromises.readFile(outTxtPath, 'utf-8') + } else { + // 使用网络服务 + } + + // 修改分镜数据的字幕 + this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, { + word: txt, + afterGpt: txt + }) + + // 提取字幕成功,推送日志 + await this.taskScheduler.AddLogToDB( + book.id, + book.type, + `${bookTaskDetail.name} 提取字幕成功`, + OtherData.DEFAULT, + LoggerStatus.SUCCESS + ) + return txt; + } /** * 执行提取字幕任务 * @param {*} task @@ -837,75 +878,22 @@ export class BasicReverse { } await this.InitService() // 获取详细的小说分镜信息 - let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById( + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById( task.bookTaskDetailId ) - if (bookTaskDetailRes.data == null) { + if (bookTaskDetail == null) { throw new Error('没有找到对应的分镜数据') } - let bookTaskDetail = bookTaskDetailRes.data - let txt = '' - // 开始提取,调用本地的服务识别字幕 - let isWisper = true - // 判断是不是用本地的wisper服务 - if (isWisper) { - // 开始调用wisper - // 使用异步的方法调用一个python程序,然后写入到指定的json文件中k - let out_dir = path.dirname(bookTaskDetail.audioPath) - - let command = `"${path.join(define.scripts_path, 'Lai.exe')}" "-t" "${out_dir}" "${ - bookTaskDetail.audioPath - }" "${bookTaskDetail.name}"` - const output = await execAsync(command, { - maxBuffer: 1024 * 1024 * 10, - encoding: 'utf-8' - }) - // 有错误输出 - if (output.stderr != '') { - let error_msg = `提取字幕成功,但有警告提示:${output.stderr}` - await this.taskScheduler.AddLogToDB( - task.bookId, - book.type, - error_msg, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - } - - // 没有的话,去读取对应的字幕文件 - let outTxtPath = path.join(out_dir, `${bookTaskDetail.name}.txt`) - txt = await fspromises.readFile(outTxtPath, 'utf-8') - } else { - // 使用网络服务 - + let book = this.bookService.GetBookDataById(task.bookId) + if (book == null) { + throw new Error('没有找到对应的小说数据') } - // 修改分镜数据的字幕 - this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, { - word: txt, - afterGpt: txt - }) + let res = await this.GetCopywritingFunc(book, bookTaskDetail) + return successMessage(null, res, 'BasicReverse_ExtractSubtitlesData') - // 提取字幕成功,推送日志 - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - `${task.name} 提取字幕成功`, - OtherData.DEFAULT, - LoggerStatus.SUCCESS - ) - return successMessage(null, `${task.name} 提取字幕成功`, 'BasicReverse_ExtractSubtitlesData') } catch (error) { - let errorMessage = `${task.name} 提取字幕失败,错误信息如下:${error.message}` - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - errorMessage, - OtherData.DEFAULT, - LoggerStatus.FAIL - ) - - return errorMessage(errorMessage, 'BasicReverse_ExtractSubtitlesData') + throw error } } diff --git a/src/main/Service/Book/bookImage.ts b/src/main/Service/Book/bookImage.ts new file mode 100644 index 0000000..8c681bd --- /dev/null +++ b/src/main/Service/Book/bookImage.ts @@ -0,0 +1,346 @@ +import { BookImageCategory, BookType, OperateBookType } from "../../../define/enum/bookEnum"; +import { GeneralResponse } from "../../../model/generalResponse"; +import { errorMessage, successMessage } from "../../Public/generalTools"; +import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; +import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +import { BookService } from "../../../define/db/service/Book/bookService"; +import { Book } from "../../../model/book"; +import path from 'path' +import { Tools } from "../../../main/tools" +import { ImageSplit } from "../../../define/Tools/image"; +import { BackupFileOrFolder, CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile, GetFileSize, GetFilesWithExtensions } from "../../../define/Tools/file"; +import { define } from "../../../define/define"; +import { MJOpt } from "../MJ/mj"; +import { isEmpty } from "lodash"; +import { DEFINE_STRING } from "../../../define/define_string"; +import { ResponseMessageType } from "../../../define/enum/softwareEnum"; +import util from 'util' +import { exec } from 'child_process' +const execAsync = util.promisify(exec); + +/** + * 小说图片执行的相关操作 + */ +export class BookImage { + bookTaskService: BookTaskService + bookTaskDetailService: BookTaskDetailService + bookService: BookService + tools: Tools; + mjOpt: MJOpt; + constructor() { + this.tools = new Tools() + this.mjOpt = new MJOpt() + } + + async InitService() { + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance(); + } + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance(); + } + if (!this.bookService) { + this.bookService = await BookService.getInstance(); + } + } + + /** + * 返回MJ的数据到前端界面,前端界面做出相应的改变 + * @param {*} data + */ + sendChangeMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN) { + global.newWindow[0].win.webContents.send(message_name, data) + } + + /** + * 开始高清图片,当前默认是四倍,后续可以调整 + * @param id 要高清操作的ID + * @param scale 高清的倍率 + * @param operateBookType 操作的类型(支持BOOK,BOOKTASK两种) + */ + async HDImage(id: string, scale: number, operateBookType: OperateBookType): Promise { + try { + await this.InitService() + if (scale <= 0 || scale > 4) { + throw new Error('高清倍率只能是2,3,4') + } + let bookTasks = undefined as Book.SelectBookTask[] + if (operateBookType == OperateBookType.BOOK) { + // 获取所有的小说批次任务 + bookTasks = this.bookTaskService.GetBookTaskData({ + bookId: id + }).data.bookTasks + } else if (operateBookType == OperateBookType.BOOKTASK) { + // 获取当前的小说批次任务 + bookTasks = this.bookTaskService.GetBookTaskData({ + id: id + }).data.bookTasks + } else { + throw new Error("不支持的操作类型,请检查") + } + + if (bookTasks.length <= 0) { + throw new Error("没有找到需要高清的批次任务,请检查"); + } + + // 高清前,先备份文件 + let bakImages = [] + for (let i = 0; i < bookTasks.length; i++) { + const element = bookTasks[i]; + let imageFolder = element.imageFolder + let baseName = path.basename(imageFolder) + let bakFolder = path.join(path.dirname(imageFolder), 'bak/' + baseName); + if (await CheckFileOrDirExist(bakFolder)) { + await DeleteFolderAllFile(bakFolder, true) + } + await BackupFileOrFolder(imageFolder, bakFolder) + // 创建原本的 + await CheckFolderExistsOrCreate(imageFolder); + // bakFolders.push(bakFolder) + let imgs = await GetFilesWithExtensions(bakFolder, ['.png', '.jpg', '.jpeg']) + bakImages.push(...imgs) + } + + + // 这边返回下数据,前端修改进度 + this.sendChangeMessage({ + code: 1, + id: undefined, + type: ResponseMessageType.HD_IMAGE, + data: { + total: bakImages.length, + current: 0 + } + }) + + // 所有的文件备份完毕,开始高高清 + for (let i = 0; i < bakImages.length; i++) { + const element = bakImages[i]; + // 解析路径 + const parsedPath = path.parse(element); + + // 获取 `bak` 之前的目录路径 + const dirWithoutBak = path.dirname(path.dirname(parsedPath.dir)); + + // 组合新路径 + const newDir = path.join(dirWithoutBak, path.basename(parsedPath.dir)); + const newPath = path.join(newDir, parsedPath.base); + // 开始高清 + let command = `"${path.join(define.package_path, "Improve/rnv.exe")}" -i "${element}" -o "${newPath}" -s ${scale}`; + let out = await execAsync(command, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' }); + + this.sendChangeMessage({ + code: 1, + id: undefined, + type: ResponseMessageType.HD_IMAGE, + data: { + total: bakImages.length, + current: i + 1 + } + }) + } + // 高清完毕,返回数据 + return successMessage(null, "所有高清图片成功", "BookImage_HDImage") + } catch (error) { + return errorMessage("高清图片失败,失败信息如下:" + error.toString(), "BookImage_HDImage") + } + } + + + /** + * 检查图片的大小(只要有一个图片的大小超过了 fileSize 的大小,就返回false) + * @param id 需要操作的的ID,可以是整个小说的ID,也可以单个批次的ID + * @param fileSize 单个图片的文件大小 + * @param operateBookType 操作的类型(支持BOOK,BOOKTASK两种) + */ + async CheckImageFileSize(id: string, fileSize: number, operateBookType: OperateBookType) { + try { + await this.InitService() + let bookTasks = undefined as Book.SelectBookTask[] + if (operateBookType == OperateBookType.BOOK) { + // 获取所有的小说批次任务 + bookTasks = this.bookTaskService.GetBookTaskData({ + bookId: id + }).data.bookTasks + } else if (operateBookType == OperateBookType.BOOKTASK) { + // 获取当前的小说批次任务 + bookTasks = this.bookTaskService.GetBookTaskData({ + id: id + }).data.bookTasks + } else { + throw new Error("不支持的操作类型,请检查") + } + + if (bookTasks.length <= 0) { + throw new Error("没有找到需要高清的批次任务,请检查"); + } + let result = [] + for (let i = 0; i < bookTasks.length; i++) { + const element = bookTasks[i]; + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookTaskId: element.id + }).data + for (let i = 0; i < bookTaskDetail.length; i++) { + const item = bookTaskDetail[i]; + if (isEmpty(item.outImagePath)) { + throw new Error(`检查高清失败,批次任务 ${element.name} 的分镜 ${item.name} 的输出图片没有找到。请先生成!`); + } + let size = await GetFileSize(item.outImagePath); + if (size >= fileSize) { + result.push({ + bookTaskId: element.id, + bookTaskDetailId: item.id, + outImagePath: item.outImagePath + }) + } + } + } + return successMessage(result, "高清前图片检查成功", "BookImage_CheckImageFileSize") + } catch (error) { + return errorMessage("检查图片的大小失败,失败信息如下:" + error.toString(), "BookImage_CheckImageFileSize") + } + } + + + /** + * 生成所有的图片,这个方法主要是分流,根据批次生成的方式,添加对应的数据 + * @param bookTaskId + */ + async GenerateImageAll(bookTaskId: string, imageCategory: BookImageCategory): Promise { + try { + console.log('开始生成所有的图片', bookTaskId) + await this.InitService() + let res = undefined as GeneralResponse.ErrorItem | GeneralResponse.SuccessItem + switch (imageCategory) { + case BookImageCategory.SD: + throw new Error('暂时不支持SD的生成') + case BookImageCategory.MJ: + res = await this.mjOpt.AddMJGenerateImageTask(bookTaskId, OperateBookType.BOOKTASK) + break; + case BookImageCategory.D3: + throw new Error('暂时不支持D3的生成') + default: + throw new Error('未知的生成类型') + } + return res + } catch (error) { + return errorMessage("添加生成所有的图片失败,失败信息如下:" + error.toString(), "BookImage_GenerateImageAll") + } + } + + /** + * 对图片进行锁定或者是解锁操作 + * @param id 要执行的id + * @param type 执行的类型,只能事lock或者是unlock + * @param operateBookType 操作的对象类型 + */ + async ImageLockOperation(id: string, type: string, operateBookType: OperateBookType): Promise { + try { + await this.InitService() + let bookTaskDetail = undefined as Book.SelectBookTaskDetail[] + if (operateBookType == OperateBookType.BOOKTASK) { + bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookTaskId: id + }).data + } else if (operateBookType == OperateBookType.BOOKTASKDETAIL) { + bookTaskDetail = [this.bookTaskDetailService.GetBookTaskDetailDataById(id)]; + } + else { + throw new Error('操作的对象类型不正确') + } + if (type == "lock") { + bookTaskDetail = bookTaskDetail.filter(item => (item.imageLock == null) || (item.imageLock == false && item.outImagePath != null)) + } else if (type == "unlock") { + bookTaskDetail = bookTaskDetail.filter(item => item.imageLock == true && item.outImagePath != null) + } else { + throw new Error('未知的锁定类型') + } + if (bookTaskDetail.length <= 0) { + throw new Error('没有要操作的数据,请检查') + } + + let result = [] + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + let lock = type == "lock" ? true : false + this.bookTaskDetailService.UpdateBookTaskDetail(element.id, { + imageLock: lock + }) + + result.push({ + id: element.id, + imageLock: lock + }) + } + + return successMessage(result, "全部图片执行锁定或解锁成功", "BookImage_ImageLockOperation") + } catch (error) { + return errorMessage("图片执行锁定或解锁失败,失败信息如下:" + error.toString(), "BookImage_ImageLockOperation") + } + } + + /** + * 下载指定的图片地址并且分割 + * @param bookTaskDetailId 对应的分镜ID + * @param imageUrl 图片地址 + * @returns + */ + async DownloadImageUrlAndSplit(bookTaskDetailId: string, imageUrl: string,) { + try { + await this.InitService(); + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId) + if (bookTaskDetail == null) { + throw new Error('获取到的数据分镜为空,无法执行操作') + } + let book = this.bookService.GetBookDataById(bookTaskDetail.bookId) + if (book == null) { + throw new Error('获取到的小说为空,无法执行操作') + } + let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail.bookTaskId) + if (bookTask == null) { + throw new Error('获取到的任务为空,无法执行操作') + } + let imagePath = path.join(book.bookFolderPath, `data\\MJOriginalImage\\${bookTaskDetail.id}.png`) + + // 判断是不是一个链接 + const urlRegex = /^(http|https):\/\/[^ "]+$/ + if (!urlRegex.test(imageUrl)) { + // 本地图片,直接复制 + await CopyFileOrFolder(imageUrl, imagePath) + } else { + // 网络图片下载 + await this.tools.downloadFileUrl(imageUrl, imagePath) + } + + // 进行图片裁剪 + let imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage')); + if (imageRes && imageRes.length < 4) { + throw new Error("图片裁剪失败") + } + // 修改数据 + // 修改数据库数据,将图片保存到对应的文件夹中 + let firstImage = imageRes[0]; + if (book.type == BookType.ORIGINAL) { + await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`)); + } + let out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`) + await CopyFileOrFolder(firstImage, out_file); + + // 修改分镜的数据 + this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, { + outImagePath: path.relative(define.project_path, out_file), + subImagePath: imageRes.map((item) => path.relative(define.project_path, item)) + }) + + return successMessage({ + outImagePath: out_file + '?time=' + new Date().getTime(), + subImagePath: imageRes.map((item) => item + '?time=' + new Date().getTime()) + }, "下载指定的图片地址并且分割成功", "BookImage_DownloadImageUrlAndSplit") + } catch (error) { + return { + code: 0, + message: '下载指定的图片地址并且分割错误,错误信息如下:' + error.message + } + } + } +} diff --git a/src/main/Service/Book/bookTask.ts b/src/main/Service/Book/bookTask.ts new file mode 100644 index 0000000..41847f7 --- /dev/null +++ b/src/main/Service/Book/bookTask.ts @@ -0,0 +1,78 @@ +import { CheckFolderExistsOrCreate, DeleteFolderAllFile } from "../../../define/Tools/file"; +import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; +import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +import { BookService } from "../../../define/db/service/Book/bookService"; +import { BookTaskStatus, OperateBookType } from "../../../define/enum/bookEnum"; +import { errorMessage, successMessage } from "../../Public/generalTools"; +import { Book } from "../../../model/book"; + +/** + * 小说批次相关的操作 + */ +export class BookTask { + bookTaskService: BookTaskService + bookTaskDetailService: BookTaskDetailService + bookService: BookService + constructor() { + } + + + async InitService() { + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance() + } if (!this.bookService) { + this.bookService = await BookService.getInstance() + } + } + + + /** + * 重置小说任务数据 + * @param bookTaskId 小说任务ID + */ + async ReSetBookTask(bookTaskId: string) { + try { + console.log(bookTaskId) + await this.InitService() + let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId); + if (bookTask == null) { + throw new Error('未找到对应的小说任务') + } + this.bookTaskService.ResetBookTask(bookTaskId); + // 数据库删除完毕,看是删除对应的文件(主要是图片) + let imageFolder = bookTask.imageFolder; + // 整个删掉在重建 + await DeleteFolderAllFile(imageFolder) + await CheckFolderExistsOrCreate(imageFolder) + return successMessage(null, "重置小说数据成功", "BookTask_ReSetBookTask") + } catch (error) { + return errorMessage('重置小说批次数据失败,错误信息如下' + error.toString(), "BookTask_ReSetBookTask"); + } + } + + /** + * 删除对应的小说批次数据 + * @param bookTaskId 小说批次ID + */ + async DeleteBookTask(bookTaskId: string) { + try { + await this.InitService(); + let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId); + if (bookTask == null) { + throw new Error('未找到对应的小说任务,无法执行删除操作') + } + let imageFolder = bookTask.imageFolder; + // 先删除每个批次对应的数据,然后删除批次 + this.bookTaskService.DeleteBookTask(bookTaskId); + // 删除成功,直接把对应的出图文件夹删掉 + await DeleteFolderAllFile(imageFolder, true) + return successMessage(null, "删除小说批次数据成功", "BookTask_DeleteBookTask") + } catch (error) { + return errorMessage('删除小说批次数据失败,错误信息如下' + error.toString(), "BookTask_DeleteBookTask"); + } + } + +} \ No newline at end of file diff --git a/src/main/Service/Book/bookVideo.ts b/src/main/Service/Book/bookVideo.ts new file mode 100644 index 0000000..07169e8 --- /dev/null +++ b/src/main/Service/Book/bookVideo.ts @@ -0,0 +1,235 @@ +import { OperateBookType } from "../../../define/enum/bookEnum"; +import { Book } from "../../../model/book"; +import { errorMessage, successMessage } from "../../Public/generalTools"; +import { BookService } from "../../../define/db/service/Book/bookService"; +import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; +import { GeneralResponse } from "../../../model/generalResponse"; +import { Setting } from '../../../main/setting/setting' +import path from 'path' +import { CheckFolderExistsOrCreate } from "../../../define/Tools/file"; +import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +import fs from 'fs' +import { ClipDraft } from '../../Public/clipDraft' + +export class BookVideo { + bookService: BookService + bookTaskService: BookTaskService + setting: Setting + bookTaskDetailService: BookTaskDetailService + constructor() { + this.setting = new Setting(global) + } + + async InitService() { + if (!this.bookService) { + this.bookService = await BookService.getInstance() + } + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance() + } + } + + + /** + * 将小说中的生成视频的数据,写道小说批次任务中 + * @param id + * @param operateBookType + */ + async UseBookVideoDataToBookTask(id: string, operateBookType: OperateBookType) { + try { + console.log(id, operateBookType) + await this.InitService(); + let book = undefined as Book.SelectBook; + let bookTasks = undefined as Book.SelectBookTask[]; + if (operateBookType == OperateBookType.BOOK) { + book = this.bookService.GetBookDataById(id); + if (book == null) { + throw new Error('未找到对应的小说') + } + bookTasks = this.bookTaskService.GetBookTaskData({ + bookId: id, + }).data; + } else if (operateBookType == OperateBookType.BOOKTASK) { + let bookTask = this.bookTaskService.GetBookTaskDataById(id); + if (bookTask == null) { + throw new Error('未找到对应的小说任务') + } + book = this.bookService.GetBookDataById(bookTask.bookId); + if (book == null) { + throw new Error('未找到对应的小说') + } + bookTasks = [bookTask]; + } else { + throw new Error("未知的操作类型"); + } + + if (bookTasks.length <= 0) { + throw new Error("没有需要操作的小说数据,请检查"); + } + + // 将修改数据放在一个事务中 + this.bookService.transaction(() => { + for (let i = 0; i < bookTasks.length; i++) { + const element = bookTasks[i]; + let modifyBookTask = this.bookService.realm.objectForPrimaryKey('BookTask', element.id); + modifyBookTask.backgroundMusic = book.backgroundMusic; + modifyBookTask.friendlyReminder = book.friendlyReminder; + modifyBookTask.draftSrtStyle = book.draftSrtStyle; + modifyBookTask.srtPath = book.srtPath; + modifyBookTask.audioPath = book.audioPath; + } + }) + return successMessage({ + backgroundMusic: book.backgroundMusic, + friendlyReminder: book.friendlyReminder, + draftSrtStyle: book.draftSrtStyle, + srtPath: book.srtPath, + audioPath: book.audioPath, + }, "将小说中的生成视频的数据,写到小说批次任务中成功", "BookTask_UseBookVideoDataToBookTask") + } catch (error) { + return errorMessage('将小说中的生成视频的数据,写到小说批次任务中失败,错误信息如下:' + error.toString(), "BookTask_UseBookVideoDataToBookTask"); + } + } + + /** + * 生成配置文件,用于生成草稿或者是合成视频 + * @param book 小说数据 + * @param bookTask 对应的小说任务数据 + */ + private async GenerateConfigFile(book: Book.SelectBook, bookTask: Book.SelectBookTask): Promise { + try { + // 先修改通用设置 + let saveProjectRes = await this.setting.ModifySampleSetting(JSON.stringify({ + project_path: book.bookFolderPath, + project_name: book.name, + })) + if (saveProjectRes.code == 0) { + throw new Error("修改通用设置失败") + } + // 开始生成配置文件 + + let configPath = path.join(book.bookFolderPath, "scripts/config.json"); + await CheckFolderExistsOrCreate(path.dirname(configPath)); + + // 开始配置 + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookTaskId: bookTask.id + }).data; + + let configData = { + srt_time_information: [], + video_config: { + srt_path: bookTask.srtPath, + audio_path: bookTask.audioPath, + draft_srt_style: bookTask.draftSrtStyle ? bookTask.draftSrtStyle : "0", + background_music: bookTask.backgroundMusic, + friendly_reminder: bookTask.friendlyReminder ? bookTask.friendlyReminder : "0", + } + } + + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + let frameData = { + no: element.no, + id: element.id, + lastId: i == 0 ? '' : bookTaskDetail[i - 1].id, + word: element.word, + old_image: element.oldImage, + after_gpt: element.afterGpt, + start_time: element.startTime, + end_time: element.endTime, + timeLimit: `${element.startTime} -- ${element.endTime}`, + subValue: element.subValue?.map(item => { + return { + start_time: item.startTime, + end_time: item.endTime, + srt_value: item.srtValue, + id: item.id + } + }), + character_tags: [], + gpt_prompt: element.gptPrompt, + mjMessage: element.mjMessage, + prompt_json: '', + name: element.name + '.png', + outImagePath: element.outImagePath, + subImagePath: element.subImagePath, + scene_tags: [], + imageLock: element.imageLock, + prompt: element.prompt + } + configData.srt_time_information.push(frameData) + } + // 完毕,将数据写出 + await fs.promises.writeFile(configPath, JSON.stringify(configData)); + } catch (error) { + throw error + } + } + + + async AddJianyingDraft(id: string, operateBookType: OperateBookType): Promise { + try { + await this.InitService(); + console.log(id, operateBookType) + let book = undefined as Book.SelectBook + let bookTasks = undefined as Book.SelectBookTask[] + if (operateBookType == OperateBookType.BOOK) { + book = this.bookService.GetBookDataById(id); + if (book == null) { + throw new Error("没有找到对应的小说数据,请检查"); + } + bookTasks = this.bookTaskService.GetBookTaskData({ + bookId: id + }).data.bookTasks + } else if (operateBookType == OperateBookType.BOOKTASK) { + // 直接获取对应的数据 + let tempBookTask = this.bookTaskService.GetBookTaskDataById(id); + if (tempBookTask == null) { + throw new Error("没有找到小说批次任务,请检查"); + } + bookTasks = [tempBookTask] + book = this.bookService.GetBookDataById(tempBookTask.bookId); + if (book == null) { + throw new Error + } + } else { + throw new Error("未知的操作类型"); + } + + if (bookTasks.length <= 0) { + throw new Error("没有找到小说批次任务,请检查") + } + + // 判断是不是生成单个,每次生成都要修改一下配置文件 + // TODO 后面这个地方要修改 + // 调用生成草稿的方法 + + let result = [] + for (let i = 0; i < bookTasks.length; i++) { + const element = bookTasks[i]; + await this.GenerateConfigFile(book, element); + // 数据处理完毕,开始输出 + let clipDraft = new ClipDraft(global, [element.name, { + srt_path: element.srtPath, + audio_path: element.audioPath, + draft_srt_style: element.draftSrtStyle ? element.draftSrtStyle : "0", + background_music: element.backgroundMusic, + friendly_reminder: element.friendlyReminder ? element.friendlyReminder : "0", + }]) + let res = await clipDraft.addDraft(); + if (res.code == 0) { + throw new Error(res.message) + } + result.push(res.draft_name); + } + return successMessage(result, `${result.join('\n')} ${'\n'} 剪映草稿添加成功`, "BookTask_AddJianyingDraft") + + } catch (error) { + return errorMessage('添加剪映草稿失败,错误信息如下:' + error.toString(), "BookTask_AddJianyingDraft"); + } + } +} \ No newline at end of file diff --git a/src/main/Service/Book/imageStyle.ts b/src/main/Service/Book/imageStyle.ts new file mode 100644 index 0000000..41d3d64 --- /dev/null +++ b/src/main/Service/Book/imageStyle.ts @@ -0,0 +1,97 @@ +import { TagDefineType } from "../../../define/enum/bookEnum"; +import { ImageStyleDefine } from "../../../define/iamgeStyleDefine"; +import { Book } from "../../../model/book"; +import { GeneralResponse } from "../../../model/generalResponse"; +import { TagCustomize } from "../../Original/TagCustomize"; +import { errorMessage, successMessage } from "../../Public/generalTools"; +import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; + +export class ImageStyle { + tagCustomize: TagCustomize + bookTaskService: BookTaskService + constructor() { + this.tagCustomize = new TagCustomize(global) + } + + + async InitService() { + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + } + + /** + * 传入默认的风格和自定义的风格,返回对应的风格的详细信息 + * @param imageStyle 默认的风格ID + * @param customizeImageStyle 自定义的风格ID + */ + async GetAllImageStyleList(imageStyle: string[], customizeImageStyle: string[]): Promise { + try { + // 拿到所有的风格 + let styleArr = []; + if (imageStyle && imageStyle.length > 0) { + let imageStyleRes = ImageStyleDefine.getImageStyleInfomation(JSON.stringify(imageStyle)) // 获取软件默认的数据 + if (imageStyleRes.code == 0) { + throw new Error(imageStyleRes.message) + } + styleArr.push(...imageStyleRes.data); + } + // 获取自定义的风格 + if (customizeImageStyle && customizeImageStyle.length > 0) { + let customizeImageStyleRes = await this.tagCustomize.GetTagDataByTypeAndProperty(['dynamic', 'style_tags']) // 获取自定义的数据 + if (customizeImageStyleRes.code == 0) { + throw new Error(customizeImageStyleRes.message) + } + let customizeImageStyleArr = customizeImageStyleRes.data + for (let i = 0; i < customizeImageStyle.length; i++) { + const element = customizeImageStyle[i]; + let style = customizeImageStyleArr.find(x => x.key == element) + if (style) { + styleArr.push(style) + } + } + } + return styleArr + } catch (error) { + throw error + } + + } + + /** + * 保存选中的小说的风格 + * @param styleList 风格列表 + * @param bookTaskId 小说任务ID + * @returns + */ + async SaveImageStyle(styleList: Book.BookStyle[], bookTaskId: string): Promise { + try { + await this.InitService() + console.log(styleList) + let imageStyle = [] as string[] + let customizeImageStyle = [] as string[] + + styleList.forEach((style) => { + if (style.type == TagDefineType.STYLE_MAIN) { + customizeImageStyle.push(style.key) + } else { + imageStyle.push(style.id) + } + }) + + // 保存数据 + this.bookTaskService.UpdetedBookTaskData(bookTaskId, { + imageStyle: imageStyle, + customizeImageStyle: customizeImageStyle + }) + + return successMessage({ + imageStyle: imageStyle, + customizeImageStyle: customizeImageStyle + }, '保存小说风格成功', 'BookBasic_SaveImageStyle') + } catch (error) { + return errorMessage('保存小说风格失败,错误信息如下:' + error.message, 'BookBasic_SaveImageStyle') + } + } + +} \ No newline at end of file diff --git a/src/main/Service/MJ/mj.ts b/src/main/Service/MJ/mj.ts new file mode 100644 index 0000000..0f6d98c --- /dev/null +++ b/src/main/Service/MJ/mj.ts @@ -0,0 +1,771 @@ +import { isEmpty, join } from "lodash"; +import { Book } from "../../../model/book"; +import { checkStringValueAddPrefix, checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools"; +import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService"; +import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../define/Tools/file"; +import { define } from "../../../define/define" +import { GetImageBase64, ImageSplit } from "../../../define/Tools/image"; +import MJApi from "./mjApi" +import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogType, MJAction, MergeType, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum"; +import { DEFINE_STRING } from "../../../define/define_string"; +import { MJ } from "../../../model/mj"; +import { MJRespoonseType } from "../../../define/enum/mjEnum"; +import { MJSetting } from "../../../model/Setting/mjSetting"; +import { GeneralResponse } from "../../../model/generalResponse" +import { LoggerStatus, ResponseMessageType } from "../../../define/enum/softwareEnum"; +import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; +import { ReverseBook } from "../Book/ReverseBook"; +import { ImageStyle } from "../Book/imageStyle"; +import { TaskScheduler } from "../taskScheduler"; +import { BookService } from "../../../define/db/service/Book/bookService"; +import { Tools } from "../../../main/tools" +import { MJSettingService } from "../../../define/db/service/SoftWare/mjSettingService"; + +import path from "path" +const { v4: uuidv4 } = require('uuid') +import fs from "fs" +const fspromise = fs.promises + +export class MJOpt { + bookBackTaskList: BookBackTaskListService + bookTaskDetail: BookTaskDetailService + reverseBook: ReverseBook; + bookTaskService: BookTaskService + mjApi: MJApi; + mjSetting: MJSetting.MjSetting + imageStyle: ImageStyle; + taskScheduler: TaskScheduler; + bookService: BookService + tools: Tools; + mjSettingService: MJSettingService; + constructor() { + this.imageStyle = new ImageStyle() + this.taskScheduler = new TaskScheduler() + this.tools = new Tools() + } + async InitService() { + if (!this.reverseBook) { + this.reverseBook = new ReverseBook() + } + if (!this.bookBackTaskList) { + this.bookBackTaskList = await BookBackTaskListService.getInstance() + } + if (!this.bookTaskDetail) { + this.bookTaskDetail = await BookTaskDetailService.getInstance() + } + if (!this.mjApi) { + this.mjApi = new MJApi() + } + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + if (!this.bookService) { + this.bookService = await BookService.getInstance() + } + if (!this.mjSettingService) { + this.mjSettingService = await MJSettingService.getInstance() + } + this.mjSetting = await this.mjApi.InitMJSetting() + + } + /** + * 返回MJ的数据到前端界面,前端界面做出相应的改变 + * @param {*} data + */ + sendChangeMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN) { + global.newWindow[0].win.webContents.send(message_name, data) + } + + //#region 选择反推的提示词相关 + + /** + * 将MJ反推出来的数据,选择指定的写入到GPT提示词中 + * @param bookId 小说的ID + * @param bookTaskId 小说的任务ID + * @param index 反推的数据的索引 + * @returns + */ + async ReversePromptToGptPrompt(bookId: string, bookTaskId: string, index: number): Promise { + try { + await this.InitService(); + let bookTaskDetails = this.bookTaskDetail.GetBookTaskData({ + bookId: bookId, + bookTaskId: bookTaskId + }).data as Book.SelectBookTaskDetail[] + + if (bookTaskDetails.length <= 0) { + throw new Error("没有找到对应的分镜数据") + } + + // 开始修改数据 + for (let i = 0; i < bookTaskDetails.length; i++) { + const element = bookTaskDetails[i]; + let reversePrompts = element.reversePrompt + let reversePrompt = reversePrompts[index - 1] + let gptPrompt = undefined + if (!isEmpty(reversePrompt.promptCN) && reversePrompt.promptCN != "") { + gptPrompt = reversePrompt.promptCN + } + if (!gptPrompt) { + gptPrompt = reversePrompt.prompt + } + // 开始修改 + this.bookTaskDetail.UpdateBookTaskDetail(element.id, { + gptPrompt: gptPrompt + }) + } + // 全部修改完毕,将修改后的数据返回 + let res = await this.reverseBook.GetBookTaskDetail(bookTaskId); + if (res.code == 0) { + throw new Error(res.message) + } + return successMessage(res.data, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt") + } catch (error) { + return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt") + } + } + + /** + * MJ反推,将反推的数据写入到GPT中 + * @param bookTaskDetailId + * @param index + */ + async SingleReverseToGptPrompt(bookTaskDetailId: string, index: number): Promise { + try { + await this.InitService(); + let bookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(bookTaskDetailId) + if (bookTaskDetail == null) { + throw new Error("没有找到对应的数据") + } + let reversePrompts = bookTaskDetail.reversePrompt + if (!reversePrompts || reversePrompts.length <= 0) { + throw new Error("没有找到对应的反推提示词数据") + } + let reversePrompt = reversePrompts[index] + let gptPrompt = reversePrompt.promptCN ? reversePrompt.promptCN : reversePrompt.prompt + // 开始修改 + this.bookTaskDetail.UpdateBookTaskDetail(bookTaskDetailId, { + gptPrompt: gptPrompt + }) + + // 保存完毕 + return successMessage(gptPrompt, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt") + + } catch (error) { + return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt") + } + } + //#endregion + + + //#region MJ反推相关 + /** + * 循环请求数据,判断反推任务是不是完成,返回数据 + * @param task + * @param reqRes + */ + async fetchWithRetry(task: TaskModal.Task, reqRes: string) { + while (true) { + try { + // 执行你的操作 + let task_res = await this.mjApi.GetMJAPITaskById(reqRes, task.id); + + // 判断他的状态是不是成功 + if (task_res.code == 0) { + // 反推失败 + this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, { + status: BookTaskStatus.REVERSE_FAIL + }); + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.FAIL, + errorMessage: task_res.message + }); + throw new Error(`${task_res.message}`); + } else { + if (task_res.progress == 100) { + task_res.type == MJRespoonseType.FINISHED; + + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.DONE + }); + + // 这边还要再处理一下数据,将反推获取的数据进行切割处理 + let reversePrompt = []; + if (task_res.prompt != undefined && task_res.prompt != "" && task_res.prompt != null) { + let string_res = task_res.prompt.split(/(?=\d️⃣)/).map(part => part.replace(/^\d️⃣\s*/, '').trim()); + reversePrompt = string_res.map((item) => { + return { + id: uuidv4(), + bookTaskDetailId: task.bookTaskDetailId, + prompt: item, + promptCN: item, + isSelect: false + }; + }); + } + + this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, { + status: BookTaskStatus.REVERSE_DONE, + reversePrompt: reversePrompt + }); + + task_res.prompt = JSON.stringify(reversePrompt); + task_res.id = task.bookTaskDetailId; + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_REVERSE, + id: task.bookTaskDetailId, + data: task_res + }); + break; + } else { + // 当获取的图片的进度小于100的时候,等待5秒继续监听 + await new Promise(resolve => setTimeout(resolve, 5000)); + } + } + + task_res.id = task.bookTaskDetailId; + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_REVERSE, + id: task.bookTaskDetailId, + data: task_res + }); + } catch (error) { + throw error; + } + } + }; + + /** + * MJ反推 + * @param task + */ + async MJImage2Text(task: TaskModal.Task): Promise { + try { + if (isEmpty(task.bookTaskDetailId)) { + throw new Error("MJ反推,没有找到对应的分镜信息") + } + await this.InitService() + + let bookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(task.bookTaskDetailId); + + let oldImagePath = bookTaskDetail.oldImage + if (isEmpty(oldImagePath)) { + throw new Error(`${bookTaskDetail.name} 没有需要反推的图片`); + } + oldImagePath = JoinPath(define.project_path, oldImagePath) + let imageBase64 = await GetImageBase64(oldImagePath) + // 这个就是任务ID + let reqRes = await this.mjApi.SubmitMJDescribe({ + image: imageBase64, + taskId: task.id + }) + if (reqRes == '23') { + // 任务队列过多,重新提交排队 + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.RECONNECT, + }) + // throw new Error(`任务队列过多,${task.bookTaskDetailId} 重新提交排队`); + return; + } + + this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, { + status: BookTaskStatus.REVERSE + }) + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_REVERSE, + id: task.bookTaskDetailId, + data: { + code: 1, + type: MJRespoonseType.UPDATED, + mjType: MJAction.DESCRIBE, + category: this.mjSetting.type, + message_id: reqRes, + id: task.bookTaskDetailId, + progress: 0, + status: "success" + } as MJ.MJResponseToFront + }) + + await this.fetchWithRetry(task, reqRes); + } catch (error) { + console.log(error.toString()) + let errorMsg = "MJ反推失败,失败信息如下:" + error.toString() + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.FAIL, + errorMessage: errorMsg + }) + this.sendChangeMessage({ + code: 0, + id: task.bookTaskDetailId, + type: ResponseMessageType.MJ_REVERSE, + dialogType: DialogType.NOTIFICATION, + message: errorMsg, + data: { + code: 0, + type: MJRespoonseType.UPDATED, + mjType: MJAction.DESCRIBE, + category: this.mjSetting.type, + message_id: undefined, + id: task.bookTaskDetailId, + progress: 0, + message: error.toString(), + status: "failure" + } + }) + return errorMessage(errorMsg, "MJReverse_MJImage2Text") + } + } + + //#endregion + + //#region 合并提示词相关 + /** + * MJ 进行合并提示词,通过类型进行判断是单个还是全部合并 + * @param id 合并的ID + * @param mergeType 合并的类型 + */ + async MergePrompt(id: string, mergeType: MergeType): Promise { + try { + await this.InitService() + + let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]; + let bookTask = undefined as Book.SelectBookTask; + + if (mergeType == MergeType.BOOKTASK) { + bookTaskDetail = this.bookTaskDetail.GetBookTaskData({ + bookTaskId: id + }).data + bookTask = this.bookTaskService.GetBookTaskDataById(id); + } else if (mergeType == MergeType.BOOKTASKDETAIL) { + bookTaskDetail = [this.bookTaskDetail.GetBookTaskDetailDataById(id)] + bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId); + } else { + throw new Error("未知的合并类型") + } + + // 获取合并的排序 + let imageBaseSetting = JSON.parse(await fspromise.readFile(define.img_base, 'utf-8')); // 没有就直接报错 + let promptSort = imageBaseSetting.prompt_sort; // 没有就直接报错 + if (!promptSort) { + throw new Error("未找到提示词排序,请先设置") + } + + // let suffixParam = imageBaseSetting.mj_config.image_suffix ; // 没有就直接报错 + let mjSettingDb = this.mjSettingService.GetMjSetting({}).data + if (mjSettingDb.length <= 0) { + throw new Error("请先添加MJ配置") + } + let suffixParam = mjSettingDb[0].imageSuffix + + // let styleString = ''; + // 拿到所有的风格 + let styleArr = await this.imageStyle.GetAllImageStyleList(bookTask.imageStyle, bookTask.customizeImageStyle); + let result = [] + + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + + let promptStr = ''; + for (let i = 0; i < promptSort.length; i++) { + const element = promptSort[i]; + promptStr += `${'${' + element.value + '}'} ` + } + console.log(promptStr) + + + // TODO 后面需要完善人物和场景的提示词 + let characterString = '' // 人物 + let sceneString = "" // 场景 + // 获取当前的自定义风格的垫图字符串 + + let styleString = "" + let style_url = '' + let sw = undefined + styleArr.forEach((item) => { + if (item.type && item.type == 'style_main') { + if (sw == undefined) { + sw = item.sref_sw + } + if (!isEmpty(item.image_url)) { + let url = item.image_url ? item.image_url : '' + style_url += ' ' + url + } + if (item.prompt) { + styleString += item.prompt + ',' + } + } else { + styleString += item.english_style + ',' + } + }) + + style_url = checkStringValueAddPrefix(style_url, '--sref ') + if (sw != undefined) { + style_url = checkStringValueAddSuffix(style_url, ` --sw ${sw}`) + } + let cref_url = '' + + promptStr = promptStr.replace('${style}', styleString) + promptStr = promptStr.replace('${character}', characterString) + promptStr = promptStr.replace('${scene}', sceneString) + promptStr = promptStr.replace( + '${prompt}', + checkStringValueAddSuffix(element.gptPrompt, ',') + ) + + // 判断是不是需要加前后缀 + if (bookTask.prefixPrompt) { + promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr + } + if (bookTask.prefixPrompt) { + promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt + } + promptStr = ' ' + promptStr; + promptStr += ` ${cref_url} ${style_url}${suffixParam}` + // 修改数据库数据 + this.bookTaskDetail.UpdateBookTaskDetail(element.id, { + prompt: promptStr + }) + // 写回数据 + result.push({ + id: element.id, + prompt: promptStr + }) + } + return successMessage(result, "MJ模式合并数据成功", "MJOpt_MergePrompt") + + } catch (error) { + return errorMessage("MJ合并提示词失败,错误信息如下:" + error.message, "MJOpt_MergePrompt") + } + } + //#endregion + + + //#region MJ生成图片相关 + /** + * 单个生成图片,将任务添加到队列中 + * @param id 要添加的ID + * @param operateBookType 操作的类型 + */ + async AddMJGenerateImageTask(id: string, operateBookType: OperateBookType): Promise { + try { + await this.InitService() + let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]; + // let bookTask = undefined as Book.SelectBookTask; + + if (operateBookType == OperateBookType.BOOKTASK) { + bookTaskDetail = this.bookTaskDetail.GetBookTaskData({ + bookTaskId: id + }).data + // bookTask = this.bookTaskService.GetBookTaskDataById(id); + } else if (operateBookType == OperateBookType.BOOKTASKDETAIL) { + bookTaskDetail = [this.bookTaskDetail.GetBookTaskDetailDataById(id)] + // bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId); + } else if (operateBookType == OperateBookType.UNDERBOOKTASK) { + let thisBookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(id); + if (thisBookTaskDetail == null) { + throw new Error("没有找到对应的数据") + } + // 获取批次的所有数据 + bookTaskDetail = this.bookTaskDetail.GetBookTaskData({ + bookTaskId: thisBookTaskDetail.bookTaskId + }).data + // bookTask = this.bookTaskService.GetBookTaskDataById(thisBookTaskDetail.bookTaskId); + bookTaskDetail = bookTaskDetail.filter((item) => item.no >= thisBookTaskDetail.no) // 需要包含自己 + } + else { + throw new Error("MJOpt_AddGenerateImageTask,未知的操作类型") + } + + // 将被锁定的数据过滤掉 + bookTaskDetail = bookTaskDetail.filter((item) => item.imageLock == false) + if (bookTaskDetail.length <= 0) { + throw new Error("没有找到可以生成图片的分镜,可能是已经被锁定") + } + + // 将任务添加到队列中 + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + let taskRes = await this.bookBackTaskList.AddBookBackTask(element.bookId, BookBackTaskType.MJ_IMAGE, TaskExecuteType.AUTO, element.bookTaskId, element.id + ); + if (taskRes.code == 0) { + throw new Error(taskRes.message) + } + // 添加返回日志 + await this.taskScheduler.AddLogToDB(element.bookId, BookBackTaskType.MJ_IMAGE, `添加 ${element.name} MJ生成视频任务成功`, element.bookTaskId, LoggerStatus.SUCCESS) + } + // 全部完毕 + return successMessage(null, "MJ添加生成图片任务成功", "MJOpt_AddGenerateImageTask") + } catch (error) { + return errorMessage("MJ添加生成图片任务失败,错误信息如下:" + error.message, "MJOpt_AddGenerateImageTask") + } + } + + /** + * 循环请求数据,判断反推任务是不是完成,返回数据 + * @param task + * @param reqRes + */ + async FetchImageTask(task: TaskModal.Task, reqRes: string, book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail) { + while (true) { + try { + // 执行你的操作 + let task_res = await this.mjApi.GetMJAPITaskById(reqRes, task.id); + task_res.id = task.bookTaskDetailId; + // 判断他的状态是不是成功 + if (task_res.code == 0) { + // 生图失败 + this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, { + status: BookTaskStatus.IMAGE_FAIL, + }); + this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { + mjApiUrl: this.mjApi.imagineUrl, + progress: 0, + category: this.mjApi.mjSetting.type, + imageClick: task_res.image_click, + imageShow: task_res.image_show, + messageId: task_res.message_id, + action: MJAction.IMAGINE, + status: 'error', + message: task_res.message + }) + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.FAIL, + errorMessage: task_res.message + }); + this.sendChangeMessage({ + code: 0, + type: ResponseMessageType.MJ_IMAGE, + id: task.bookTaskDetailId, + data: { + ...task_res, + status: "error" + }, + message: task_res.message + }); + return; + // throw new Error(`${task_res.message}`); + } else { + if (task_res.progress == 100) { + task_res.type == MJRespoonseType.FINISHED; + console.log(task.id, "22222") + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.DONE + }); + // 下载图片 + let imagePath = path.join(book.bookFolderPath, `data\\MJOriginalImage\\${task_res.message_id}.png`); + await CheckFolderExistsOrCreate(path.dirname(imagePath)) + await this.tools.downloadFileUrl(task_res.image_click, imagePath) + + // 进行图片裁剪 + let imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage')); + if (imageRes && imageRes.length < 4) { + throw new Error("图片裁剪失败") + } + + // 修改数据库数据,将图片保存到对应的文件夹中 + let firstImage = imageRes[0]; + if (book.type == BookType.ORIGINAL) { + await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`)); + } + let out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`) + await CopyFileOrFolder(firstImage, out_file); + task_res.outImagePath = firstImage; + task_res.subImagePath = imageRes; + + task_res.id = task.bookTaskDetailId; + // 修改分镜的数据 + this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, { + outImagePath: path.relative(define.project_path, out_file), + subImagePath: imageRes.map((item) => path.relative(define.project_path, item)) + }) + this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { + mjApiUrl: this.mjApi.imagineUrl, + progress: 100, + category: this.mjApi.mjSetting.type, + imageClick: task_res.image_click, + imageShow: task_res.image_show, + messageId: task_res.message_id, + action: MJAction.IMAGINE, + status: task_res.status, + }) + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_IMAGE, + id: task.bookTaskDetailId, + data: task_res + }); + break; + } + } + + // 这边也要修改数据 + task_res.id = task.bookTaskDetailId; + this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { + mjApiUrl: this.mjApi.imagineUrl, + progress: task_res.progress, + category: this.mjApi.mjSetting.type, + imageClick: task_res.image_click, + imageShow: task_res.image_show, + messageId: task_res.message_id, + action: MJAction.IMAGINE, + status: task_res.status, + message: task_res.message + }) + task_res.outImagePath = task_res.image_path; + + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_IMAGE, + id: task.bookTaskDetailId, + data: task_res + }); + // 当获取的图片的进度小于100的时候,等待5秒继续监听 + await new Promise(resolve => setTimeout(resolve, 5000)); + } catch (error) { + throw error; + } + } + }; + + /** + * MJ生成图片 + * @param task + */ + async MJImagine(task: TaskModal.Task): Promise { + try { + if (isEmpty(task.bookTaskDetailId)) { + throw new Error("MJ出图,没有找到对应的分镜信息") + } + await this.InitService() + let bookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(task.bookTaskDetailId); + if (bookTaskDetail == null) { + throw new Error("没有找到对应的分镜信息") + } + let book = this.bookService.GetBookDataById(bookTaskDetail.bookId) + if (book == null) { + throw new Error("没有找到对应的小说信息") + } + let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail.bookTaskId) + if (bookTask == null) { + throw new Error("没有找到对应的任务信息") + } + let prompt = bookTaskDetail.prompt + if (isEmpty(prompt)) { + throw new Error(`${bookTaskDetail.name} 没有找到对应的提示词`) + } + // 这个就是任务ID + let reqRes = await this.mjApi.SubmitMJImagineAPI(task.id, prompt) + if (reqRes == '23') { + console.log(task.id, "33333") + // 任务队列过多,重新提交排队 + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.RECONNECT, + }) + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_IMAGE, + id: task.bookTaskDetailId, + data: { + code: 1, + type: MJRespoonseType.UPDATED, + mjType: MJAction.IMAGINE, + category: this.mjSetting.type, + message_id: '', + id: task.bookTaskDetailId, + progress: 0, + status: "re_connect" + } as MJ.MJResponseToFront + }) + this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { + mjApiUrl: this.mjApi.imagineUrl, + progress: 0, + category: this.mjApi.mjSetting.type, + imageClick: "", + imageShow: "", + messageId: "", + action: MJAction.IMAGINE, + status: "re_connect", + }) + // throw new Error(`任务队列过多,${task.bookTaskDetailId} 重新提交排队`); + return; + } + + this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, { + status: BookTaskStatus.IMAGE + }) + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.RUNNING + }) + this.sendChangeMessage({ + code: 1, + type: ResponseMessageType.MJ_IMAGE, + id: task.bookTaskDetailId, + data: { + code: 1, + type: MJRespoonseType.UPDATED, + mjType: MJAction.IMAGINE, + category: this.mjSetting.type, + message_id: reqRes, + id: task.bookTaskDetailId, + progress: 0, + status: "submited" + } as MJ.MJResponseToFront + }) + await this.FetchImageTask(task, reqRes, book, bookTask, bookTaskDetail); + + } catch (error) { + console.log(error.toString()) + let errorMsg = "MJ生图失败,失败信息如下:" + error.toString() + console.log(task.id, "44444") + this.bookBackTaskList.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.FAIL, + errorMessage: errorMsg + }) + this.sendChangeMessage({ + code: 0, + id: task.bookTaskDetailId, + type: ResponseMessageType.MJ_IMAGE, + dialogType: DialogType.NOTIFICATION, + message: errorMsg, + data: { + code: 0, + type: MJRespoonseType.UPDATED, + mjType: MJAction.IMAGINE, + category: this.mjSetting.type, + message_id: undefined, + id: task.bookTaskDetailId, + progress: 0, + message: error.toString(), + status: "error" + } + }) + this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { + mjApiUrl: this.mjApi.imagineUrl, + progress: 0, + category: this.mjApi.mjSetting.type, + imageClick: "", + imageShow: "", + messageId: "", + action: MJAction.IMAGINE, + status: "error", + message: error.toString() + }) + return errorMessage(errorMsg, "MJReverse_MJImage2Text") + } + } + + //#endregion +} \ No newline at end of file diff --git a/src/main/Service/MJ/mjApi.ts b/src/main/Service/MJ/mjApi.ts new file mode 100644 index 0000000..d7f041a --- /dev/null +++ b/src/main/Service/MJ/mjApi.ts @@ -0,0 +1,289 @@ +import axios from "axios" +import { define } from "../../../define/define" +import { GetImageBase64 } from "../../../define/Tools/image" +import { MJImageType, MJRespoonseType, MJSpeed } from "../../../define/enum/mjEnum" +import { MJSettingService } from "../../../define/db/service/SoftWare/mjSettingService" +import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService" +import { BookBackTaskStatus } from "../../../define/enum/bookEnum" +import { MJSetting } from "../../../model/Setting/mjSetting" +import { GPT } from "../../Public/GPT" +import { MJ } from "../../../model/mj" +import { LaiAPIType } from "../../../define/enum/softwareEnum" + +/** + * 调用MJ的API类 + */ +class MJApi { + mjSetting: MJSetting.MjSetting + bootType: string + imagineUrl: string + fetchTaskUrl: string + describeUrl: string + + constructor() { + this.bootType = "MID_JOURNEY" + } + + async InitMJSetting(): Promise { + // 获取MJ配置,从数据库中 + let _mjSettingService = await MJSettingService.getInstance() + let mjSettings = _mjSettingService.GetMJSettingTreeData() + if (mjSettings.code == 0) { + throw new Error(mjSettings.message) + } + this.mjSetting = mjSettings.data + this.bootType = this.mjSetting.selectRobot == "niji" ? "NIJI_JOURNEY" : "MID_JOURNEY" + if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + this.imagineUrl = define.remotemj_api + 'mj/submit/imagine' + this.describeUrl = define.remotemj_api + 'mj/submit/describe' + this.fetchTaskUrl = define.remotemj_api + 'mj/task/${id}/fetch' + } else { + if (global.config.laiApiSelect == LaiAPIType.HK_PROXY) { + this.imagineUrl = define.hkServerUrl + 'mj/submit/imagine' + this.describeUrl = define.hkServerUrl + 'mj/submit/describe' + this.fetchTaskUrl = define.hkServerUrl + 'mj/task/${id}/fetch' + } else if (global.config.laiApiSelect == LaiAPIType.BAK_MAIN) { + this.imagineUrl = define.bakServerUrl + 'mj/submit/imagine' + this.describeUrl = define.bakServerUrl + 'mj/submit/describe' + this.fetchTaskUrl = define.bakServerUrl + 'mj/task/${id}/fetch' + } else { + let gpt = new GPT() + let mj_api = (await gpt.GetGPTBusinessOption('all', (value) => value.mj_url)).data + let mj_api_url_index = mj_api.findIndex((item) => item.value == this.mjSetting.apiSetting.mjApiUrl) + if (mj_api_url_index == -1) { + throw new Error('没有找到对应的MJ API的配置,请先检查配置') + } + this.imagineUrl = mj_api[mj_api_url_index].mj_url.imagine; + this.fetchTaskUrl = mj_api[mj_api_url_index].mj_url.once_get_task; + this.describeUrl = mj_api[mj_api_url_index].mj_url.imagine.replace("imagine", "describe"); + } + } + return mjSettings.data + } + + //#region 获取对应的任务,通过ID + /** + * 获取MJ的API任务 + * @param taskId + * + */ + async GetMJAPITaskById(taskId: string, backTaskId: string) { + try { + + await this.InitMJSetting(); + let url = this.fetchTaskUrl.replace("${id}", taskId) + let headers = undefined + if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + headers = { + 'mj-api-secret': define.API + } + } else { + headers = { + Authorization: this.mjSetting.apiSetting.apiKey + } + } + + let res = await axios.get(url, { + headers: headers + }) + + let progress = res.data.progress && res.data.progress.length > 0 + ? parseInt(res.data.progress.slice(0, -1)) + : 0 + + let status = res.data.status.toLowerCase() + let code = status == 'failure' || status == 'cancel' ? 0 : 1 + + let _bookBackTaskListService = await BookBackTaskListService.getInstance() + // 失败 + if (code == 0) { + _bookBackTaskListService.UpdateTaskStatus({ + id: backTaskId, + status: BookBackTaskStatus.FAIL, + errorMessage: res.data.message + }) + } + let resObj = { + type: MJRespoonseType.UPDATED, + progress: isNaN(progress) ? 0 : progress, + category: this.mjSetting.type, + image_click: res.data.imageUrl, + image_show: res.data.imageUrl, + image_path: res.data.imageUrl, + message_id: taskId, + status: status, + code: code, + prompt: res.data.prompt == "" ? res.data.promptEn : res.data.prompt, + message: res.data.failReason, + mj_api_url: this.fetchTaskUrl, + } as MJ.MJResponseToFront + return resObj + } catch (error) { + throw error + } + } + //#endregion + + + //#region MJ反推相关操作 + + /** + * 提交MJ的反推 + */ + async SubmitMJDescribe(param: MJ.APIDescribeParams): Promise { + await this.InitMJSetting() + let res = undefined + switch (this.mjSetting.type) { + case MJImageType.REMOTE_MJ: + case MJImageType.API_MJ: + res = await this.SubmitMJDescribeAPI(param) + break + default: + throw new Error("MJ出图的类型不支持") + } + return res + } + /** + * API和代理模式的反推 + * @param param + * @returns 返回执行的任务的ID + */ + async SubmitMJDescribeAPI(param: MJ.APIDescribeParams): Promise { + let _bookBackTaskListService = await BookBackTaskListService.getInstance() + // 提交API的反推 + let data = { + botType: this.bootType, + base64: param.image, + accountFilter: { + modes: [this.mjSetting.apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"], + remark: global.machineId + }, + + } + let config = { + headers: { + 'Content-Type': 'application/json' + } + } + + if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + config.headers["mj-api-secret"] = define.API; + } else { + delete data.accountFilter.remark + config.headers["Authorization"] = this.mjSetting.apiSetting.apiKey; + } + + // 开始请求 + let res = await axios.post(this.describeUrl, data, config) + + // 某些API的返回的code为23,表示队列已满,需要重新请求 + if (res.data.code == 23) { + _bookBackTaskListService.UpdateTaskStatus({ + id: param.taskId, + status: BookBackTaskStatus.RECONNECT + }) + return '23' + } + + if (res.data.code != 1 && res.data.code != 22) { + _bookBackTaskListService.UpdateTaskStatus({ + id: param.taskId, + status: BookBackTaskStatus.FAIL, + errorMessage: res.data.description + }) + throw new Error(res.data.description) + } + + _bookBackTaskListService.UpdateTaskStatus({ + id: param.taskId, + status: BookBackTaskStatus.RUNNING + }) + + return res.data.result as string + } + //#endregion + + //#region 提交MJ生图任务 + + /** + * 提交MJ生图任务 + * @param param + * @returns + */ + async SubmitMJImagine(taskId: string, prompt: string): Promise { + await this.InitMJSetting() + let res = undefined + switch (this.mjSetting.type) { + case MJImageType.REMOTE_MJ: + case MJImageType.API_MJ: + res = await this.SubmitMJImagineAPI(taskId, prompt) + break + default: + throw new Error("MJ出图的类型不支持") + } + return res + } + + /** + * 提交MJ API/代理模式 出图任务 + * @param taskId + */ + async SubmitMJImagineAPI(taskId: string, prompt: string): Promise { + + let _bookBackTaskListService = await BookBackTaskListService.getInstance() + // 提交API的出图任务 + let data = { + botType: this.bootType, + prompt: prompt, + accountFilter: { + modes: [this.mjSetting.apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"], + remark: global.machineId + }, + + } + let config = { + headers: { + 'Content-Type': 'application/json' + } + } + + if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + config.headers["mj-api-secret"] = define.API; + } else { + delete data.accountFilter.remark + config.headers["Authorization"] = this.mjSetting.apiSetting.apiKey; + } + + // 开始请求 + let res = await axios.post(this.imagineUrl, data, config) + + // 某些API的返回的code为23,表示队列已满,需要重新请求 + if (res.data.code == 23) { + _bookBackTaskListService.UpdateTaskStatus({ + id: taskId, + status: BookBackTaskStatus.RECONNECT + }) + return '23' + } + + if (res.data.code != 1 && res.data.code != 22) { + _bookBackTaskListService.UpdateTaskStatus({ + id: taskId, + status: BookBackTaskStatus.FAIL, + errorMessage: res.data.description + }) + throw new Error(res.data.description) + } + + _bookBackTaskListService.UpdateTaskStatus({ + id: taskId, + status: BookBackTaskStatus.RUNNING + }) + + return res.data.result as string + } + + + //#endregion +} +export default MJApi \ No newline at end of file diff --git a/src/main/Service/MJ/mjReverse.ts b/src/main/Service/MJ/mjReverse.ts new file mode 100644 index 0000000..dd771c9 --- /dev/null +++ b/src/main/Service/MJ/mjReverse.ts @@ -0,0 +1,100 @@ +// import { isEmpty } from "lodash"; +// import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +// import { Book } from "../../../model/book"; +// import { GeneralResponse } from "../../../model/generalResponse"; +// import { ReverseBook } from "../Book/ReverseBook"; +// import { errorMessage, successMessage } from "../../Public/generalTools"; + +// export class MJReverse { +// bookTaskDetailService: BookTaskDetailService; +// reverseBook: ReverseBook; +// constructor() { } + +// private async InitService() { +// // 初始化一些服务 +// if (!this.bookTaskDetailService) { +// this.bookTaskDetailService = await BookTaskDetailService.getInstance() +// } +// if (!this.reverseBook) { +// this.reverseBook = new ReverseBook() +// } +// } + +// /** +// * 将MJ反推出来的数据,选择指定的写入到GPT提示词中 +// * @param bookId 小说的ID +// * @param bookTaskId 小说的任务ID +// * @param index 反推的数据的索引 +// * @returns +// */ +// async ReversePromptToGptPrompt(bookId: string, bookTaskId: string, index: number): Promise { +// try { +// await this.InitService(); +// let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({ +// bookId: bookId, +// bookTaskId: bookTaskId +// }).data as Book.SelectBookTaskDetail[] + +// if (bookTaskDetails.length <= 0) { +// throw new Error("没有找到对应的分镜数据") +// } + +// // 开始修改数据 +// for (let i = 0; i < bookTaskDetails.length; i++) { +// const element = bookTaskDetails[i]; +// let reversePrompts = element.reversePrompt +// let reversePrompt = reversePrompts[index - 1] +// let gptPrompt = undefined +// if (!isEmpty(reversePrompt.promptCN) && reversePrompt.promptCN != "") { +// gptPrompt = reversePrompt.promptCN +// } +// if (!gptPrompt) { +// gptPrompt = reversePrompt.prompt +// } +// // 开始修改 +// this.bookTaskDetailService.UpdateBookTaskDetail(element.id, { +// gptPrompt: gptPrompt +// }) +// } +// // 全部修改完毕,将修改后的数据返回 +// let res = await this.reverseBook.GetBookTaskDetail(bookTaskId); +// if (res.code == 0) { +// throw new Error(res.message) +// } +// return successMessage(res.data, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt") +// } catch (error) { +// return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt") +// } +// } + +// /** +// * MJ反推,将反推的数据写入到GPT中 +// * @param bookTaskDetailId +// * @param index +// */ +// async SingleReverseToGptPrompt(bookTaskDetailId: string, index: number): Promise { +// try { +// await this.InitService(); +// let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId) +// if (bookTaskDetail == null) { +// throw new Error("没有找到对应的数据") +// } +// let reversePrompts = bookTaskDetail.reversePrompt +// if (!reversePrompts || reversePrompts.length <= 0) { +// throw new Error("没有找到对应的反推提示词数据") +// } +// let reversePrompt = reversePrompts[index] +// let gptPrompt = reversePrompt.promptCN ? reversePrompt.promptCN : reversePrompt.prompt +// // 开始修改 +// this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, { +// gptPrompt: gptPrompt +// }) + +// // 保存完毕 +// return successMessage(gptPrompt, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt") + +// } catch (error) { +// return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt") +// } +// } +// } \ No newline at end of file diff --git a/src/main/Service/SD/sd.ts b/src/main/Service/SD/sd.ts new file mode 100644 index 0000000..baa529e --- /dev/null +++ b/src/main/Service/SD/sd.ts @@ -0,0 +1,136 @@ +import { MergeType } from "../../../define/enum/bookEnum"; +import { Book } from "../../../model/book"; +import { GeneralResponse } from "../../../model/generalResponse"; +import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools"; +import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; +import { define } from '../../../define/define' +import fs from "fs"; +import { ImageStyleDefine } from "../../../define/iamgeStyleDefine"; +import { ImageStyle } from "../Book/imageStyle"; +const fspromise = fs.promises + +export class SDOpt { + bookTaskDetailService: BookTaskDetailService + bookTaskService: BookTaskService + imageStyle: ImageStyle + constructor() { + + } + + + async InitService() { + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance() + } + if (!this.bookTaskService) { + this.bookTaskService = await BookTaskService.getInstance() + } + if (!this.imageStyle) { + this.imageStyle = new ImageStyle() + } + } + + /** + * SD的提示词合并 + * @param id 要处理的ID + * @param mergeType 合并的类型(用于判断是单个还是批量) + * @returns + */ + async MergePrompt(id: string, mergeType: MergeType): Promise { + try { + await this.InitService() + let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]; + let bookTask = undefined as Book.SelectBookTask; + + if (mergeType == MergeType.BOOKTASK) { + bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookTaskId: id + }).data + bookTask = this.bookTaskService.GetBookTaskDataById(id); + } else if (mergeType == MergeType.BOOKTASKDETAIL) { + bookTaskDetail = [this.bookTaskDetailService.GetBookTaskDetailDataById(id)]; + bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId); + } else { + throw new Error("未知的合并类型") + } + + // 获取合并的排序 + let promptSort = JSON.parse(await fspromise.readFile(define.img_base, 'utf-8')).prompt_sort; // 没有就直接报错 + if (!promptSort) { + throw new Error("未找到提示词排序,请先设置") + } + + let styleString = ''; + // 拿到所有的风格 + let styleArr = await this.imageStyle.GetAllImageStyleList(bookTask.imageStyle, bookTask.customizeImageStyle); + for (let i = 0; i < styleArr.length; i++) { + const element = styleArr[i] + if (element.type == 'style_main') { + styleString += element.prompt + ', ' + if (element.lora && element.lora != '无' && element.lora_weight) { + styleString += `, ` + } + } else { + styleString += element.english_style + ',' + } + } + + // 获取SD的通用前缀 + let sdGlobalPrompt = JSON.parse(await fspromise.readFile(define.sd_setting, 'utf-8')).webui.prompt; + + // TODO 反推这边目前就只有风格和提示词,暂时没有人物和场景 + let sceneString = ""; // 场景 + let characterString = ""; // 人物 + + + let result = []; // 返回前端的数据数组 + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + + let promptStr = ''; + for (let i = 0; i < promptSort.length; i++) { + const element = promptSort[i]; + promptStr += `${'${' + element.value + '}'} ` + } + + // 开始合并 + promptStr = promptStr.replace('${style}', styleString) // 风格 + promptStr = promptStr.replace('${character}', characterString) // 人物 + promptStr = promptStr.replace('${scene}', sceneString) // 场景 + promptStr = promptStr.replace( + '${prompt}', + checkStringValueAddSuffix(element.gptPrompt, ',') + ) + + // 判断是不是需要加前后缀 + if (bookTask.prefixPrompt) { + promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr + } + if (bookTask.prefixPrompt) { + promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt + } + if (sdGlobalPrompt) { + promptStr = checkStringValueAddSuffix(sdGlobalPrompt, ',') + promptStr + } + + promptStr = ' ' + promptStr; + console.log(promptStr) + // 修改数据库数据 + this.bookTaskDetailService.UpdateBookTaskDetail(element.id, { + prompt: promptStr + }) + // 写回数据 + result.push({ + id: element.id, + prompt: promptStr + }) + } + + return successMessage(result, "SD和并提示词数据成功", "SDOpt_MergePrompt") + + } catch (error) { + return errorMessage("SD合并提示词,错误信息如下:" + error.toString(), "SDOpt_MergePrompt") + } + } +} \ No newline at end of file diff --git a/src/main/Task/sdReverse.js b/src/main/Service/SD/sdReverse.ts similarity index 100% rename from src/main/Task/sdReverse.js rename to src/main/Service/SD/sdReverse.ts diff --git a/src/main/Service/Translate/Translate.ts b/src/main/Service/Translate/Translate.ts new file mode 100644 index 0000000..4fe3f44 --- /dev/null +++ b/src/main/Service/Translate/Translate.ts @@ -0,0 +1,1173 @@ +import { DEFINE_STRING } from "../../../define/define_string"; +const tencentcloud = require("tencentcloud-sdk-nodejs") +import { define } from "../../../define/define"; +import { MD5 } from "crypto-js"; +import axios from "axios"; +import path from "path"; +import { Tools } from "../../tools"; +import { successMessage } from "../../Public/generalTools"; +import { GeneralResponse } from "../../../model/generalResponse"; +import { TranslateModel } from "../../../model/translate"; +const alimt20181012 = require('@alicloud/alimt20181012'); +const OpenApi = require('@alicloud/openapi-client'); +const Util = require('@alicloud/tea-util'); +let fspromises = require("fs").promises; +import { SoftwareService } from "../../../define/db/service/SoftWare/softwareService"; +import { isEmpty } from "lodash"; +import { ValidateJson } from "../../../define/Tools/validate"; + +let { + Signer +} = require('@volcengine/openapi'); + +export class Translate { + tools: Tools; + softwareService: SoftwareService; + translationBusiness: string; + translationAppId: string; + translationSecret: string; + + constructor() { + this.tools = new Tools(); + } + + /** + * 初始化翻译设置 + */ + private async InitTranslate() { + if (!this.softwareService) { + this.softwareService = await SoftwareService.getInstance(); + } + // 获取翻译设置 + if (!this.translationBusiness || !this.translationAppId || !this.translationSecret) { + let translateSetting = this.softwareService.GetSoftWarePropertyData("translationSetting"); + if (isEmpty(translateSetting)) { + throw new Error("翻译设置为空,请先设置"); + } + let tryParse = ValidateJson(translateSetting); + if (!tryParse) { + throw new Error("翻译设置的格式错误,请重置后重新添加"); + } + let translateSettingData = JSON.parse(translateSetting); + let selectModel = translateSettingData.selectModel; + let translateIndex = translateSettingData.translates.findIndex(item => item.name == selectModel); + if (translateIndex < 0) { + throw new Error("没有找到对应的翻译API设置"); + } + let translateData = translateSettingData.translates[translateIndex]; + for (const key in translateData) { + if (!translateData[key]) { + throw new Error(`翻译设置中的 ${key} 不能为空`); + } + } + this.translationBusiness = translateData.translation_business; + this.translationAppId = translateData.translation_app_id; + this.translationSecret = translateData.translation_secret; + } + } + + // /** + // * 将整句翻译的任务添加到数据库队列中 + // * @param {*} value + // * 0 第 0 个参数是要翻译的数据(数组) + // * 1 第 1 个参数是源语言 + // * 2 第 2 个参数是目标语言 + // * 3 第 3 个参数是否分割(默认不分割false) + // * 4 第 4 个参数是要不要全局弹窗提示 + // * @returns + // */ + // async TranslateReturnNowTask(value) { + // try { + + // value = JSON.parse(value); + // let data = value[0]; + // let to = value[2]; + // let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_RETURN_NOW_TASK; + // for (let i = 0; i < data.length; i++) { + // const element = data[i]; + // // 添加任务到队列 + // global.requestQuene.enqueue(async () => { + // try { + // let res = await this.TranslateReturnNow([element.gpt_prompt, value[1], to, value[3], value[4]]); + // if (res.code != 1) { + // throw new Error(res.message); + // } + // let res_p = null; + + // if (!value[3]) { + // if (to == "zh") { + // res_p = res.data.map(item => item.dst).join(","); + // } else { + // res_p = res.data.map(item => item.src).join(","); + // } + // } else { + // res_p = res.data; + // } + + // // 修改chinese_prompt + // global.fileQueue.enqueue(async () => { + // let json_path = path.join(global.config.project_path, `tmp/input_crop/${element.name}.json`); + // let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); + // if (!value[3]) { + // prompt_json.gpt_prompt = res_p; + // } else { + // prompt_json.chinese_prompt = res_p; + // } + // await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); + // }) + + // global.newWindow[0].win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + // code: 1, + // to: to, + // rowId: element.id, + // data: res_p, + // }) + + + // } catch (error) { + // throw error; + // } + + // }, `${batch}_${element.name}`, batch) + // } + // // 监听总批次完成 + // global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + // if (failedTasks.length > 0) { + // let message = ` + // 翻译任务都已完成。 + // 但是以下任务执行失败: + // ` + // failedTasks.forEach(({ taskId, error }) => { + // message += `${taskId}-, \n 错误信息: ${error}` + '\n'; + // }); + + // global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + // code: 0, + // message: message + // }) + // } else { + // if (value[4]) { + // global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + // code: 1, + // message: "翻译任务完成" + // }) + // } + // } + // }) + // return { + // code: 1, + // message: "翻译任务已加入队列任务中" + // } + // } catch (error) { + // return { + // code: 0, + // message: "翻译任务出错,错误信息: " + error.toString() + // } + // } + // } + + /** + * + * @param {*} value 0:当前要翻译的字符串 + * 1:源语言 + * 2:目标语言 + * 3:是否拆分(以逗号拆分) + * [tags,'zh','en',false] + */ + async TranslateReturnNow(value: TranslateModel.TranslateNowReturnParams): Promise { + try { + await this.InitTranslate(); + // 百度翻译 + if (this.translationBusiness.includes("baidu")) { + return await this.TranslateReturnNowBaidu(value); + } else if (this.translationBusiness.includes("volcengine")) { + // 火山引擎 + return await this.TranslateReturnNowVolcengine(value); + } else if (this.translationBusiness.includes("tencent")) { + // 腾讯翻译 + return await this.TranslateReturnNowTencent(value); + } else if (this.translationBusiness.includes("aliyun")) { + return await this.TranslateReturnNowAliyun(value); + } else { + throw new Error("没有找到对应的翻译API") + } + + } catch (error) { + throw error; + } + } + + /** + * 添加翻译任务到队列中 + * @param { + * translateData :要翻译的数据 + * from : 源语言 + * to : 目标语言 + * window.id : 显示的窗体的ID + * isShow : 是不是提示 + * [translateData, from, to, window.id,isShow] + * } value + */ + async TranslatePrompt(value) { + try { + await this.InitTranslate(); + value[0] = JSON.parse(value[0]) + // baidu翻译 + if (this.translationBusiness.includes("baidu")) { + return await this.TranslatePromptBaidu(value); + } else if (this.translationBusiness.includes("volcengine")) { + // 火山引擎 + return await this.TranslatePromptVolcengine(value); + } else if (this.translationBusiness.includes("tencent")) { + // 腾讯翻译 + return await this.TranslatePromptTencent(value); + } else if (this.translationBusiness.includes("aliyun")) { + // 阿里云翻译 + return await this.TranslatePromptAliyun(value); + } + + } catch (error) { + return { + code: 0, + message: error.toString() + } + } + } + + //#region 阿里云翻译 + /** + * 阿里云翻译写入队列中 + * @param {*} value + */ + async TranslatePromptAliyun(value) { + try { + let win = global.newWindow.filter(item => item.id == value[3])[0]; + if (!win) { + win = global.newWindow[0]; + } + let translateData = value[0]; + let from = value[1]; + let to = value[2]; + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; + + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i]; + global.requestQuene.enqueue(async () => { + if (translateData.length > 5) { + await this.tools.delay(2000); + } + let arr_data = []; + if (to == "zh") { + let tmp_data = element.prompt; + arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(","); + arr_data = arr_data.filter(item => item != '' && item != null); + } else if (to == "en") { + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j]; + if (item != "" && item != null) { + arr_data.push(item.dst); + } + } + } + // 如果为空(直接返回) + if (arr_data.length <= 0) { + return; + } + + let req_data = {}; + for (let j = 0; j < arr_data.length; j++) { + const element = arr_data[j]; + req_data[j.toString()] = element; + } + + let config = new OpenApi.Config({ + accessKeyId: this.translationAppId, + accessKeySecret: this.translationSecret, + }); + config.endpoint = `mt.cn-hangzhou.aliyuncs.com`; + + let client = new alimt20181012.default(config); + + let getBatchTranslateRequest = new alimt20181012.GetBatchTranslateRequest({ + apiType: 'translate_standard', + scene: 'general', + sourceLanguage: from, + targetLanguage: to, + formatType: 'text', + sourceText: JSON.stringify(req_data), + }); + let runtime = new Util.RuntimeOptions({}); + + // 复制代码运行请自行打印 API 的返回值 + let res = await client.getBatchTranslateWithOptions(getBatchTranslateRequest, runtime); + console.log(res); + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.body.translatedList; + if (translateList.length != arr_data.length) { + throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); + } + + let res_data = []; + if (to == "zh") { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j]; + let res_tmp = translateList.find(item => item.index == j); + let obj = { + src: item, + dst: res_tmp.translated, + } + res_data.push(obj); + } + } else if (to == 'en') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j]; + // 获取指定的index的返回数据 + let res_tmp = translateList.find(item => item.index == j); + let obj = { + src: res_tmp.translated, + dst: item + } + res_data.push(obj); + } + } + // 数据返回。写入本地配置文件 + // 修改chinese_prompt + global.fileQueue.enqueue(async () => { + let json_path = path.join(global.config.project_path, `tmp/input_crop/${element.name}.json`); + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); + prompt_json.chinese_prompt = res_data; + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); + }) + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data, + }) + + }, `${batch}_${element.name}`, batch) + } + // 监听总批次完成 + global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n'; + }); + + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: "批次翻译任务完成" + }) + } + } + }) + return { + code: 1, + message: "翻译任务已加入队列任务中" + } + + } catch (error) { + throw error; + } + } + + /** + * 阿里云翻译实时返回 + * @param {*} value + */ + async TranslateReturnNowAliyun(value: TranslateModel.TranslateNowReturnParams): Promise { + try { + // 判断该当前的翻译API + let from = value.from; + let to = value.to; + let ts_d = value.text.replaceAll("_", " ").replaceAll(',', ","); + let req_data = {}; + let req_count = 0; + let req_arr = []; + if (value.isSplit) { + let tmp_arr = ts_d.split(','); + for (let i = 0; i < tmp_arr.length; i++) { + const element = tmp_arr[i]; + if (element != '' && element != null) { + req_data[i.toString()] = element; + req_arr.push(element); + } + req_count += 1; + } + } else { + req_data["0"] = ts_d; + req_count = 1; + req_arr.push(ts_d); + } + if (req_count <= 0) { + throw new Error("没有传入数据"); + } + + let config = new OpenApi.Config({ + accessKeyId: this.translationAppId, + accessKeySecret: this.translationSecret, + }); + config.endpoint = `mt.cn-hangzhou.aliyuncs.com`; + + let client = new alimt20181012.default(config); + + let getBatchTranslateRequest = new alimt20181012.GetBatchTranslateRequest({ + apiType: 'translate_standard', + scene: 'general', + sourceLanguage: from, + targetLanguage: to, + formatType: 'text', + sourceText: JSON.stringify(req_data), + }); + let runtime = new Util.RuntimeOptions({}); + + // 复制代码运行请自行打印 API 的返回值 + let res = await client.getBatchTranslateWithOptions(getBatchTranslateRequest, runtime); + console.log(res); + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.body.translatedList; + if (translateList.length != req_count) { + throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); + } + // { + // "src": "blush", + // "dst": "脸红" + // } + // 数据处理 + let res_data = []; + for (let j = 0; j < req_arr.length; j++) { + const item = req_arr[j]; + let res_tmp = translateList.find(item => item.index == j); + if (to == "zh") { + let obj = { + src: item, + dst: res_tmp.translated + } + res_data.push(obj); + } else if (to == "en") { + let obj = { + src: res_tmp.translated, + dst: item + } + res_data.push(obj); + } + } + + // 直接返回数据 + return successMessage({ + to: to, + data: res_data + }, '翻译成功', 'Translate_TranslateReturnNowAliyun'); + } catch (error) { + throw error; + } + } + + //#endregion + + //#region 腾讯翻译 + + /** + * 腾讯翻译实时返回 + * @param {*} value + */ + async TranslateReturnNowTencent(value: TranslateModel.TranslateNowReturnParams): Promise { + try { + // 判断该当前的翻译API + let from = value.from; + let to = value.to; + let ts_d = value.text.replaceAll("_", " ").replaceAll(',', ","); + let req_data = []; + if (value.isSplit) { + req_data = ts_d.split(','); + } else { + req_data.push(ts_d) + } + req_data = req_data.filter(item => item != "" && item != null); + if (req_data.length <= 0) { + throw new Error("没有传入数据"); + } + const CvmClient = tencentcloud.tmt.v20180321.Client; + const client = new CvmClient({ + credential: { + secretId: this.translationAppId, + secretKey: this.translationSecret + }, + // 产品地域 + region: "ap-shanghai", + // 可选配置实例 + profile: { + signMethod: "TC3-HMAC-SHA256", // 签名方法 + httpProfile: { + reqMethod: "POST", // 请求方法 + reqTimeout: 60, // 请求超时时间,默认60s + }, + }, + }) + + let res = await client.TextTranslateBatch({ + SourceTextList: req_data, + Source: from, + Target: to, + ProjectId: 0 + }); + console.log(res); + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.TargetTextList; + if (translateList.length != req_data.length) { + throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); + } + // { + // "src": "blush", + // "dst": "脸红" + // } + // 数据处理 + let res_data = []; + for (let j = 0; j < req_data.length; j++) { + const item = req_data[j]; + if (to == "zh") { + let obj = { + src: item, + dst: translateList[j] + } + res_data.push(obj); + } else if (to == "en") { + let obj = { + src: translateList[j], + dst: item + } + res_data.push(obj); + } + } + + // 直接返回数据 + return successMessage({ + to: to, + data: res_data + }, "翻译成功", 'Translate_TranslateReturnNowTencent'); + } catch (error) { + throw error; + } + } + + /** + * 腾讯翻译将翻译的消息写入到队列中 + * @param {*} value + */ + async TranslatePromptTencent(value) { + try { + let win = global.newWindow.filter(item => item.id == value[3])[0]; + if (!win) { + win = global.newWindow[0]; + } + let translateData = value[0]; + let from = value[1]; + let to = value[2]; + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; + let secretId = this.translationAppId; + let secretKey = this.translationSecret; + const CvmClient = tencentcloud.tmt.v20180321.Client + const client = new CvmClient({ + credential: { + secretId: secretId, + secretKey: secretKey + }, + region: "ap-shanghai", + profile: { + signMethod: "TC3-HMAC-SHA256", // 签名方法 + httpProfile: { + reqMethod: "POST", // 请求方法 + reqTimeout: 30, // 请求超时时间,默认60s + }, + }, + }) + + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i]; + global.requestQuene.enqueue(async () => { + if (translateData.length > 5) { + await this.tools.delay(2000); + } + let arr_data = []; + if (to == "zh") { + let tmp_data = element.prompt; + arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(","); + arr_data = arr_data.filter(item => item != '' && item != null); + } else if (to == "en") { + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j]; + if (item != "" && item != null) { + arr_data.push(item.dst); + } + } + } + // 如果为空(直接返回) + if (arr_data.length <= 0) { + return; + } + // 请求数据 + let req_data = { + Source: from, + Target: to, + SourceTextList: arr_data, + ProjectId: 0 + } + + let res = await client.TextTranslateBatch(req_data); + console.log(res); + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.TargetTextList; + if (translateList.length != arr_data.length) { + throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); + } + + let res_data = []; + // { + // "src": "blush", + // "dst": "脸红" + // } + if (to == "zh") { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j]; + let obj = { + src: item, + dst: translateList[j] + } + res_data.push(obj); + } + } else if (to == 'en') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j]; + let obj = { + src: translateList[j], + dst: item + } + res_data.push(obj); + } + } + // 数据返回。写入本地配置文件 + // 修改chinese_prompt + global.fileQueue.enqueue(async () => { + let json_path = path.join(global.config.project_path, `tmp/input_crop/${element.name}.json`); + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); + prompt_json.chinese_prompt = res_data; + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); + }) + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data, + }) + + }, `${batch}_${element.name}`, batch) + } + // 监听总批次完成 + global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n'; + }); + + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: "批次翻译任务完成" + }) + } + } + }) + return { + code: 1, + message: "翻译任务已加入队列任务中" + } + + } catch (error) { + throw error; + } + } + + //#endregion + + //#region 火山引擎翻译 + + /** + * 火山引擎翻译实时返回 + * @param {*} value + */ + async TranslateReturnNowVolcengine(value: TranslateModel.TranslateNowReturnParams): Promise { + try { + // 判断该当前的翻译API + let from = value.from; + let to = value.to; + let ts_d = value.text.replaceAll("_", " ").replaceAll(',', ","); + let req_data = []; + if (value.isSplit) { + req_data = ts_d.split(','); + } else { + req_data.push(ts_d) + } + if (req_data.length <= 0) { + throw new Error("没有传入数据"); + } + let signer = await this.GetVolcengineSinger(); + + let config = { + method: 'post', + maxBodyLength: Infinity, + url: `${this.translationBusiness}${signer}`, + headers: { + 'Content-Type': 'application/json' + }, + data: JSON.stringify({ + SourceLanguage: from, + TargetLanguage: to, + TextList: req_data + }) + }; + let res = await axios.request(config); + if (res.status != 200) { + throw new Error("请求错误。请检查网络"); + } + // 判断是不是有返回错误 + if (res.data.ResponseMetadata && res.data.ResponseMetadata.Error) { + let err = res.data.ResponseMetadata.Error; + throw new Error(`错误码: ${err.Code} 错误编号:${err.CodeN} 错误详细信息:${err.Message}`); + } + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.data.TranslationList; + if (translateList.length != req_data.length) { + throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); + } + // { + // "src": "blush", + // "dst": "脸红" + // } + // 数据处理 + let res_data = []; + for (let j = 0; j < req_data.length; j++) { + const item = req_data[j]; + if (to == "zh") { + let obj = { + src: item, + dst: translateList[j].Translation + } + res_data.push(obj); + } else if (to == "en") { + let obj = { + src: translateList[j].Translation, + dst: item + } + res_data.push(obj); + } + } + + // 直接返回数据 + return successMessage({ + to: to, + data: res_data + }, "翻译成功", 'Translate_TranslateReturnNowVolcengine'); + } catch (error) { + throw error; + } + } + + + /** + * 火山引擎翻译所有数据队列返回 + * @param {*} value + */ + async TranslatePromptVolcengine(value) { + try { + let win = global.newWindow.filter(item => item.id == value[3])[0]; + if (!win) { + win = global.newWindow[0]; + } + let signer = await this.GetVolcengineSinger(); + let translateData = value[0]; + let from = value[1]; + let to = value[2]; + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i]; + global.requestQuene.enqueue(async () => { + if (translateData.length > 5) { + await this.tools.delay(2000); + } + let arr_data = []; + if (to == "zh") { + let tmp_data = element.prompt; + arr_data = tmp_data.replaceAll('_', ' ').replaceAll(',', ',').split(","); + arr_data = arr_data.filter(item => item != '' && item != null); + } else if (to == "en") { + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j]; + if (item != "" && item != null) { + arr_data.push(item.dst); + } + } + } + // 如果为空(直接返回) + if (arr_data.length <= 0) { + return; + } + // 开始请求 + let req_data = JSON.stringify({ + SourceLanguage: from, + TargetLanguage: to, + TextList: arr_data + }) + let config = { + method: "post", + maxBodyLength: Infinity, + url: `${this.translationBusiness}${signer}`, + headers: { + 'Content-Type': 'application/json' + }, + data: req_data + } + let res = await axios.request(config); + console.log(res); + if (res.status != 200) { + throw new Error("请求状态码错误。请检查网络"); + } + + // 判断是不是有返回错误 + if (res.data.ResponseMetadata && res.data.ResponseMetadata.Error) { + let err = res.data.ResponseMetadata.Error; + throw new Error(`错误码: ${err.Code} 错误编号:${err.CodeN} 错误详细信息:${err.Message}`); + } + + // 处理返回的数据 + // 检出返回的数据和输入的数据是不是一样的 + let translateList = res.data.TranslationList; + if (translateList.length != arr_data.length) { + throw new Error("请求的数据长度和返回的数据长度不一致。请重试"); + } + + let res_data = []; + // { + // "src": "blush", + // "dst": "脸红" + // } + if (to == "zh") { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j]; + let obj = { + src: item, + dst: translateList[j].Translation + } + res_data.push(obj); + } + } else if (to == 'en') { + for (let j = 0; j < arr_data.length; j++) { + const item = arr_data[j]; + let obj = { + src: translateList[j].Translation, + dst: item + } + res_data.push(obj); + } + } + // 数据返回。写入本地配置文件 + // 修改chinese_prompt + global.fileQueue.enqueue(async () => { + let json_path = path.join(global.config.project_path, `tmp/input_crop/${element.name}.json`); + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); + prompt_json.chinese_prompt = res_data; + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); + }) + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data, + }) + + }, `${batch}_${element.name}`, batch) + } + // 监听总批次完成 + global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n'; + }); + + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: "批次翻译任务完成" + }) + } + } + }) + return { + code: 1, + message: "翻译任务已加入队列任务中" + } + + } catch (error) { + throw error; + } + } + + /** + * 获取火山引擎请求的签名 + */ + async GetVolcengineSinger() { + try { + const openApiRequestData = { + method: "POST", + region: "cn-north-1", + params: { + Action: "TranslateText", + Version: "2020-06-01", + }, + Service: "translate" + } + + const credentials = { + accessKeyId: this.translationAppId, + secretKey: this.translationSecret + } + + const signer = new Signer(openApiRequestData, "translate"); + + // 最终经过加签的 HTTP Query Params + const signedQueryString = signer.getSignUrl(credentials); + console.log(signedQueryString) + return signedQueryString; + + } catch (error) { + throw error; + } + } + + //#endregion + + //#region 百度翻译 + /** + * 百度引擎翻译翻译所有数据 + * @param {} value + * @returns + */ + async TranslatePromptBaidu(value) { + try { + let win = global.newWindow.filter(item => item.id == value[3])[0]; + if (!win) { + win = global.newWindow[0]; + } + let translateData = value[0]; + let from = value[1]; + let to = value[2]; + let batch = DEFINE_STRING.QUEUE_BATCH.TRANSLATE_PROMPT; + let appId = this.translationAppId; + + // 添加一个频次判断是不是演示 + + for (let i = 0; i < translateData.length; i++) { + const element = translateData[i]; + global.requestQuene.enqueue(async () => { + try { + if (translateData.length > 5) { + await this.tools.delay(2000); + } + let ts_d = ""; + if (to == "zh") { + ts_d = element.prompt.replaceAll("_", " ").replaceAll(',', ",").replaceAll(',', "\n"); + } else if (to == "en") { + let tmp_arr = []; + // 中文转英文。重新拼接一下 + for (let j = 0; j < element.chinese_prompt.length; j++) { + const item = element.chinese_prompt[j]; + tmp_arr.push(item.dst); + } + ts_d = tmp_arr.join('\n'); + } + let salt = Date.now(); + let sign = MD5(`${this.translationAppId}${ts_d}${salt}${this.translationSecret}`).toString(); + let res = await axios.get(this.translationBusiness, { + params: { + q: ts_d, + appid: appId, + salt: salt, + from: from, + to: to, + sign: sign + } + }); + if (res.status != 200) { + throw new Error("请求错误。请检查网络"); + } + // 判断是不是有错误码 + if (res.data.error_code) { + throw new Error(res.data.error_msg); + } + + let res_data = [] + // 将所有的数据协会到本地(然后发送消息到前台界面) + if (res.data.to == "zh") { + res_data = res.data.trans_result + } else { + // 直接在这边处理(前端不用处理) + for (let i = 0; i < res.data.trans_result.length; i++) { + const element = res.data.trans_result[i]; + let obj = { + src: element.dst, + dst: element.src + }; + res_data.push(obj); + } + } + + // 修改chinese_prompt + global.fileQueue.enqueue(async () => { + let json_path = path.join(global.config.project_path, `tmp/input_crop/${element.name}.json`); + let prompt_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8')); + prompt_json.chinese_prompt = res_data; + await fspromises.writeFile(json_path, JSON.stringify(prompt_json)); + }) + + win.win.webContents.send(DEFINE_STRING.TRANSLATE_RETURN_REFRESH, { + code: 1, + to: to, + rowId: element.id, + data: res_data, + }) + + } catch (error) { + throw error; + } + }, `${batch}_${element.name}`, batch); + + } + // 监听总批次完成 + global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { + if (failedTasks.length > 0) { + let message = ` + 翻译任务都已完成。 + 但是以下任务执行失败: + ` + failedTasks.forEach(({ taskId, error }) => { + message += `${taskId}-, \n 错误信息: ${error}` + '\n'; + }); + + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: message + }) + } else { + if (value[4]) { + global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 1, + message: "批次翻译任务完成" + }) + } + } + }) + return { + code: 1, + message: "翻译任务已加入队列任务中" + } + } catch (error) { + throw error; + } + } + + /** + * 百度翻译引擎翻译单个数据。立即返回 + * @param {*} value + * @returns + */ + async TranslateReturnNowBaidu(value: TranslateModel.TranslateNowReturnParams) { + try { + // 判断该当前的翻译API + let appId = this.translationAppId; + let ts_d = value.text.replaceAll("_", " ").replaceAll(',', ","); + if (value.isSplit) { + ts_d = ts_d.replaceAll('.', "\n"); + } + let salt = Date.now(); + let sign = MD5(`${this.translationAppId}${ts_d}${salt}${this.translationSecret}`).toString(); + let res = await axios.get(this.translationBusiness, { + params: { + q: ts_d, + appid: appId, + salt: salt, + from: value.from, + to: value.to, + sign: sign + } + }); + if (res.status != 200) { + throw new Error("请求错误。请检查网络"); + } + // 判断是不是有错误码 + if (res.data.error_code) { + throw new Error(res.data.error_msg); + } + + let res_data = [] + // 将所有的数据协会到本地(然后发送消息到前台界面) + if (res.data.to == "zh") { + res_data = res.data.trans_result + } else { + // 直接在这边处理(前端不用处理) + for (let i = 0; i < res.data.trans_result.length; i++) { + const element = res.data.trans_result[i]; + let obj = { + src: element.dst, + dst: element.src + }; + res_data.push(obj); + } + } + // 直接返回数据 + return successMessage({ + to: value.to, + data: res_data + }, '翻译成功', "Translate_TranslateReturnNowBaidu") + } catch (error) { + throw error; + } + } + //#endregion +} \ No newline at end of file diff --git a/src/main/Service/Translate/TranslateService.ts b/src/main/Service/Translate/TranslateService.ts new file mode 100644 index 0000000..830f15d --- /dev/null +++ b/src/main/Service/Translate/TranslateService.ts @@ -0,0 +1,226 @@ +import { GeneralResponse } from "../../../model/generalResponse"; +import { TranslateModel } from "../../../model/translate"; +import { errorMessage, successMessage } from "../../Public/generalTools"; +import { Translate } from "./Translate"; +import { DEFINE_STRING } from "../../../define/define_string" +import { TranslateAPIType, TranslateType } from "../../../define/enum/translate"; +import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; +import { Book } from "../../../model/book"; +import { ResponseMessageType } from "../../../define/enum/softwareEnum"; +import { SoftwareService } from '../../../define/db/service/SoftWare/softwareService' +import { isEmpty } from "lodash"; +import { ValidateJson } from "../../../define/Tools/validate"; + +/** + * 翻译实现服务 + */ +export class TranslateService { + translate: Translate + bookTaskDetail: BookTaskDetailService; + softwareService: SoftwareService + + constructor() { + } + + async InitService() { + if (!this.bookTaskDetail) { + this.bookTaskDetail = await BookTaskDetailService.getInstance() + } + if (!this.softwareService) { + this.softwareService = await SoftwareService.getInstance() + } + if (!this.translate) { + this.translate = new Translate() + } + } + + // 返回翻译结果。用于前端修改 + private sendTranslateReturn(windowId: number, data: GeneralResponse.MessageResponse): void { + let win = global.newWindow[0] + if (windowId) { + win = global.newWindow.filter(item => item.id == windowId)[0]; + } + win.win.webContents.send(DEFINE_STRING.BOOK.MAIN_DATA_RETURN, data) + } + + + //#region 翻译设置 + + /** + * 初始化翻译设置 + * @returns + */ + InitialTranslateSetting(): TranslateModel.TranslateModel { + return { + selectModel: TranslateAPIType.BAIDU, + translation_auto: false, + translates: [ + { + name: TranslateAPIType.BAIDU, + translation_business: "https://fanyi-api.baidu.com/api/trans/vip/translate", + translation_app_id: undefined, + translation_secret: undefined + }, + { + name: TranslateAPIType.TENCENT, + translation_business: "https://tmt.tencentcloudapi.com", + translation_app_id: undefined, + translation_secret: undefined + }, + { + name: TranslateAPIType.VOLCENGINE, + translation_business: "https://translate.volcengineapi.com?", + translation_app_id: undefined, + translation_secret: undefined + }, + { + name: TranslateAPIType.ALI, + translation_business: "https://mt.cn-hangzhou.aliyuncs.com", + translation_app_id: undefined, + translation_secret: undefined + } + ] + } + } + + /** + * 获取翻译设置 + */ + async GetTranslateSetting(): Promise { + try { + await this.InitService() + let translateSetting = undefined as TranslateModel.TranslateModel + let translateSettingString = this.softwareService.GetSoftWarePropertyData('translationSetting'); + if (isEmpty(translateSettingString)) { + // 初始化 + translateSetting = this.InitialTranslateSetting() + } else { + // 解析 + let tryParse = ValidateJson(translateSettingString) + if (!tryParse) { + throw new Error("翻译设置数据解析失败") + } + translateSetting = JSON.parse(translateSettingString); + } + return successMessage(translateSetting, "获取翻译设置成功", "TranslateService_GetTranslateSetting") + } catch (error) { + return errorMessage("获取翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_GetTranslateSetting") + } + } + + /** + * 重置翻译设置 + */ + async ResetTranslateSetting(): Promise { + try { + let translateSetting = this.InitialTranslateSetting() + let res = this.softwareService.SaveSoftwarePropertyData('translationSetting', JSON.stringify(translateSetting)) + return successMessage(translateSetting, "重置翻译设置成功", "TranslateService_ResetTranslateSetting") + + } catch (error) { + return errorMessage("重置翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_ResetTranslateSetting") + } + } + + /** + * 保存翻译设置 + * @param value 保存的数据 + * @returns + */ + async SaveTranslateSetting(value: TranslateModel.TranslateModel): Promise { + try { + let res = this.softwareService.SaveSoftwarePropertyData('translationSetting', JSON.stringify(value)) + return successMessage(value, "保存翻译设置成功", "TranslateService_SaveTranslateSetting") + } catch (error) { + return errorMessage("保存翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_SaveTranslateSetting") + + } + } + + + //#endregion + + + + + /** + * 反推翻译提示词处理 + * @param bookTaskDetailId 对应的分镜ID + * @param reversePromptId 对应的反推提示词数据ID + * @param to 目标语言 + * @param dstString 翻译后的字符串 + */ + private async TranslateProcessReversePrompt(bookTaskDetailId: string, reversePromptId: string, to: string, dstString: string): Promise { + // 开始修改翻译后的数据 + let updateData = {} as Book.ReversePrompt + if (to == "zh") { + updateData.promptCN = dstString + } else if (to == "en") { + updateData.prompt = dstString + updateData.promptCN = dstString + } + // 修改数据 + this.bookTaskDetail.UpdateBookTaskDetailReversePrompt(bookTaskDetailId, reversePromptId, updateData) + } + + // 处理返回的数据 + async TranslateReturnProcess(value: TranslateModel.TranslateNowIPCParams, to: string, dstString: string) { + switch (value.type) { + case TranslateType.REVERSE_PROMPT_TRANSLATE: + await this.TranslateProcessReversePrompt(value.bookTaskDetailId, value.reversePromptId, to, dstString) + break; + default: + throw new Error("未知的翻译类型"); + } + } + + // 翻译 + async TranslateNowReturn(value: TranslateModel.TranslateNowIPCParams[]): Promise { + try { + await this.InitService() + // 循环所有的数据,返回翻译结果 + for (let i = 0; i < value.length; i++) { + const element = value[i]; + let res = await this.translate.TranslateReturnNow(element) + // 单个翻译,将返回的数据写入到原数据中 + + // 添加一个对返回信息进行处理的函数 + let data = res.data + // 先将数据进行拼接 + let srcString = "" + if (element.isSplit) { + let dstStrs = [] + + } else { + // 没有拆分的,只有一句 + srcString = data.data[0].dst + } + // 写回数据库 + await this.TranslateReturnProcess(element, data.to, srcString); + + // 做个返回数据 + this.sendTranslateReturn(element.windowId, { + code: 1, + id: element.bookTaskDetailId, + type: ResponseMessageType.PROMPT_TRANSLATE, + data: { + progress: i + 1, + total: value.length, + from: element.from, + to: data.to, + bookTaskDetailId: element.bookTaskDetailId, + reversePromptId: element.reversePromptId, + prompt: srcString, + promptCN: srcString + } + }) + } + + return successMessage(null, "全部翻译完成", "TranslateService_TranslateNowReturn") + + } catch (error) { + return errorMessage("翻译失败,失败信息如下:" + error.toString(), "TranslateService_TranslateNowReturn") + } + } + +} \ No newline at end of file diff --git a/src/main/Task/ffmpegOptions.js b/src/main/Service/ffmpegOptions.ts similarity index 80% rename from src/main/Task/ffmpegOptions.js rename to src/main/Service/ffmpegOptions.ts index 516a62d..8fbfef1 100644 --- a/src/main/Task/ffmpegOptions.js +++ b/src/main/Service/ffmpegOptions.ts @@ -1,11 +1,11 @@ import path from 'path' -import { TaskScheduler } from './taskScheduler' -import { errorMessage, successMessage } from '../generalTools' -import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file' +import { errorMessage, successMessage } from '../Public/generalTools' +import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from '../../define/Tools/file' import { MillisecondsToTimeString } from '../../define/Tools/time' import Ffmpeg from 'fluent-ffmpeg' import { SetFfmpegPath } from '../setting/ffmpegSetting' import fs from 'fs' +import { GeneralResponse } from '../../model/generalResponse' const fspromises = fs.promises SetFfmpegPath() @@ -14,8 +14,11 @@ SetFfmpegPath() * FFmpeg 封装的一些操作 */ export class FfmpegOptions { + ecode: string // 编码的方式 + dcode: string // 解码的方式 + + constructor() { - this.taskScheduler = new TaskScheduler() } InitCodec() { @@ -33,6 +36,64 @@ export class FfmpegOptions { } else if (global.gpu.type === 'AMD') { videoDcodec = 'h264_amf' } + this.dcode = videoDcodec + } + + /** + * 压缩视频文件 + * @param videoPath 文件地址 + * @param wh 宽高比 + * @param crf crf值 + * @returns + */ + async FfmpegCompressVideo(videoPath: string, maxV: number, crf: string): Promise { + // 判断视频地址是不是存在 + let videoIsExist = await CheckFileOrDirExist(videoPath) + if (!videoIsExist) { + throw new Error('视频地址对应的文件不存在') + } + if (!videoPath.toLowerCase().endsWith('.mp4')) { + throw new Error("只支持MP4文件"); + } + let tempVideo = path.join(path.dirname(videoPath), "tmp.mp4"); + await CopyFileOrFolder(videoPath, tempVideo); + + await fspromises.unlink(videoPath) + let wh = await this.FfmpegGetVideoSize(tempVideo) + if (wh.code != 1) { + throw new Error(wh.message) + } + let rate = undefined + let width = undefined + let height = undefined + // 计算尺寸 + if (wh.data.width > wh.data.height) { + rate = maxV / wh.data.width + width = maxV + height = wh.data.height * rate + } else { + height = maxV + rate = maxV / wh.data.height + width = wh.data.width * rate + } + + return new Promise((resolve, reject) => { + Ffmpeg(tempVideo) + .outputOptions([ + `-vf scale=${width}:${height}`, // 调整视频尺寸到 1280x720 + `-b:v ${crf}` // 设置视频比特率为 1000kbps + ]) + .on('end', async () => { + // 删除缓存文件 + await fspromises.unlink(tempVideo) + resolve('视频压缩处理完成'); + }) + .on('error', (err) => { + reject('视频压缩处理失败: ' + err.toString()); + }) + .save(videoPath) + + }) } /** @@ -188,7 +249,7 @@ export class FfmpegOptions { * @param {*} videoPath 视频文件地址 * @returns */ - async FfmpegGetVideoSize(videoPath) { + async FfmpegGetVideoSize(videoPath: string): Promise { try { let videoIsExist = await CheckFileOrDirExist(videoPath) if (videoIsExist == false) { @@ -267,6 +328,13 @@ export class FfmpegOptions { throw new Error(frameRes.message) } let outImagePaths = [] + if(await CheckFileOrDirExist(outImagePath) == false){ + return successMessage( + outImagePaths, + '获取指定位置的帧和裁剪成功', + 'WatermarkAndSubtitle_FfmpegGetVideoFramdAndClip' + ) + } // 这边可以会裁剪多个,所以需要循环 for (let i = 0; i < clipRanges.length; i++) { const element = clipRanges[i] diff --git a/src/main/Task/watermarkAndSubtitle.js b/src/main/Service/subtitle.ts similarity index 74% rename from src/main/Task/watermarkAndSubtitle.js rename to src/main/Service/subtitle.ts index 73f9c82..97e0261 100644 --- a/src/main/Task/watermarkAndSubtitle.js +++ b/src/main/Service/subtitle.ts @@ -1,6 +1,6 @@ import { isEmpty } from 'lodash' import { BookService } from '../../define/db/service/Book/bookService' -import { errorMessage, successMessage } from '../generalTools' +import { errorMessage, successMessage } from '../Public/generalTools' import { FfmpegOptions } from './ffmpegOptions' import { SubtitleSavePositionType } from '../../define/enum/waterMarkAndSubtitle' import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService' @@ -8,10 +8,12 @@ import { define } from '../../define/define' import path from 'path' import { CheckFileOrDirExist, + CheckFolderExistsOrCreate, DeleteFolderAllFile, GetFilesWithExtensions } from '../../define/Tools/file' import { shell } from 'electron' +import { Book } from '../../model/book' import fs from 'fs' const util = require('util') const { exec } = require('child_process') @@ -21,13 +23,23 @@ const fspromises = fs.promises /** * 去除水印和获取字幕相关操作 */ -export class WatermarkAndSubtitle { - constructor() {} +export class Subtitle { + bookService: BookService + bookTaskDetailService: BookTaskDetailService + ffmpegOptions: FfmpegOptions + + constructor() { } async InitService() { - this.bookService = await BookService.getInstance() - this.bookTaskDetailService = await BookTaskDetailService.getInstance() - this.FfmpegOptions = new FfmpegOptions() + if (!this.bookService) { + this.bookService = await BookService.getInstance() + } + if (!this.bookTaskDetailService) { + this.bookTaskDetailService = await BookTaskDetailService.getInstance() + } + if (!this.ffmpegOptions) { + this.ffmpegOptions = new FfmpegOptions() + } } //#region 通用方法 @@ -61,16 +73,15 @@ export class WatermarkAndSubtitle { //#endregion - //#region 字幕 - /** * 获取当前视频中所有的字幕信息 * @param {*} value 需要的参数的对象,包含下面的参数 * @param {*} value.id 小说ID/小说分镜详细信息ID/null * @param {*} value.type 保存的类型(主视频/分镜视频/后续会添加外部单独的视频提取) * @param {*} value.videoPath 视频路径 + * @param {*} value.subtitlePosition 字幕位置信息 */ - async GetVideoFrameText(value) { + async GetVideoFrameText(value: Book.GetVideoFrameTextParams) { try { await this.InitService() let videoPath @@ -78,54 +89,67 @@ export class WatermarkAndSubtitle { let position if (value.type == SubtitleSavePositionType.MAIN_VIDEO) { let bookRes = this.bookService.GetBookDataById(value.id) - if (bookRes.data == null) { + if (bookRes == null) { throw new Error('没有找到小说对应的的视频地址') } - let book = bookRes.data + let book = bookRes tempImageFolder = path.join(define.project_path, `${book.id}/data/subtitle/${book.id}/temp`) if (isEmpty(book.subtitlePosition)) { throw new Error('请先保存位置信息') } position = JSON.parse(book.subtitlePosition) videoPath = book.oldVideoPath + } else if (value.type == SubtitleSavePositionType.STORYBOARD_VIDEO) { + let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(value.id); + if (bookTaskDetail == null) { + throw new Error("没有找到小说分镜详细信息") + } + + tempImageFolder = path.join(define.project_path, `${bookTaskDetail.bookId}/data/subtitle/${bookTaskDetail.name}_${bookTaskDetail.id}/temp`) + if (isEmpty(value.subtitlePosition)) { + throw new Error('请先保存位置信息') + } + position = JSON.parse(value.subtitlePosition) + videoPath = bookTaskDetail.videoPath + } else { + throw new Error("不支持的操作"); } - // // 判断文件夹是不是存在,存在的话,将里面的所有文件删除 - // await DeleteFolderAllFile(tempImageFolder) + await CheckFolderExistsOrCreate(tempImageFolder) + // 判断文件夹是不是存在,存在的话,将里面的所有文件删除 + await DeleteFolderAllFile(tempImageFolder) - // // 将视频进行抽帧,(目前是每秒1帧,时间小于一秒,抽一帧) - // let getDurationRes = await this.FfmpegOptions.FfmpegGetVideoDuration(videoPath) - // if (getDurationRes.code == 0) { - // throw new Error(getDurationRes.message) - // } - // let videoDuration = getDurationRes.data - // let frameTime = this.GenerateFrameTimes(videoDuration, 1) - // for (let i = 0; i < frameTime.length; i++) { - // const item = frameTime[i] - // let name = i.toString().padStart(6, '0') - // let imagePath = path.join(tempImageFolder, `frame_${name}.png`) - // // 开始裁剪抽, - // let res = await this.FfmpegOptions.FfmpegGetVideoFramdAndClip( - // videoPath, - // item, - // imagePath, - // position - // ) - // // 开始识别 - // if (res.code == 0) { - // throw new Error(res.message) - // } - // } - // 截取完毕,删除大的图片 + // 将视频进行抽帧,(目前是每秒1帧,时间小于一秒,抽一帧) + let getDurationRes = await this.ffmpegOptions.FfmpegGetVideoDuration(videoPath) + if (getDurationRes.code == 0) { + throw new Error(getDurationRes.message) + } + let videoDuration = getDurationRes.data + let frameTime = this.GenerateFrameTimes(videoDuration, 1) + for (let i = 0; i < frameTime.length; i++) { + const item = frameTime[i]; + let name = i.toString().padStart(6, '0') + let imagePath = path.join(tempImageFolder, `frame_${name}.png`) + // 开始抽帧 + let res = await this.ffmpegOptions.FfmpegGetVideoFramdAndClip( + videoPath, + item, + imagePath, + position + ) + if (res.code == 0) { + throw new Error(res.message) + } + } // 开始识别 let textRes = await this.GetCurrentFrameText({ id: value.id, type: value.type, - imageFolder : tempImageFolder + imageFolder: tempImageFolder }) - let allTextData = [] + let allTextData = [] as string[] // 开始获取所有的数据 let jsonPaths = await GetFilesWithExtensions(tempImageFolder, ['.json']) for (let i = 0; i < jsonPaths.length; i++) { @@ -138,7 +162,15 @@ export class WatermarkAndSubtitle { } } - console.log(allTextData.join('\n')) + console.log(allTextData.join(',')) + // 这边计算相似度,返回过于相似的数据 + // let res = await RemoveSimilarTexts(allTextData) + + return successMessage( + allTextData.join(','), + '获取视频的的文案信息成功', + 'WatermarkAndSubtitle_GetVideoFrameText' + ) } catch (error) { return errorMessage( '提取视频的的文案信息失败,错误消息如下:' + error.toString(), @@ -157,28 +189,23 @@ export class WatermarkAndSubtitle { try { await this.InitService() let iamgePaths = [] - let imageFolder - if (value.type == SubtitleSavePositionType.MAIN_VIDEO) { - // 判断是不是有位置信息 - imageFolder = value.imageFolder - ? value.imageFolder - : path.join(define.project_path, `${value.id}/data/subtitle/${value.id}`) - let imageFolderIsExist = await CheckFileOrDirExist(imageFolder) - if (!imageFolderIsExist) { - throw new Error('请先保存位置信息') - } - - let images = await GetFilesWithExtensions(imageFolder, ['.png']) - let regex = /.*frame_.*\.png$/ - images.forEach((element) => { - // 使用正则表达式测试文件名 - if (regex.test(element)) { - iamgePaths.push(element) - } - }) - } else if (value.type == SubtitleSavePositionType.STORYBOARD_VIDEO) { + let imageFolder = value.imageFolder + ? value.imageFolder + : path.join(define.project_path, `${value.id}/data/subtitle/${value.id}`) + let imageFolderIsExist = await CheckFileOrDirExist(imageFolder) + if (!imageFolderIsExist) { + throw new Error('请先保存位置信息') } + let images = await GetFilesWithExtensions(imageFolder, ['.png']) + let regex = /.*frame_.*\.png$/ + images.forEach((element) => { + // 使用正则表达式测试文件名 + if (regex.test(element)) { + iamgePaths.push(element) + } + }) + // 开始识别 for (let i = 0; i < iamgePaths.length; i++) { const imagePath = iamgePaths[i] @@ -225,7 +252,10 @@ export class WatermarkAndSubtitle { async OpenBookSubtitlePositionScreenshot(value) { try { let folder - if (value.type == SubtitleSavePositionType.MAIN_VIDEO) { + if ( + value.type == SubtitleSavePositionType.MAIN_VIDEO || + value.type == SubtitleSavePositionType.SETTING + ) { folder = path.join(define.project_path, `${value.id}/data/subtitle/${value.id}`) } else if (value.type == SubtitleSavePositionType.STORYBOARD_VIDEO) { folder = path.join(define.project_path, `${value.id}/data/subtitle/${value.id}`) @@ -237,7 +267,7 @@ export class WatermarkAndSubtitle { throw new Error('文件夹不存在,请先保存字幕位置信息') } - // 打开文件夹\ + // 打开文件夹 shell.openPath(folder) return successMessage( null, @@ -268,17 +298,20 @@ export class WatermarkAndSubtitle { let videoPath let outImagePath // 小说视频保存 - this.FfmpegOptions = new FfmpegOptions() - if (value.type == SubtitleSavePositionType.MAIN_VIDEO) { + this.ffmpegOptions = new FfmpegOptions() + if ( + value.type == SubtitleSavePositionType.MAIN_VIDEO || + value.type == SubtitleSavePositionType.SETTING + ) { if (value.id == null) { throw new Error('小说ID不能为空') } // 获取指定的小说 let bookRes = this.bookService.GetBookDataById(value.id) - if (bookRes.data == null) { - throw new Error(bookRes.message) + if (bookRes == null) { + throw new Error('没有找到小说信息') } - let book = bookRes.data + let book = bookRes if (value.bookSubtitlePosition.length <= 0) { throw new Error('没有获取到字幕信息') @@ -291,7 +324,7 @@ export class WatermarkAndSubtitle { outImagePath = path.join(book.bookFolderPath, `data/subtitle/${book.id}/frame.png`) // 获取视频的宽高数据 - let videoSizeRes = await this.FfmpegOptions.FfmpegGetVideoSize(videoPath) + let videoSizeRes = await this.ffmpegOptions.FfmpegGetVideoSize(videoPath) if (videoSizeRes.code == 0) { throw new Error(videoSizeRes.message) } @@ -332,11 +365,10 @@ export class WatermarkAndSubtitle { throw new Error('小说分镜详细信息ID不能为空') } // 获取指定的小说分镜详细信息 - let bookStoryboardRes = this.bookTaskDetailService.GetBookTaskDetailDataById(value.id) - if (bookStoryboardRes.data == null) { + let bookStoryboard = this.bookTaskDetailService.GetBookTaskDetailDataById(value.id) + if (bookStoryboard == null) { throw new Error('没有找到小说分镜信息') } - let bookStoryboard = bookStoryboardRes.data if (value.bookSubtitlePosition.length <= 0) { throw new Error('没有获取到字幕信息') @@ -353,8 +385,8 @@ export class WatermarkAndSubtitle { ) // 获取视频的宽高数据 - this.FfmpegOptions = new FfmpegOptions() - let videoSizeRes = await this.FfmpegOptions.FfmpegGetVideoSize(videoPath) + this.ffmpegOptions = new FfmpegOptions() + let videoSizeRes = await this.ffmpegOptions.FfmpegGetVideoSize(videoPath) if (videoSizeRes.code == 0) { throw new Error(videoSizeRes.message) } @@ -382,7 +414,7 @@ export class WatermarkAndSubtitle { }) } // 数据保存 - let saveRes = this.bookTaskDetailService.UpdateBookTaskDetail(bookStoryboard.value.id, { + let saveRes = this.bookTaskDetailService.UpdateBookTaskDetail(bookStoryboard.id, { subtitlePosition: JSON.stringify(saveData) }) if (saveRes.code == 0) { @@ -392,7 +424,7 @@ export class WatermarkAndSubtitle { // 开始设置裁剪出来的图片位置 // 裁剪一个示例图片 - let saveImagePath = await this.FfmpegOptions.FfmpegGetVideoFramdAndClip( + let saveImagePath = await this.ffmpegOptions.FfmpegGetVideoFramdAndClip( videoPath, value.currentTime * 1000, outImagePath, @@ -413,6 +445,4 @@ export class WatermarkAndSubtitle { ) } } - - //#endregion } diff --git a/src/main/Service/taskManage.ts b/src/main/Service/taskManage.ts new file mode 100644 index 0000000..b3975ec --- /dev/null +++ b/src/main/Service/taskManage.ts @@ -0,0 +1,305 @@ +import { BookBackTaskListService } from '../../define/db/service/Book/bookBackTaskListService' +import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from '../../define/enum/bookEnum' +import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' +import { errorMessage, successMessage } from '../Public/generalTools' +import { BasicReverse } from './Book/basicReverse' +import { ReverseBook } from './Book/ReverseBook' +import { GeneralResponse } from '../../model/generalResponse' +import { DEFINE_STRING } from '../../define/define_string' +import { MJOpt } from './MJ/mj' +import { AsyncQueue } from '../../main/quene' + +export class TaskManager { + isExecuting: boolean = false; + currentTaskList: TaskModal.Task[] = []; + globalConfig: any; + reverseBook: ReverseBook = new ReverseBook(); + basicReverse: BasicReverse = new BasicReverse(); + softwareService!: SoftwareService; + bookBackTaskListService!: BookBackTaskListService; + eventListeners: Record = {}; + + spaceTime: number = 5000; + count = 0; + isListening = false; + intervalId: any; // 用于存储 setInterval 的 ID + mjOpt: MJOpt + + constructor() { + this.isExecuting = false; + this.currentTaskList = []; + this.globalConfig = global.config; + this.basicReverse = new BasicReverse(); + this.reverseBook = new ReverseBook(); + this.mjOpt = new MJOpt(); + } + + async InitService() { + if (!this.softwareService) { + this.softwareService = await SoftwareService.getInstance(); + } + if (!this.bookBackTaskListService) { + this.bookBackTaskListService = await BookBackTaskListService.getInstance(); + } + await this.mjOpt.InitService(); + } + + async GetGlobalConfig() { + try { + await this.InitService(); + let softData = this.softwareService.GetSoftwareData(); + if (softData.data.length <= 0) { + throw new Error('获取软件数据失败'); + } + let config = softData.data[0]; + global.config = JSON.parse(config.globalSetting); + this.globalConfig = global.config; + return successMessage(global.config, '获取全局配置完成', 'TaskManager_GetGlobalConfig'); + } catch (error) { + console.error('GetGlobalConfig Error:', error); + return errorMessage(`获取全局配置失败,失败信息如下:${error.message}`, 'TaskManager_GetGlobalConfig'); + } + } + + + // 初始化事件监听方法 + InitListeners() { + if (this.isListening) return; // 如果已经在监听,直接返回 + + this.isListening = true; // 标记为已开始监听 + + const executeWithDynamicInterval = async () => { + await this.ExecuteAutoTask(); + this.count++; + console.log("等待时间--" + this.spaceTime, this.count); + // 动态调整等待时间 + clearInterval(this.intervalId); + this.intervalId = setInterval(executeWithDynamicInterval, this.spaceTime); + }; + this.intervalId = setInterval(executeWithDynamicInterval, this.spaceTime); + } + + // 停止监听的方法 + StopListeners() { + this.isListening = false; // 标记为停止监听 + clearInterval(this.intervalId); // 清除定时器 + } + + async ExecuteAutoTask() { + await this.InitService(); + + // 加之前先判断是不是还能执行任务 + let waitTask = global.requestQuene.getWaitingQueue(); + if (waitTask > 20) // 最懂同时等待二十个 + { + console.log('等待中的任务太多,等待中的任务数量:', waitTask); + this.spaceTime = 20000; + return; + } + + // 判断MJ队列是不是存在 + if (!global.mjQueue) { + // MJ 队列不存在,创建 + global.mjQueue = new AsyncQueue(global, 1); + } + + // 开始添加 + // 查任务 + const tasks = this.bookBackTaskListService.GetWaitTaskAndSlice(TaskExecuteType.AUTO, 20 - waitTask); + if (tasks.code == 0) { + return errorMessage(`获取等待中的任务失败,失败信息如下:${tasks.message}`, 'TaskManager_ExecuteAutoTask'); + } + + if (!tasks.data || tasks.data.length <= 0) { + console.log('没有等待中的任务'); + this.spaceTime = 20000; + return; + } + + this.spaceTime = 5000; + //循环添加任务 + for (let index = 0; index < tasks.data.length; index++) { + const element = tasks.data[index]; + if (element.type == BookBackTaskType.MJ_IMAGE || element.type == BookBackTaskType.MJ_REVERSE) { + if (global.mjQueue.getWaitingQueue() > 10) { + console.log('MJ等待中的任务太多,等待中的任务数量:', global.mjQueue.getWaitingQueue()); + this.spaceTime = 20000; + return; + } + // MJ任务 + let res = await this.AddQueue(element); + continue; + } else { + // 其他任务 + let res = await this.AddQueue(element); + } + // 添加完成,修改一下提交时间 // 要判断是否超时 + } + } + + //#region 添加任务到内存任务中 + /** + * 添加分镜计算任务 + * @param task 任务信息 + */ + AddGetFrameDataTask(task: TaskModal.Task): void { + let batch = DEFINE_STRING.BOOK.GET_FRAME_DATA; + global.requestQuene.enqueue(async () => { + await this.basicReverse.GetFrameData(task); + }, `${batch}_${task.id}`, batch); + + } + + /** + * 添加视频分镜任务 + * @param task 任务信息 + */ + AddCutVideoData(task: TaskModal.Task): void { + let batch = DEFINE_STRING.BOOK.FRAMING; + global.requestQuene.enqueue(async () => { + await this.basicReverse.CutVideoData(task); + }, `${batch}_${task.id}`, batch) + } + + + /** + * 添加切割视频任务 + * @param task 任务信息 + */ + AddSplitAudioData(task: TaskModal.Task): void { + let batch = DEFINE_STRING.BOOK.SPLI_TAUDIO; + global.requestQuene.enqueue(async () => { + await this.basicReverse.SplitAudioData(task); + }, `${batch}_${task.id}`, batch) + } + + /** + * 添加抽帧的任务 + * @param task 任务信息 + */ + AddGetFrame(task: TaskModal.Task): void { + let batch = DEFINE_STRING.BOOK.GET_FRAME; + global.requestQuene.enqueue(async () => { + await this.basicReverse.GetFrame(task); + }, `${batch}_${task.id}`, batch) + } + + /** + * 添加识别字幕任务 + * @param task 任务信息 + */ + AddExtractSubtitlesData(task: TaskModal.Task): void { + let batch = DEFINE_STRING.BOOK.GET_COPYWRITING; + global.requestQuene.enqueue(async () => { + await this.basicReverse.ExtractSubtitlesData(task); + }, `${batch}_${task.id}`, batch) + + } + + /** + * 添加单独反推任务 + * @param task + */ + AddSingleReversePrompt(task: TaskModal.Task): void { + let batch = DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT; + global.requestQuene.enqueue(async () => { + await this.reverseBook.SingleReversePrompt(task); + }, `${batch}_${task.id}`, batch) + } + + /** + * 添加任务到内存队列中,分流 + * @param task 任务相关 + * @returns + */ + async AddQueue(task: TaskModal.Task) { + let _bookBackTaskListService = await BookBackTaskListService.getInstance(); + try { + switch (task.type) { + case BookBackTaskType.STORYBOARD: + this.AddGetFrameDataTask(task); + break; + case BookBackTaskType.SPLIT: + this.AddCutVideoData(task); + break; + case BookBackTaskType.AUDIO: + this.AddSplitAudioData(task); + break; + case BookBackTaskType.FRAME: + this.AddGetFrame(task); + break; + case BookBackTaskType.RECOGNIZE: + this.AddExtractSubtitlesData(task); + break; + case BookBackTaskType.MJ_REVERSE || BookBackTaskType.SD_REVERSE: + this.AddSingleReversePrompt(task); + break; + + case BookBackTaskType.MJ_IMAGE: + this.AddImageMJImage(task); + break; + default: + throw new Error('未知的任务类型'); + } + // 是不是要添加自动任务 + // await this.AddTaskHandle(task, true); + + // 添加成功后,更新任务状态 + let updateRes = _bookBackTaskListService.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.RUNNING + }); + if (updateRes.code == 0) { + throw new Error(updateRes.message); + } + return successMessage(null, `${task.name}_${task.id} 任务添加调度完成`, 'TaskManager_AddQueue'); + } catch (error) { + let updateRes = _bookBackTaskListService.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.FAIL, + errorMessage: "任务调度失败,请手动重试" + }); + if (updateRes.code == 0) { + return errorMessage(`处理 ${task.type} 类型任务 ${task.name} 失败,失败信息如下:${error.message}`, 'TaskManager_handleTask'); + } + + return errorMessage(`处理 ${task.type} 类型任务 ${task.name} 失败,失败信息如下:${error.message}`, 'TaskManager_handleTask'); + } + } + + /** + * 将MJ生图生成 + * @param task + */ + async AddImageMJImage(task: TaskModal.Task) { + // 判断任务数量是不是又修改 + let taskNumber = global.mjQueue.getConcurrencyLimit(); + if (taskNumber != this.mjOpt.mjSetting.taskCount) { + global.mjQueue.concurrencyLimit = this.mjOpt.mjSetting.taskCount // 重置并发执行的数量 + } + // 判断是不是MJ的任务 + let batch = DEFINE_STRING.MJ.MJ_IMAGE; + global.mjQueue.enqueue(async () => { + await this.mjOpt.MJImagine(task); + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`); + } + + //#endregion + + async AddTaskHandle(task: TaskModal.Task, isAdd = false) { + if (!isAdd) { + return; + } + if (task.type == BookBackTaskType.STORYBOARD) { + await this.basicReverse.AddCutVideoDataTask(task.bookId); + } else if (task.type == BookBackTaskType.SPLIT) { + await this.basicReverse.AddSplitAudioDataTask(task.bookId, task.bookTaskId); + } else if (task.type == BookBackTaskType.AUDIO) { + await this.basicReverse.AddGetFrameTask(task.bookId, task.bookTaskId); + } else if (task.type == BookBackTaskType.FRAME) { + await this.basicReverse.AddExtractSubtitlesDataTask(task.bookId, task.bookTaskId); + } else { + throw new Error('不支持的任务类型'); + } + } +} diff --git a/src/main/Task/taskScheduler.js b/src/main/Service/taskScheduler.ts similarity index 80% rename from src/main/Task/taskScheduler.js rename to src/main/Service/taskScheduler.ts index 7090242..71fea9c 100644 --- a/src/main/Task/taskScheduler.js +++ b/src/main/Service/taskScheduler.ts @@ -1,10 +1,11 @@ import { LoggerService } from '../../define/db/service/SoftWare/loggerService' import { DEFINE_STRING } from '../../define/define_string' import { LoggerStatus, OtherData } from '../../define/enum/softwareEnum' -import { successMessage } from '../generalTools' +import { successMessage, errorMessage } from '../Public/generalTools' +import { GeneralResponse } from '../../model/generalResponse' export class TaskScheduler { - constructor() {} + constructor() { } /** * 添加日志到数据库,然后返回日志信息到前端,日志记录失败不会报错 * @param {*} bookId 小说ID,必填 @@ -15,12 +16,12 @@ export class TaskScheduler { * @returns */ async AddLogToDB( - bookId, - type, - content, - bookTaskId = OtherData.DEFAULT, + bookId: string, + type: string, + content: string, + bookTaskId: string, status = LoggerStatus.DOING - ) { + ): Promise { try { let log = { bookId: bookId, diff --git a/src/main/Service/tts.ts b/src/main/Service/tts.ts new file mode 100644 index 0000000..299fa54 --- /dev/null +++ b/src/main/Service/tts.ts @@ -0,0 +1,142 @@ +import { errorMessage, successMessage } from '../Public/generalTools' +import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' +import path from 'path' +import { define } from '../../define/define' +import { isEmpty } from 'lodash' +import { ValidateJson } from '../../define/Tools/validate' +const { EdgeTTS } = require('node-edge-tts') + +export class TTS { + softService: SoftwareService + + constructor() { } + + /** + * 初始化TTS服务 + */ + async InitService() { + if (!this.softService) { + this.softService = await SoftwareService.getInstance() + } + } + + /** + * 初始化TTS设置 + */ + InitTTSSetting() { + return { + selectModel: 'edge-tts', + edgeTTS: { + value: 'zh-CN-XiaoxiaoNeural', + gender: 'Female', + label: '晓晓', + lang: 'zh-CN', + saveSubtitles: true, + pitch: 0, // 语调 + rate: 10, // 倍速 + volumn: 0 // 音量 + } + } + } + + /** + * 获取TTS配置 + */ + // @ts-ignore + async GetTTSCOnfig(): Promise { + try { + await this.InitService() + let res = this.softService.GetSoftWarePropertyData('ttsSetting') + let resObj = undefined + if (isEmpty(res)) { + // 没有数据,需要初始化 + resObj = this.InitTTSSetting() + } else { + let tryParse = ValidateJson(res) + if (!tryParse) { + throw new Error('解析TTS配置失败,数据格式不正确') + } + resObj = JSON.parse(res) + } + return successMessage(resObj, '获取TTS配置成功', 'TTS_GetTTSCOnfig') + } catch (error) { + return errorMessage('获取TTS配置失败,错误信息如下:' + error.toString(), 'TTS_GetTTSCOnfig') + } + } + /** + * 保存TTS配置 + * @param {*} data 要保存的数据 + */ + // @ts-ignore + async SaveTTSConfig(data: TTSSettingModel.TTSSetting) { + try { + await this.InitService() + let res = this.softService.SaveSoftwarePropertyData('ttsSetting', JSON.stringify(data)) + return res + } catch (error) { + return errorMessage('保存TTS配置失败,错误信息如下:' + error.toString(), 'TTS_SaveTTSConfig') + } + } + + /** + * 生成音频 + * @param text 要生成的文本 + */ + async GenerateAudio(text: string) { + try { + await this.InitService() + let ttsSetting = await this.GetTTSCOnfig() + if (ttsSetting.code === 0) { + return ttsSetting + } + let res + + let audioPath = path.join(define.project_path, 'audio.mp3') + let selectModel = ttsSetting.data.selectModel + switch (selectModel) { + case 'edge-tts': + res = await this.GenerateAudioByEdgeTTS(text, ttsSetting.data.edgeTTS) + break + + default: + throw new Error('未知的TTS模式') + } + return res + } catch (error) { + return errorMessage('生成音频失败,错误信息如下:' + error.toString(), 'TTS_GenerateAudio') + } + } + + /** + * 使用EdgeTTS生成音频的方法 + * @param text 要生成的文本 + * @param edgeTTS edgetts的设置 + * @returns + */ + // @ts-ignore + async GenerateAudioByEdgeTTS(text: string, edgeTTS: TTSSettingModel.EdgeTTSSetting) { + try { + const tts = new EdgeTTS({ + voice: edgeTTS.value, + lang: edgeTTS.lang, + outputFormat: 'audio-24khz-96kbitrate-mono-mp3', + saveSubtitles: edgeTTS.saveSubtitles, + pitch: `${edgeTTS.pitch}%`, + rate: `${edgeTTS.rate}%`, + volumn: `${edgeTTS.volumn}%` + }) + let ttsRes = await tts.ttsPromise(text, 'C:\\Users\\27698\\Desktop\\audio.mp3') + console.log(ttsRes) + return successMessage( + 'C:\\Users\\27698\\Desktop\\audio.mp3', + '生成音频成功', + 'TTS_GenerateAudioByEdgeTTS' + ) + } catch (error) { + return errorMessage( + '生成音频失败,错误信息如下:' + error.toString(), + 'TTS_GenerateAudioByEdgeTTS' + ) + } + } +} diff --git a/src/main/Service/watermark.ts b/src/main/Service/watermark.ts new file mode 100644 index 0000000..7bd5712 --- /dev/null +++ b/src/main/Service/watermark.ts @@ -0,0 +1,253 @@ +import path from 'path' +import fs from 'fs' +import util from 'util' +import { exec } from 'child_process' +const execAsync = util.promisify(exec); +import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file' +import { errorMessage, successMessage } from '../Public/generalTools' +import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' +import { isEmpty } from 'lodash' +import { RemoveWatermarkType } from '../../define/enum/waterMarkAndSubtitle' +import { ValidateJson } from '../../define/Tools/validate' +import { define } from '../../define/define' +import { LOGGER_DEFINE } from '../../define/logger_define' +import axios from 'axios' +import { Base64ToFile } from '../../define/Tools/image' +import { TaskScheduler } from './taskScheduler'; +import { LoggerStatus, OtherData } from '../../define/enum/softwareEnum'; +import { basicApi } from '../../api/apiBasic'; + +export class Watermark { + softwareService: SoftwareService + taskScheduler: TaskScheduler; + constructor() { } + + async InitService() { + if (!this.softwareService) { + this.softwareService = await SoftwareService.getInstance() + } + if (!this.taskScheduler) { + this.taskScheduler = new TaskScheduler() + } + } + + //#region 设置 + + /** + * 初始化水印设置 + */ + InitWatermarkSetting() { + return { + selectModel: RemoveWatermarkType.LOCAL_LAMA, + iopaint: { + url: undefined + } + } as ImageModel.RemoveWatermarkSetting + } + + /** + * 获取去除水印的设置 + */ + async GetWatermarkSetting() { + try { + await this.InitService(); + let watermarkSetting = this.InitWatermarkSetting(); + let setting = this.softwareService.GetSoftWarePropertyData("watermarkSetting"); + if (!isEmpty(setting)) { + // 直接初始化 + let tryP = ValidateJson(setting); + if (!tryP) { + throw new Error('解析去除水印的设置失败,数据格式不正确') + } + watermarkSetting = JSON.parse(setting); + } + return successMessage(watermarkSetting, '获取去除水印的设置成功', 'Image_GetWatermarkSetting') + } catch (error) { + return errorMessage("获取去除水印的设置失败,失败信息如下:" + error.toString(), 'Image_GetWatermarkSetting') + } + } + + /** + * 保存去除水印的设置 + * @param value + * @returns + */ + async SaveWatermarkSetting(value: ImageModel.RemoveWatermarkSetting) { + try { + await this.InitService(); + if (value.selectModel == RemoveWatermarkType.IOPAINT && !value.iopaint.url) { + throw new Error('iopaint模式,iopaint的地址不能为空') + } + + if (value.selectModel == RemoveWatermarkType.LOCAL_LAMA) { + // 简单判断环境是不是存在 + let lamaPath = path.join(define.scripts_path, 'lama/lama_inpaint.exe'); + if (!(await CheckFileOrDirExist(lamaPath))) { + throw new Error('本地LAMA环墋不存在,请先安装,详细信息看下方的 ‘lama安装教程’') + } + } + + if (value.iopaint.url) { + // 验证iopaint的地址是不是一个合法的url + let reg = new RegExp('^(http|https)://') + if (!reg.test(value.iopaint.url)) { + throw new Error('iopaint的地址不是一个合法的网络地址') + } + if (!value.iopaint.url.endsWith('/')) { + value.iopaint.url = value.iopaint.url + '/' + } + } + + + // 保存数据 + let res = this.softwareService.SaveSoftwarePropertyData('watermarkSetting', JSON.stringify(value)); + return successMessage(null, "保存去除水印的设置成功", 'Image_SaveWatermarkSetting') + } catch (error) { + return errorMessage("保存去除水印的设置失败,失败信息如下:" + error.toString(), 'Image_SaveWatermarkSetting') + } + } + + //#endregion + + //#region 去除水印 + + /** + * 去除水印,试用本地lama + * @param value + */ + async ProcessImageByLama(value: ImageModel.ProcessImageParams): Promise { + try { + let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`) + // 就是判断指定的文件和文件夹是不是存在 + let has_exe = await CheckFileOrDirExist(lama_script) + if (!has_exe) { + throw new Error('图片水印处理组件不存在,请看教程自行下载') + } + let has_model = await CheckFileOrDirExist( + path.resolve(define.scripts_path, 'lama/model/big-lama.pt') + ) + if (!has_model) { + throw new Error('图片水印处理的模型不存在,请看教程自行下载') + } + global.logger.info( + LOGGER_DEFINE.REMOVE_WATERMARK, + `开始使用lama去除水印,开始调用lama程序` + ) + let script = `cd "${path.dirname(lama_script)}" && "${lama_script}" "-l" "${value.inputFilePath}" "${value.maskPath}" "${value.outFilePath}"` + let scriptRes = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' }) + // 判断是不是有输出文件 + let has_out = await CheckFileOrDirExist(value.outFilePath) + if (!has_out) { + throw new Error('lama去除水印失败,没有输出文件') + } + + if (value.type == 'arrayBuffer') { + // 读取导出的文件 + let res_data = await fs.promises.readFile(value.outFilePath) + return res_data + } else if (value.type == 'file') { + return value.outFilePath + } + + } catch (error) { + throw new Error('使用Lama去除水印失败,失败信息如下:' + error.toString()) + } + } + + /** + * 去除s水印,使用iopaint + * @param value 传入的参数 + * @param iopaintUrl + */ + async ProcessImageByIopaint(value: ImageModel.ProcessImageParams, iopaintUrl: string): Promise { + try { + iopaintUrl = iopaintUrl + 'api/v1/inpaint' + let headers = { + accept: '*/*', + 'accept-language': 'zh-CN,zh;q=0.9', + 'content-type': 'application/json' + } + let data = { + image: value.imageBase64, + mask: value.maskBase64, + ldm_steps: 30, + ldm_sampler: 'ddim', + zits_wireframe: true, + cv2_flag: 'INPAINT_NS', + cv2_radius: 5, + hd_strategy: 'Crop', + hd_strategy_crop_triger_size: 640, + hd_strategy_crop_margin: 128, + hd_trategy_resize_imit: 2048 * 5, + prompt: '', + negative_prompt: + 'out of frame, lowres, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, disfigured, gross proportions, malformed limbs, watermark, signature', + use_croper: false, + croper_x: 284, + croper_y: 284, + croper_height: 512, + croper_width: 512, + use_extender: false, + extender_x: 0, + extender_y: 0, + extender_height: 1080, + extender_width: 1080, + sd_mask_blur: 12, + sd_strength: 1, + sd_steps: 50, + sd_guidance_scale: 7.5, + sd_sampler: 'DPM++ 2M', + sd_seed: -1, + sd_match_histograms: false, + sd_lcm_lora: false, + paint_by_example_example_image: null, + p2p_image_guidance_scale: 1.5, + enable_controlnet: false, + controlnet_conditioning_scale: 0.4, + controlnet_method: '', + enable_brushnet: false, + brushnet_method: 'random_mask', + brushnet_conditioning_scale: 1, + enable_powerpaint_v2: false, + powerpaint_task: 'text-guided' + } + let res = await basicApi.post(iopaintUrl, data, headers) + + if (value.type == 'arrayBuffer') { + return res.data + } else if (value.type == 'file') { + let buffer = Buffer.from(res.data) + await fs.promises.writeFile(value.outFilePath, buffer) + return value.outFilePath + } + } catch (error) { + throw new Error('使用Iopaint去除水印失败,失败信息如下:' + error.toString()) + } + } + + /** + * 去除图片水印,当前只支持本地,后续会支持 iopaint + * @param value + */ + async ProcessImage(value: ImageModel.ProcessImageParams) { + let outDir = path.dirname(value.outFilePath) + await CheckFolderExistsOrCreate(outDir); + + let watermarkSettingRes = await this.GetWatermarkSetting(); + if (watermarkSettingRes.code == 0) { + throw new Error(watermarkSettingRes.message) + } + let watermarkSetting = watermarkSettingRes.data as ImageModel.RemoveWatermarkSetting; + + switch (watermarkSetting.selectModel) { + case RemoveWatermarkType.LOCAL_LAMA: + return await this.ProcessImageByLama(value) + case RemoveWatermarkType.IOPAINT: + return await this.ProcessImageByIopaint(value, watermarkSetting.iopaint.url) + default: + throw new Error('未知的去除水印模式') + } + } + + //#endregion +} \ No newline at end of file diff --git a/src/main/Task/writing.js b/src/main/Service/writing.js similarity index 96% rename from src/main/Task/writing.js rename to src/main/Service/writing.js index eeb1655..48860d4 100644 --- a/src/main/Task/writing.js +++ b/src/main/Service/writing.js @@ -6,7 +6,7 @@ import { PublicMethod } from '../Public/publicMethod' import { define } from '../../define/define' import { get, has, isEmpty } from 'lodash' import { ClipSetting } from '../../define/setting/clipSetting' -import { errorMessage, successMessage } from '../generalTools' +import { errorMessage, successMessage } from '../Public/generalTools' import { ServiceBase } from '../../define/db/service/serviceBase' import { GetDoubaoErrorResponse, @@ -15,6 +15,7 @@ import { GetRixApiErrorResponse } from '../../define/response/openAIResponse.ts' import axios from 'axios' +import { ValidateJson } from '../../define/Tools/validate.ts' const { v4: uuidv4 } = require('uuid') // 引入UUID库来生成唯一标识符 let tools = new Tools() @@ -42,10 +43,14 @@ export class Writing extends ServiceBase { // 判断对应的AI的设置是不是有 let aiSetting = this.softService.GetSoftWarePropertyData('aiSetting') - if (isEmpty(aiSetting.data)) { + if (isEmpty(aiSetting)) { throw new Error('请先设置AI设置') } - aiSetting = JSON.parse(aiSetting.data) + let tryP = ValidateJson(aiSetting) + if (!tryP) { + throw new Error('AI设置的数据格式不正确') + } + aiSetting = JSON.parse(aiSetting) // 判断是不是有对应的AI设置 let aiData = get(aiSetting, setting.gptAI) for (const aid in aiData) { @@ -57,8 +62,7 @@ export class Writing extends ServiceBase { throw new Error('请先设置文案') } - // 开始请求 - console.log(aiData) + // 开始请求AI let axiosRes = await axios.post('/api/Forward/ForwardWord', { promptTypeId: setting.gptType, promptId: setting.gptData, @@ -255,6 +259,8 @@ export class Writing extends ServiceBase { let init_num = textData.length let srt_path = data[1] let current_text = '' + let iii = 0 + let sss = '' try { if (!srt_path) { // 获取项目下面的所有的srt @@ -289,7 +295,10 @@ export class Writing extends ServiceBase { let srt_obj = null let text_count = 0 let tmp_str = '' + for (let i = 0; i < data.length; ) { + iii = i + sss = data[i].after_gpt let srt_value = data[i].text current_text = `字幕: “${srt_value}” 和文案第${text_count + 1} 行数据 “${ textData[text_count].after_gpt @@ -402,6 +411,8 @@ export class Writing extends ServiceBase { data: textData } } catch (error) { + // console.log(iii) + // console.log(sss) return { code: 0, message: error.toString() diff --git a/src/main/Task/taskManage.js b/src/main/Task/taskManage.js deleted file mode 100644 index 6233b27..0000000 --- a/src/main/Task/taskManage.js +++ /dev/null @@ -1,200 +0,0 @@ -import { BookBackTaskListService } from '../../define/db/service/Book/bookBackTaskListService' -import { BookService } from '../../define/db/service/Book/bookService' -import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService' -import { BookTaskService } from '../../define/db/service/Book/bookTaskService' -import { OtherData } from '../../define/enum/softwareEnum' -import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from '../../define/enum/bookEnum' -import { isEmpty } from 'lodash' -import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' -import { errorMessage, successMessage } from '../generalTools' -import { BasicReverse } from './basicReverse' - -export class TaskManager { - constructor() { - this.isExecuting = false - this.currentTaskList = [] - this.globalConfig = global.config - // this.taskExecute = new TaskExecute() - this.basicReverse = new BasicReverse() - } - - /** - * 初始化服务 - */ - async InitService() { - if (!this.softwareService) { - this.softwareService = await SoftwareService.getInstance() - } - if (!this.bookBackTaskListService) { - this.bookBackTaskListService = await BookBackTaskListService.getInstance() - } - } - /** - * 加载数据库中的配置到全局变量中 - */ - async GetGlobalConfig() { - try { - await this.InitService() - let softData = this.softwareService.GetSoftwareData(null) - if (softData.data.length <= 0) { - throw new Error('获取软件数据失败') - } - let config = softData.data[0] - global.config = JSON.parse(config.globalSetting) - this.globalConfig = global.config - return successMessage(global.config, '获取全局配置完成', 'TaskManager_GetGlobalConfig') - } catch (error) { - return errorMessage( - `获取全局配置失败,失败信息如下:` + error.message, - 'TaskManager_GetGlobalConfig' - ) - } - } - - /** - * 执行自动任务 - * @returns - */ - async ExecuteAutoTask() { - await this.InitService() - if (this.isExecuting && this.currentTaskList.length > this.globalConfig.task_number) { - console.log('任务正在执行,跳过此次执行') - return - } - this.isExecuting = true - try { - while (this.currentTaskList.length < this.globalConfig.task_number) { - // 获取正在等待中第一个任务 - const tasks = this.bookBackTaskListService.GetWaitTaskAndSlice(TaskExecuteType.AUTO, 1) - if (!tasks.data || tasks.data.length <= 0) { - console.log('没有等待中的任务') - break - } - let task = tasks.data[0] - this.currentTaskList.push(task) - this.handleTask(task) - .then((data) => { - if (data.code == 0) { - this.bookBackTaskListService.UpdateTaskStatus({ - id: task.id, - status: BookBackTaskStatus.FAIL, - errorMessage: data.message - }) - return Promise.reject(new Error(data.message)) - } else if (data.code == 1) { - this.bookBackTaskListService.UpdateTaskStatus({ - id: task.id, - status: BookBackTaskStatus.DONE - }) - } else { - return Promise.reject(new Error(`${task.type} 返回的数据结构不对`)) - } - }) - .catch((error) => { - // 失败,修改当前task的状态为fail - this.bookBackTaskListService.UpdateTaskStatus({ - id: task.id, - status: BookBackTaskStatus.FAIL, - errorMessage: error.message - }) - throw error - }) - .finally(() => { - this.currentTaskList = this.currentTaskList.filter((t) => t.id != task.id) - this.ExecuteAutoTask() - }) - } - } catch (err) { - return errorMessage( - `执行任务失败,失败信息如下:${err.message}`, - 'TaskManager_ExecuteAutoTask' - ) - } finally { - // 判断是不是还有任务,没有则设置任务执行状态为false - if (this.currentTaskList.length <= 0) { - this.isExecuting = false - } - } - } - - async handleTask(task) { - try { - let _bookBackTaskListService = await BookBackTaskListService.getInstance() - let res - // 调用分镜头任务 - if (task.type == BookBackTaskType.STORYBOARD) { - res = await this.basicReverse.GetFrameData(task) - } else if (task.type == BookBackTaskType.SPLIT) { - // 调用分割视频任务 - res = await this.basicReverse.CutVideoData(task) - } else if (task.type == BookBackTaskType.AUDIO) { - // 提取音频任务 - res = await this.basicReverse.SplitAudioData(task) - } else if (task.type == BookBackTaskType.FRAME) { - // 抽取视频帧任务 - res = await this.basicReverse.GetFrame(task) - } else if (task.type == BookBackTaskType.RECOGNIZE) { - // 识别识别字幕任务 - res = await this.basicReverse.ExtractSubtitlesData(task) - } - // 未知的任务类型 - else { - throw new Error('未知的任务类型') - } - - if (res.code == 0) { - // 修改当前队列的任务状态 - let updateRes = _bookBackTaskListService.UpdateTaskStatus({ - id: task.id, - status: BookBackTaskStatus.FAIL, - errorMessage: res.message - }) - if (updateRes.code == 0) { - throw new Error(updateRes.message) - } - throw new Error(res.message) - } - - // 判断是不是要添加后续任务 - await this.AddTaskHandle(task, true) - - // 修改当前队列的任务状态 - let updateRes = _bookBackTaskListService.UpdateTaskStatus({ - id: task.id, - status: BookBackTaskStatus.DONE - }) - if (updateRes.code == 0) { - throw new Error(updateRes.message) - } - return successMessage(null, '任务执行成功', 'TaskManager_handleTask') - } catch (error) { - return errorMessage( - `处理 ${task.type} 类型任务 ${task.name} 失败,失败信息如下:${error.message}`, - 'TaskManager_handleTask' - ) - } - } - - /** - * 是否添加后续任务 - * @param {*} task - * @param {*} isAdd - */ - async AddTaskHandle(task, isAdd = false) { - if (!isAdd) { - return - } - - if (task.type == BookBackTaskType.STORYBOARD) { - await this.basicReverse.AddCutVideoDataTask(task.bookId) - } else if (task.type == BookBackTaskType.SPLIT) { - await this.basicReverse.AddSplitAudioDataTask(task.bookId, task.bookTaskId) - } else if (task.type == BookBackTaskType.AUDIO) { - await this.basicReverse.AddGetFrameTask(task.bookId, task.bookTaskId) - } else if (task.type == BookBackTaskType.FRAME) { - await this.basicReverse.AddExtractSubtitlesDataTask(task.bookId, task.bookTaskId) - } else { - throw new Error('不支持的任务类型') - } - } -} diff --git a/src/main/discord/mjSimple.js b/src/main/discord/mjSimple.js index 651ccd2..e9df98c 100644 --- a/src/main/discord/mjSimple.js +++ b/src/main/discord/mjSimple.js @@ -4,7 +4,7 @@ import { DiscordAPI } from "../../api/discordApi"; import { MjSetting } from "../../define/setting/mjSetting"; import { DynamicSetting } from "../../define/setting/dynamicSetting"; import { AwesomeHelp } from "awesome-js" -import { errorMessage, successMessage } from "../generalTools"; +import { errorMessage, successMessage } from "../Public/generalTools"; export class MjSimple { constructor(global) { diff --git a/src/main/generalTools.js b/src/main/generalTools.js deleted file mode 100644 index e17a38e..0000000 --- a/src/main/generalTools.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的后缀 - * @param {*} value 要检查的字符串 - * @param {*} suffix 要添加的后缀 - * @returns - */ -function checkStringValueAddSuffix(value, suffix) { - if (value && value !== null && value !== undefined && value !== '') { - return value + suffix; - } else { - return ''; - } -} - -/** - * 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的前缀 - * @param {*} value 要检查的值 - * @param {*} prefix 要添加的前缀 - * @returns - */ - -function checkStringValueAddPrefix(value, prefix) { - if (value && value !== null && value !== undefined && value !== '') { - return prefix + value; - } else { - return ''; - } -} - -/** - * 新建一个函数,判断传入的字符串的值是不是存在,存在的话删除后面指定数量的字符 - * @param {*} value 要删除的字符串 - * @param {*} suffix 删除的后缀 - * @returns - */ -function checkStringValueDeleteSuffix(value, suffix) { - // 增加一个判断,当前删除的数量是不是大于字符串的长度 - if (value && value !== null && value !== undefined && value !== '') { - if (suffix.length > value.length) { - return ''; - } else { - return value.slice(0, value.length - suffix.length); - } - } else { - return ''; - } -} - -/** - * 新建一个函数,判断传入的字符串的值是不是存在,存在的话删除前面指定数量的字符 - * @param {*} value 操作的字符串 - * @param {*} prefix 要删除的字符串 - * @returns - */ -function checkStringValueDeletePrefix(value, prefix) { - // 增加一个判断,当前删除的数量是不是大于字符串的长度 - if (value && value !== null && value !== undefined && value !== '') { - if (prefix.length > value.length) { - return ''; - } else { - return value.slice(prefix.length); - } - } else { - return ''; - } -} -/** - * 返回成功的消息,包含code,data,message - * @param {*} data 返回的数据 - * @param {*} message 成功消息 - * @param {*} service 消息日志类型 - * @returns - */ -function successMessage(data, message = null, service = null) { - if (service) { - global.logger.info(service, message ? message : '成功返回数据'); - } - return { - code: 1, - data: data, - message: message - } -} - -/** - * 返回失败的消息, - * @param {*} message 错误信息 - * @param {*} service 错误日志类型 - * @returns - */ -function errorMessage(message, service = null) { - if (service) { - global.logger.error(service, message ? message : '未知报错,没有捕获的错误'); - } - return { - code: 0, - message: message - } -} - -export { - checkStringValueAddSuffix, - checkStringValueAddPrefix, - checkStringValueDeletePrefix, - checkStringValueDeleteSuffix, - successMessage, - errorMessage -} \ No newline at end of file diff --git a/src/main/index.js b/src/main/index.js index 9444967..9de4bef 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -9,14 +9,14 @@ import { electronApp, optimizer, is } from '@electron-toolkit/utils' import icon from '../../resources/icon.ico?asset' import { define } from '../define/define.js' import { func } from './func.js' -import { AsyncQueue } from './quene.js' -import { DEFINE_STRING } from '../define/define_string.js' +import { AsyncQueue } from './quene' +import { DEFINE_STRING } from '../define/define_string' import { Tools } from './tools.js' import { ImageGenerate } from './ReverseManage/imageGenerate.js' import { Setting } from './setting/setting.js' import { has, isEmpty } from 'lodash' import { AutoSync } from './setting/autoSync.js' -import { TaskManager } from './Task/taskManage.js' +import { TaskManager } from './Service/taskManage' // ipc import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js' @@ -30,9 +30,6 @@ let setting = new Setting(global) async function InitData(gl) { let res = await setting.getSettingDafultData() gl.config = res - gl.requestQuene = new AsyncQueue(gl, res.task_number) - gl.fileQueue = new AsyncQueue(gl, 1) - gl.taskManager = new TaskManager() return res } @@ -95,7 +92,7 @@ async function createWindow(hash = 'ShowMessage', data, url = null) { } else { if (is.dev && process.env['ELECTRON_RENDERER_URL']) { mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/#/' + hash) - mainWindow.webContents.openDevTools() + // mainWindow.webContents.openDevTools() } else { if (hash != '') { mainWindow.loadURL(`file://${path.join(__dirname, '../renderer/index.html')}#/${hash}`) @@ -152,7 +149,10 @@ app.whenReady().then(async () => { await AutoSync() global.newWindow = [] - mainWindow = createWindow('ShowMessage', null) + mainWindow = await createWindow('ShowMessage', null) + + global.requestQuene = new AsyncQueue(global, global.config.task_number) + global.fileQueue = new AsyncQueue(global, 1) //判断是不是又配置文件,没有的话,将temp中的基础配置加载 // 判断文件夹是不是存在 @@ -463,11 +463,6 @@ ipcMain.handle(DEFINE_STRING.REFRASH_IMAGWE_DATA, async (event, value) => { return res }) -// 监听打开全局窗口事件 -ipcMain.on(DEFINE_STRING.SHOW_GLOABAL_MESSAGE_DIALOG, (event, value) => { - global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, value) -}) - /** * 监听一个窗口,返回窗口创建成功后需要的基础数据 */ diff --git a/src/main/logger.js b/src/main/logger.js index 82bf55f..808cf79 100644 --- a/src/main/logger.js +++ b/src/main/logger.js @@ -2,6 +2,7 @@ import winston from 'winston'; import path from 'path'; import DailyRotateFile from 'winston-daily-rotate-file'; +import moment from 'moment-timezone'; export class Logger { constructor(log_folder) { @@ -9,7 +10,9 @@ export class Logger { this.logger = winston.createLogger({ level: 'info', format: winston.format.combine( - winston.format.timestamp(), + winston.format.timestamp({ + format: () => moment().tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss') + }), winston.format.printf(info => `${(new Date()).toLocaleString()} [${info.level.toUpperCase()}] [${info.service}] ${info.message}`) ), transports: [ diff --git a/src/main/quene.js b/src/main/quene.js index 6978735..d334325 100644 --- a/src/main/quene.js +++ b/src/main/quene.js @@ -1,313 +1,363 @@ -import { DEFINE_STRING } from "../define/define_string"; -import { Tools } from "./tools"; +import { DEFINE_STRING } from '../define/define_string' +import { Tools } from './tools' + export class AsyncQueue { - constructor(global, concurrencyLimit = 1, manualMode = false) { - this.global = global; - this.tasks = []; - this.concurrencyLimit = concurrencyLimit; - this.manualMode = manualMode; - this.currentConcurrency = 0; - // 扩展批次完成状态对象以支持子批次 - this.batchCompletion = {}; - this.taskDeadline = global.endTime; - this.showEndTime = true; - this.tools = new Tools(); - this.currentCreateItem = null; + constructor(global, concurrencyLimit = 1, manualMode = false) { + this.global = global + this.tasks = [] + this.concurrencyLimit = concurrencyLimit + this.manualMode = manualMode + this.currentConcurrency = 0 + // 扩展批次完成状态对象以支持子批次 + this.batchCompletion = {} + this.taskDeadline = global.endTime + this.showEndTime = true + this.tools = new Tools() + this.currentCreateItem = null - // 只有在手动模式下才会使用。要设置当前正在执行的任务,将正在执行的任务添加到里面,需要在任务完成之后手动移除 - this.taskProgress = []; + // 只有在手动模式下才会使用。要设置当前正在执行的任务,将正在执行的任务添加到里面,需要在任务完成之后手动移除 + this.taskProgress = [] + } + + async enqueue(task, taskId, batchId, subBatchId = 'default') { + if (batchId && batchId != DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) { + // 判断当前的任务是否已经存在,存在则不添加 + let index = this.tasks.findIndex( + (item) => + item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId + ) + if (index != -1) { + throw new Error(`Task ${taskId} in batch ${batchId} already exists.`) + } + } + if (!this.batchCompletion[batchId]) { + this.batchCompletion[batchId] = { + remaining: 0, + subBatches: {}, + callback: null, + failedTasks: [] + } + } + if (!this.batchCompletion[batchId].subBatches[subBatchId]) { + this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, failedTasks: [] } } - async enqueue(task, taskId, batchId, subBatchId = 'default') { + this.batchCompletion[batchId].remaining++ + this.batchCompletion[batchId].subBatches[subBatchId].remaining++ + this.tasks.push({ task, taskId, batchId, subBatchId }) + if (!this.manualMode) { + await this.process() + } + } - if (batchId && batchId != DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) { - // 判断当前的任务是否已经存在,存在则不添加 - let index = this.tasks.findIndex(item => item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId); - if (index != -1) { - throw new Error(`Task ${taskId} in batch ${batchId} already exists.`); - } - } - if (!this.batchCompletion[batchId]) { - this.batchCompletion[batchId] = { remaining: 0, subBatches: {}, callback: null, failedTasks: [] }; - } - if (!this.batchCompletion[batchId].subBatches[subBatchId]) { - this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, failedTasks: [] }; - } + setBatchCompletionCallback(batchId, callback) { + if (this.batchCompletion[batchId]) { + this.batchCompletion[batchId].callback = callback + } + } - this.batchCompletion[batchId].remaining++; - this.batchCompletion[batchId].subBatches[subBatchId].remaining++; - this.tasks.push({ task, taskId, batchId, subBatchId }); - if (!this.manualMode) { - await this.process(); - } + setSubBatchCompletionCallback(batchId, subBatchId, callback) { + if (this.batchCompletion[batchId]) { + this.batchCompletion[batchId].subBatches[subBatchId].callback = callback + } + } + + setTaskDeadline(deadline) { + this.taskDeadline = deadline + } + + async process(task_count = 0) { + // 判断是不是有机器码检测的标识 + if (!this.global.CheckMachineId) { + // throw new Error("Machine ID not detected, please check the machine ID."); + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: '请联系管理员激活' + }) + return } - setBatchCompletionCallback(batchId, callback) { - if (this.batchCompletion[batchId]) { - this.batchCompletion[batchId].callback = callback; - } + if (this.global.endTime && new Date(Date.now()).toISOString() > this.global.endTime) { + this.tasks = [] + if (this.showEndTime) { + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { + code: 0, + message: '试用时间已到,请联系客服' + }) + } + this.showEndTime = false + return } - setSubBatchCompletionCallback(batchId, subBatchId, callback) { - if (this.batchCompletion[batchId]) { - this.batchCompletion[batchId].subBatches[subBatchId].callback = callback; - } - } - - setTaskDeadline(deadline) { - this.taskDeadline = deadline; - } - - async process(task_count = 0) { - - // 判断是不是有机器码检测的标识 - if (!this.global.CheckMachineId) { - // throw new Error("Machine ID not detected, please check the machine ID."); - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { code: 0, message: "请联系管理员激活" }); - return; - } - - if (this.global.endTime && new Date(Date.now()).toISOString() > this.global.endTime) { - this.tasks = []; - if (this.showEndTime) { - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { code: 0, message: "试用时间已到,请联系客服" }); - } - this.showEndTime = false; - return; - } - - while (this.tasks.length > 0 && (this.manualMode ? this.taskProgress.length < task_count : this.currentConcurrency < this.concurrencyLimit)) { - const { task, taskId, batchId, subBatchId } = this.tasks.shift(); - this.currentConcurrency++; - Promise.resolve(task()).then(() => { - this.currentConcurrency--; - this.handleTaskCompletion(batchId, subBatchId, null); - if (!this.manualMode) { - this.process(); - } - }).catch((error) => { - // 判断是不是网络请求,不是网络请求直接报错 - let retryCount = 0; - const maxRetryCount = 5; - let retryTask = async () => { - if (retryCount < maxRetryCount) { - retryCount++; - await this.tools.delay(2000); - this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, - { code: 0, message: `生图失败,共5次尝试,目前第${retryCount}次` }); - task().then(() => { - this.currentConcurrency--; - this.handleTaskCompletion(batchId, subBatchId, null); - if (!this.manualMode) { - this.process(); - } - }).catch((error) => { - retryTask(); - }); - } else { - // console.error(`Task ${taskId} failed after ${maxRetryCount} retries`); - this.currentConcurrency--; - this.handleTaskCompletion(batchId, subBatchId, { taskId, error }); - if (!this.manualMode) { - this.process(); - } - } - } - - if ([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE].includes(batchId)) { - retryTask(); - } else { - console.error('An error occurred in task ' + taskId + ':', error); - // 出现报错。直接停掉当前对应的的子批次 - this.currentConcurrency--; - this.handleTaskCompletion(batchId, subBatchId, { taskId, error }); - if (!this.manualMode) { - this.process(); - } else { - // 将当前的设置删除,将历史人物删除 - this.setCurrentCreateItem(null); - this.taskProgress = []; - } - } - - }); - } - } - - // 任务完成执行的函数,用于处理任务完成后的状态更新等 - handleTaskCompletion(batchId, subBatchId, taskError) { - const batch = this.batchCompletion[batchId]; - const subBatch = this.batchCompletion[batchId].subBatches[subBatchId]; - - if (taskError) { - batch.failedTasks.push(taskError); - subBatch.failedTasks.push(taskError); - // 删除整个子批次 - this.removeTask(batchId, null, subBatchId) - } - this.batchCompletion[batchId].remaining--; - this.batchCompletion[batchId].subBatches[subBatchId].remaining--; - - // 子批次完成 - if (this.batchCompletion[batchId].subBatches[subBatchId].remaining === 0) { - console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`); - const callback = this.batchCompletion[batchId].subBatches[subBatchId].callback; - if (callback) { - callback(this.batchCompletion[batchId].subBatches[subBatchId].failedTasks); - } - this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, callback: null, failedTasks: [] }; - } - - if (this.batchCompletion[batchId].remaining === 0) { - const callback = this.batchCompletion[batchId].callback; - if (callback) { - callback(this.batchCompletion[batchId].failedTasks); - } - this.batchCompletion[batchId] = { remaining: 0, subBatches: {}, callback: null, failedTasks: [] }; - } - } - - // 移除任务 - removeTask(batchId, taskId, subBatchId = 'default') { - try { - - if (!this.batchCompletion[batchId]) { - throw new Error(`Batch ${batchId} does not exist.`); - } - - // 获取当前批次的所有的任务 - let initialCount = this.tasks.filter(item => item.batchId === batchId).length; - let finalCount = initialCount; - - // 首先,判断是否指定了taskId来决定是移除单个任务还是整个批次 - if (taskId == null) { - // 删除整个批次 - this.tasks = this.tasks.filter(item => item.batchId !== batchId); - finalCount = this.tasks.filter(item => item.batchId === batchId).length; - } else if (taskId == 'all') { - // 删除所有的指定的子批次 - this.tasks = this.tasks.filter(item => !(item.subBatchId == subBatchId && item.batchId == batchId)); - finalCount = this.tasks.filter(item => item.subBatchId == subBatchId && item.batchId == batchId).length; - } else { - // 删除指定的任务,需要考虑subBatchId - this.tasks = this.tasks.filter(item => !(item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId)); - finalCount = this.tasks.filter(item => item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId).length; - } - // 在移除后再次计算该批次的任务数量 - // const finalCount = this.tasks.length; - // 使用两次计数的差值更新 remaining - const removedTasks = initialCount - finalCount; - if (removedTasks > 0) { - this.batchCompletion[batchId].remaining -= removedTasks; - if (this.batchCompletion[batchId].subBatches[subBatchId]) { - this.batchCompletion[batchId].subBatches[subBatchId].remaining -= removedTasks; - this.batchCompletion[batchId].subBatches[subBatchId].callback = null; - } - } - - // 检查并处理完整批次和小批次的完成状态 - const batch = this.batchCompletion[batchId]; - // 判断子批次的数量 - const subBatch = this.batchCompletion[batchId].subBatches[subBatchId]; - // 子批次完成 - if (subBatch && subBatch.remaining == 0 && subBatch.callback) { - subBatch.callback(subBatch.failedTasks) - delete this.batchCompletion[batchId].subBatches[subBatchId]; // 清理批次完成状态 - } - // 总批次完成 - if (batch && batch.remaining === 0 && batch.callback) { - const callback = batch.callback; - callback(batch.failedTasks); - delete this.batchCompletion[batchId]; // 清理批次完成状态 - } else { - // 遍历小批次,看是否有需要清理的 - for (let subBatchId in batch.subBatches) { - const subBatch = batch.subBatches[subBatchId]; - if (subBatch.remaining === 0) { - // 这里可以进行小批次完成后的处理,比如调用小批次的特定回调 - console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`); - // 可以选择删除空的小批次状态,如果不需要保留它的完成记录 - delete batch.subBatches[subBatchId]; - } - } - } - return { - code: 1 - } - - } catch (error) { - return { + while ( + this.tasks.length > 0 && + (this.manualMode + ? this.taskProgress.length < task_count + : this.currentConcurrency < this.concurrencyLimit) + ) { + const { task, taskId, batchId, subBatchId } = this.tasks.shift() + this.currentConcurrency++ + Promise.resolve(task()) + .then(() => { + this.currentConcurrency-- + this.handleTaskCompletion(batchId, subBatchId, null) + if (!this.manualMode) { + this.process() + } + }) + .catch((error) => { + // 判断是不是网络请求,不是网络请求直接报错 + let retryCount = 0 + const maxRetryCount = 5 + let retryTask = async () => { + if (retryCount < maxRetryCount) { + retryCount++ + await this.tools.delay(2000) + this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, { code: 0, - message: error.toString() - } - } - } - - // 获取指定batch的任务列表 - getTasks(batchId) { - return this.tasks.filter(item => item.batchId === batchId); - } - - - // 获取失败的任务 - getFailedTasks(batchId, subBatchId = 'default') { - if (this.batchCompletion[batchId] && this.batchCompletion[batchId].subBatches[subBatchId]) { - return this.batchCompletion[batchId].subBatches[subBatchId].failedTasks; - } - return []; - } - - // 手动开启下一个任务 - async startNextTask(taskCount = 3) { - // 判断当前是不是有任务正在执行 - if (this.currentCreateItem) { - return; - } - console.log("调用开始下一个任务", this.taskProgress) - if (this.manualMode && this.tasks.length > 0 && this.taskProgress.length < taskCount) { - this.process(taskCount); - } - } - - // 修改currentItem - setCurrentCreateItem(item) { - this.currentCreateItem = item; - if (item) { - // 判断相同的ID的任务是否存在,存在则不添加 - let index = this.taskProgress.findIndex(task => task.id === item.id); - if (index == -1) { - this.taskProgress.push(item); + message: `生图失败,共5次尝试,目前第${retryCount}次` + }) + task() + .then(() => { + this.currentConcurrency-- + this.handleTaskCompletion(batchId, subBatchId, null) + if (!this.manualMode) { + this.process() + } + }) + .catch((error) => { + retryTask() + }) } else { - // 直接修改 - this.taskProgress[index] = item; + // console.error(`Task ${taskId} failed after ${maxRetryCount} retries`); + this.currentConcurrency-- + this.handleTaskCompletion(batchId, subBatchId, { taskId, error }) + if (!this.manualMode) { + this.process() + } } + } + + if ( + [ + DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, + DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE + ].includes(batchId) + ) { + retryTask() + } else { + console.error('An error occurred in task ' + taskId + ':', error) + // 出现报错。直接停掉当前对应的的子批次 + this.currentConcurrency-- + this.handleTaskCompletion(batchId, subBatchId, { taskId, error }) + if (!this.manualMode) { + this.process() + } else { + // 将当前的设置删除,将历史人物删除 + this.setCurrentCreateItem(null) + this.taskProgress = [] + } + } + }) + } + } + + // 任务完成执行的函数,用于处理任务完成后的状态更新等 + handleTaskCompletion(batchId, subBatchId, taskError) { + const batch = this.batchCompletion[batchId] + const subBatch = this.batchCompletion[batchId].subBatches[subBatchId] + + if (taskError) { + batch.failedTasks.push(taskError) + subBatch.failedTasks.push(taskError) + // 删除整个子批次 + this.removeTask(batchId, null, subBatchId) + } + this.batchCompletion[batchId].remaining-- + this.batchCompletion[batchId].subBatches[subBatchId].remaining-- + + // 子批次完成 + if (this.batchCompletion[batchId].subBatches[subBatchId].remaining === 0) { + console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`) + const callback = this.batchCompletion[batchId].subBatches[subBatchId].callback + if (callback) { + callback(this.batchCompletion[batchId].subBatches[subBatchId].failedTasks) + } + this.batchCompletion[batchId].subBatches[subBatchId] = { + remaining: 0, + callback: null, + failedTasks: [] + } + } + + if (this.batchCompletion[batchId].remaining === 0) { + const callback = this.batchCompletion[batchId].callback + if (callback) { + callback(this.batchCompletion[batchId].failedTasks) + } + this.batchCompletion[batchId] = { + remaining: 0, + subBatches: {}, + callback: null, + failedTasks: [] + } + } + } + + // 移除任务 + removeTask(batchId, taskId, subBatchId = 'default') { + try { + if (!this.batchCompletion[batchId]) { + throw new Error(`Batch ${batchId} does not exist.`) + } + + // 获取当前批次的所有的任务 + let initialCount = this.tasks.filter((item) => item.batchId === batchId).length + let finalCount = initialCount + + // 首先,判断是否指定了taskId来决定是移除单个任务还是整个批次 + if (taskId == null) { + // 删除整个批次 + this.tasks = this.tasks.filter((item) => item.batchId !== batchId) + finalCount = this.tasks.filter((item) => item.batchId === batchId).length + } else if (taskId == 'all') { + // 删除所有的指定的子批次 + this.tasks = this.tasks.filter( + (item) => !(item.subBatchId == subBatchId && item.batchId == batchId) + ) + finalCount = this.tasks.filter( + (item) => item.subBatchId == subBatchId && item.batchId == batchId + ).length + } else { + // 删除指定的任务,需要考虑subBatchId + this.tasks = this.tasks.filter( + (item) => + !(item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId) + ) + finalCount = this.tasks.filter( + (item) => + item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId + ).length + } + // 在移除后再次计算该批次的任务数量 + // const finalCount = this.tasks.length; + // 使用两次计数的差值更新 remaining + const removedTasks = initialCount - finalCount + if (removedTasks > 0) { + this.batchCompletion[batchId].remaining -= removedTasks + if (this.batchCompletion[batchId].subBatches[subBatchId]) { + this.batchCompletion[batchId].subBatches[subBatchId].remaining -= removedTasks + this.batchCompletion[batchId].subBatches[subBatchId].callback = null } + } + // 检查并处理完整批次和小批次的完成状态 + const batch = this.batchCompletion[batchId] + // 判断子批次的数量 + const subBatch = this.batchCompletion[batchId].subBatches[subBatchId] + // 子批次完成 + if (subBatch && subBatch.remaining == 0 && subBatch.callback) { + subBatch.callback(subBatch.failedTasks) + delete this.batchCompletion[batchId].subBatches[subBatchId] // 清理批次完成状态 + } + // 总批次完成 + if (batch && batch.remaining === 0 && batch.callback) { + const callback = batch.callback + callback(batch.failedTasks) + delete this.batchCompletion[batchId] // 清理批次完成状态 + } else { + // 遍历小批次,看是否有需要清理的 + for (let subBatchId in batch.subBatches) { + const subBatch = batch.subBatches[subBatchId] + if (subBatch.remaining === 0) { + // 这里可以进行小批次完成后的处理,比如调用小批次的特定回调 + console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`) + // 可以选择删除空的小批次状态,如果不需要保留它的完成记录 + delete batch.subBatches[subBatchId] + } + } + } + return { + code: 1 + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - // 获取currentItem - getCurrentCreateItem() { - return this.currentCreateItem; - } + // 获取指定batch的任务列表 + getTasks(batchId) { + return this.tasks.filter((item) => item.batchId === batchId) + } - // 获取当前正在执行的任务数量 - getCurrentConcurrency() { - return this.currentConcurrency; + // 获取失败的任务 + getFailedTasks(batchId, subBatchId = 'default') { + if (this.batchCompletion[batchId] && this.batchCompletion[batchId].subBatches[subBatchId]) { + return this.batchCompletion[batchId].subBatches[subBatchId].failedTasks } + return [] + } - // 获取当前任务的限制任务数量 - getConcurrencyLimit() { - return this.concurrencyLimit + // 手动开启下一个任务 + async startNextTask(taskCount = 3) { + // 判断当前是不是有任务正在执行 + if (this.currentCreateItem) { + return } + console.log('调用开始下一个任务', this.taskProgress) + if (this.manualMode && this.tasks.length > 0 && this.taskProgress.length < taskCount) { + this.process(taskCount) + } + } - // 获取当前正在执行的任务对象() - getTaskProgress() { - return this.taskProgress; + // 修改currentItem + setCurrentCreateItem(item) { + this.currentCreateItem = item + if (item) { + // 判断相同的ID的任务是否存在,存在则不添加 + let index = this.taskProgress.findIndex((task) => task.id === item.id) + if (index == -1) { + this.taskProgress.push(item) + } else { + // 直接修改 + this.taskProgress[index] = item + } } + } - // 删除自动设置的任务 - removeTaskProgress(callback) { - this.taskProgress = callback(this.taskProgress); - console.log("删除对应消息的任务", this.taskProgress) - } -} \ No newline at end of file + // 获取currentItem + getCurrentCreateItem() { + return this.currentCreateItem + } + + // 获取当前正在执行的任务数量 + getCurrentConcurrency() { + return this.currentConcurrency + } + + // 获取等待中的队列 + getWaitingQueue() { + return this.tasks.length + } + + // 获取当前任务的限制任务数量 + getConcurrencyLimit() { + return this.concurrencyLimit + } + + // 修改任务数据 + + // 获取当前正在执行的任务对象() + getTaskProgress() { + return this.taskProgress + } + + // 删除自动设置的任务 + removeTaskProgress(callback) { + this.taskProgress = callback(this.taskProgress) + console.log('删除对应消息的任务', this.taskProgress) + } +} diff --git a/src/main/setting/basicSetting.js b/src/main/setting/basicSetting.js index b052848..3b10246 100644 --- a/src/main/setting/basicSetting.js +++ b/src/main/setting/basicSetting.js @@ -1,6 +1,6 @@ import SoftwareService from '../../define/db/service/SoftWare/softwareService' import { ComponentSize } from '../../define/enum/softwareEnum' -import { errorMessage, successMessage } from '../generalTools' +import { errorMessage, successMessage } from '../Public/generalTools' export class BasicSetting { constructor() {} diff --git a/src/main/setting/gptSetting.js b/src/main/setting/gptSetting.js index 6a1732a..b7bf144 100644 --- a/src/main/setting/gptSetting.js +++ b/src/main/setting/gptSetting.js @@ -1,9 +1,11 @@ import path from 'path' import os from 'os' import { define } from '../../define/define' -import { errorMessage, successMessage } from '../generalTools' +import { errorMessage, successMessage } from '../Public/generalTools' import axios from 'axios' import { ServiceBase } from '../../define/db/service/serviceBase' +import { isEmpty } from 'lodash' +import { ValidateJson } from '../../define/Tools/validate' export class GptSetting extends ServiceBase { constructor() { @@ -42,7 +44,7 @@ export class GptSetting extends ServiceBase { try { await this.InitService() let res = this.softService.GetSoftWarePropertyData('aiSetting') - if (res.data == null || res.data == undefined || res.data == '') { + if (isEmpty(res)) { // 没有数据需要额外初始化 res.data = { laiapi: { @@ -62,9 +64,13 @@ export class GptSetting extends ServiceBase { } } } else { - res.data = JSON.parse(res.data) + let tryP = ValidateJson(res) + if (!tryP) { + throw new Error('AI设置的数据格式不正确') + } + res = JSON.parse(res) } - return res + return successMessage(res, '请求成功', 'GptSetting_GetAISetting') } catch (error) { return errorMessage( '获取软件设置里面的AI设置错误,错误信息如下:' + error.toString(), diff --git a/src/main/setting/mjSetting.js b/src/main/setting/mjSetting.js index 13670f3..5c6a326 100644 --- a/src/main/setting/mjSetting.js +++ b/src/main/setting/mjSetting.js @@ -1,7 +1,7 @@ import axios from 'axios' import { MJSettingService } from '../../define/db/service/SoftWare/mjSettingService' import { define } from '../../define/define' -import { errorMessage, successMessage } from '../generalTools' +import { errorMessage, successMessage } from '../Public/generalTools' import { isEmpty } from 'lodash' const { v4: uuidv4 } = require('uuid') diff --git a/src/main/setting/setting.js b/src/main/setting/setting.js index e16a7ef..0d293f3 100644 --- a/src/main/setting/setting.js +++ b/src/main/setting/setting.js @@ -1,347 +1,355 @@ -const fspromises = require("fs").promises; -import path from "path"; -import axios from "axios"; -const { JSDOM } = require('jsdom'); -import { define } from "../../define/define"; -import { Tools } from "../tools"; -import { ClipSetting } from "../../define/setting/clipSetting"; -import { ImageSetting } from "../../define/setting/imageSetting"; -import { DEFINE_STRING } from "../../define/define_string"; -import { TagDefine } from "../../define/tagDefine"; -import { errorMessage } from "../generalTools"; +const fspromises = require('fs').promises +import path from 'path' +import axios from 'axios' +const { JSDOM } = require('jsdom') +import { define } from '../../define/define' +import { Tools } from '../tools' +import { ClipSetting } from '../../define/setting/clipSetting' +import { ImageSetting } from '../../define/setting/imageSetting' +import { DEFINE_STRING } from '../../define/define_string' +import { TagDefine } from '../../define/tagDefine' +import { errorMessage } from '../Public/generalTools' +import { TaskManager } from '../Service/taskManage' -let tagDefine = new TagDefine(global); +let tagDefine = new TagDefine(global) export class Setting { - constructor(global) { - this.global = global; - this.tools = new Tools(); + constructor(global) { + this.global = global + this.tools = new Tools() + } + + //#region 剪映设置 + + /** + * 添加背景音乐文件夹设置 + * @param {*} value + */ + async AddBackgroundMusicFolder(value) { + return await ClipSetting.AddBackgroundMusicFolder(value) + } + + /** + * 获取剪映背景音乐配置列表 + */ + async GetBackGroundMusicConfigList() { + try { + let res = await this.tools.getJsonFilePropertyValue( + define.clip_setting, + 'background_music_setting', + [] + ) + return { + code: 1, + value: res + } + } catch (error) { + return { + code: 0, + error: error.toString() + } } + } - //#region 剪映设置 + /** + * 删除剪辑配置里面对应ID的数据 + * @param {要删除的样式ID} value + */ + async deleteClipSetting(property, value) { + return ClipSetting.deleteClipSetting(property, value) + } - /** - * 添加背景音乐文件夹设置 - * @param {*} value - */ - async AddBackgroundMusicFolder(value) { - return await ClipSetting.AddBackgroundMusicFolder(value); + /** + * 获取剪映的关键帧配置 + * @returns + */ + async GetKeyFrameConfigData() { + try { + let key_frame = await this.tools.getJsonFilePropertyValue( + define.clip_setting, + 'key_frame', + null, + false + ) + return { + code: 1, + data: key_frame + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - /** - * 获取剪映背景音乐配置列表 - */ - async GetBackGroundMusicConfigList() { - try { - let res = await this.tools.getJsonFilePropertyValue(define.clip_setting, "background_music_setting", []); - return { - code: 1, - value: res - } - } catch (error) { - return { - code: 0, - error: error.toString() - } + /** + * 获取剪映关键帧配置 + * @returns + */ + async GetKeyFrameOptions() { + return await ClipSetting.GetKeyFrameOptions() + } + + async SaveKeyFrameSetting(value) { + return await ClipSetting.SaveKeyFrameSetting(value) + } + + //#endregion + + //#region 图片设置 + /** + * 获取自动保存图片的存储文件的方式 + * @returns + */ + async GetAutoSaveImageClassifyOptions() { + return await ImageSetting.GetAutoSaveImageClassifyOptions() + } + + /** + * 保存自动保存图片的设置 + * @param {*} value + * @returns + */ + async SaveImageAutoSaveSetting(value) { + return await ImageSetting.SaveImageAutoSaveSetting(value) + } + + /** + * 获取自动保存图片的设置 + * @param {*} value + * @returns + */ + async GetImageAutoSaveSetting(value) { + return await ImageSetting.GetImageAutoSaveSetting(value) + } + + /** + * 手动另存文件夹 + * @returns + */ + async SaveImageToOtherFolder(value) { + return await ImageSetting.SaveImageToOtherFolder([], value) + } + + //#endregion + + /** + * 移除任务 + * @param {*} value + */ + async RemoveTask(value) { + if (value[0] == DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) { + this.global.fileQueue.removeTask(value[0], value[1], value[2]) + } else { + this.global.requestQuene.removeTask(value[0], value[1], value[2]) + if (this.global.mjGenerateQuene) { + this.global.mjGenerateQuene.removeTask(value[0], value[1], value[2]) + } + } + } + /** + * 加载SD配置文件 + */ + async InitSDConfig() { + try { + let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')) + return { + code: 1, + data: { + webui_api_url: sd_config.setting.webui_api_url, + type: sd_config.setting.type, + sampler_name: sd_config.webui.sampler_name, + prompt: sd_config.webui.prompt, + negative_prompt: sd_config.webui.negative_prompt, + denoising_strength: sd_config.webui.denoising_strength, + steps: sd_config.webui.steps, + width: sd_config.webui.width, + height: sd_config.webui.height, + adetailer: sd_config.webui.adetailer, + batch_size: sd_config.setting.batch_size, + seed: sd_config.setting.seed, + style_weight: sd_config.setting.style_weight, + cfg_scale: sd_config.webui.cfg_scale, + sd_model: sd_config.sd_model, + lora: sd_config.lora, + sampler: sd_config.sampler } + } + } catch (error) { + return { + code: 0, + message: error + } } + } - /** - * 删除剪辑配置里面对应ID的数据 - * @param {要删除的样式ID} value - */ - async deleteClipSetting(property, value) { - return ClipSetting.deleteClipSetting(property, value); + /** + * 获取主页显示的信息 + */ + async GetShowMessage() { + try { + let res = await axios.get('https://share.weiyun.com/1EPtoGg8') + const dom = new JSDOM(res.data, { + runScripts: 'dangerously', + resources: 'usable' + }) + + // 创建一个函数来异步获取syncData + async function getSyncData() { + // 创建一个Promise来等待onload事件 + await new Promise((resolve, reject) => { + dom.window.onload = () => { + resolve() + } + + // 设置一个超时,以防onload事件永远不触发 + setTimeout(() => { + reject(new Error('Loading timed out')) + }, 5000) // 10秒超时 + }) + + // 返回syncData对象 + return dom.window.syncData + } + + // 使用异步函数并处理结果 + let re = await getSyncData() + return { + code: 1, + data: re + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - /** - * 获取剪映的关键帧配置 - * @returns - */ - async GetKeyFrameConfigData() { - try { - let key_frame = await this.tools.getJsonFilePropertyValue(define.clip_setting, "key_frame", null, false); - return { - code: 1, - data: key_frame - } - } catch (error) { - return { - code: 0, - message: error.toString() - } - } + /** + * 初始化配置 + */ + async getSettingDafultData() { + // 加载通用配置 + let data = await fspromises.readFile(define.config_path, 'utf-8') + let sd_data = await fspromises.readFile(define.sd_setting, 'utf-8') + let config_json_date = JSON.parse(data) + config_json_date.webui_api_url = JSON.parse(sd_data).setting.webui_api_url + config_json_date['space_image'] = define.zhanwei_image + return config_json_date + } + + /** + * 修改配置文件中的剪映的草稿位置 + * @param {剪映的草稿位置} value + * @returns + */ + async ModifySampleSetting(value) { + try { + value = JSON.parse(value) + // 当前的配置文件的内容就是global.config的内容 + // 直接修改 global.config的内容即可 + // 传入的value是一个对象,需要遍历其中的所有属性,并将属性值进行修改 + for (let key in value) { + this.global.config[key] = value[key] + } + await fspromises.writeFile(define.config_path, JSON.stringify(this.global.config)) + return { + code: 1, + message: '保存成功' + } + } catch (error) { + return { + cdoe: '0', + message: '保存失败,错误信息如下:' + '\n' + error.toString() + } } + } - /** - * 获取剪映关键帧配置 - * @returns - */ - async GetKeyFrameOptions() { - return await ClipSetting.GetKeyFrameOptions(); + /** + * 检查机器码是不是存在 + * @param {*} value + * @returns + */ + async CheckMachineId(value) { + try { + // 判断机器码是不是存在 + let res = await axios.post('http://api.yu-zhile.com/GetMachineStatus', { + machineId: value + }) + + // let res = await axios.get('http://lapi.laitool.cn/api/Machine/GetMachineStatus?machineId=' + value); + if (res.status != 200) { + throw new Error('请求错误') + } + if (res.data.code == 0) { + throw new Error(res.data.message) + } + + this.global.endTime = res.data.endTime + this.global.permissions = res.data.permissions + this.global.CheckMachineId = true + // 这边初始化任务队列 + if (!global.taskManager) { + global.taskManager = new TaskManager() + global.taskManager.InitListeners() // 启动监听 + } + return { + code: 1 + } + } catch (error) { + return { + code: 0, + message: error.toString() + } } + } - async SaveKeyFrameSetting(value) { - return await ClipSetting.SaveKeyFrameSetting(value); - } + /** + * 获取选择角色场景模式的options + * @returns + */ + async GetRoleSceneModeOptions() { + return tagDefine.getTagSelectModel() + } - //#endregion + /** + * 获取生图的类别(sd,mj,d3) + * @returns + */ + async GetImageGenerateCategory() { + return ImageSetting.GetImageGenerateCategory() + } + //#region SD设置 - //#region 图片设置 - /** - * 获取自动保存图片的存储文件的方式 - * @returns - */ - async GetAutoSaveImageClassifyOptions() { - return await ImageSetting.GetAutoSaveImageClassifyOptions(); - } + //#endregion - /** - * 保存自动保存图片的设置 - * @param {*} value - * @returns - */ - async SaveImageAutoSaveSetting(value) { - return await ImageSetting.SaveImageAutoSaveSetting(value); - } + //#region MJ设置 - /** - * 获取自动保存图片的设置 - * @param {*} value - * @returns - */ - async GetImageAutoSaveSetting(value) { - return await ImageSetting.GetImageAutoSaveSetting(value); - } + /** + * 获取指定的配置文件里面指定的属性的数据 + * @param {*} value 执行方法必要的信息 + * 0 define中的指定属性(指定的配置文件) + * 1 要获取的什么属性信息 property,property 为null,赶回当前配置文件的所有数据 + * 2 是不是要校验属性不存在 + * 3 属性没有找到的默认值 + */ + async GetDefineConfigJsonByProperty(value) { + return ImageSetting.GetDefineConfigJsonByProperty(value) + } - /** - * 手动另存文件夹 - * @returns - */ - async SaveImageToOtherFolder(value) { - return await ImageSetting.SaveImageToOtherFolder([], value); - } + /** + * 保存指定的配置文件里面指定的属性的数据 + * @param {*} value + * 0 define中的指定属性(指定的配置文件) + * 1 要获取的什么属性信息 property,property 为null,赶回当前配置文件的所有数据 + * 2 要写入的值 + * 3 是不是要校验属性不存在 + */ + async SaveDefineConfigJsonByProperty(value) { + return ImageSetting.SaveDefineConfigJsonByProperty(value) + } - - //#endregion - - - /** - * 移除任务 - * @param {*} value - */ - async RemoveTask(value) { - if (value[0] == DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) { - this.global.fileQueue.removeTask(value[0], value[1], value[2]) - } else { - this.global.requestQuene.removeTask(value[0], value[1], value[2]) - if (this.global.mjGenerateQuene) { - this.global.mjGenerateQuene.removeTask(value[0], value[1], value[2]) - } - } - } - /** - * 加载SD配置文件 - */ - async InitSDConfig() { - try { - let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); - return { - code: 1, - data: { - webui_api_url: sd_config.setting.webui_api_url, - type: sd_config.setting.type, - sampler_name: sd_config.webui.sampler_name, - prompt: sd_config.webui.prompt, - negative_prompt: sd_config.webui.negative_prompt, - denoising_strength: sd_config.webui.denoising_strength, - steps: sd_config.webui.steps, - width: sd_config.webui.width, - height: sd_config.webui.height, - adetailer: sd_config.webui.adetailer, - batch_size: sd_config.setting.batch_size, - seed: sd_config.setting.seed, - style_weight: sd_config.setting.style_weight, - cfg_scale: sd_config.webui.cfg_scale, - sd_model: sd_config.sd_model, - lora: sd_config.lora, - sampler: sd_config.sampler, - } - } - } catch (error) { - return { - code: 0, - message: error - } - } - } - - - /** - * 获取主页显示的信息 - */ - async GetShowMessage() { - try { - let res = await axios.get('https://share.weiyun.com/1EPtoGg8'); - const dom = new JSDOM(res.data, { - runScripts: "dangerously", - resources: "usable", - }); - - // 创建一个函数来异步获取syncData - async function getSyncData() { - // 创建一个Promise来等待onload事件 - await new Promise((resolve, reject) => { - dom.window.onload = () => { - resolve(); - }; - - // 设置一个超时,以防onload事件永远不触发 - setTimeout(() => { - reject(new Error('Loading timed out')); - }, 5000); // 10秒超时 - }); - - // 返回syncData对象 - return dom.window.syncData; - } - - // 使用异步函数并处理结果 - let re = await getSyncData(); - return { - code: 1, - data: re - } - - } catch (error) { - return { - code: 0, - message: error.toString() - } - } - } - - /** - * 初始化配置 - */ - async getSettingDafultData() { - // 加载通用配置 - let data = await fspromises.readFile(define.config_path, "utf-8"); - let sd_data = await fspromises.readFile(define.sd_setting, 'utf-8'); - let config_json_date = JSON.parse(data); - config_json_date.webui_api_url = JSON.parse(sd_data).setting.webui_api_url; - config_json_date["space_image"] = define.zhanwei_image; - return config_json_date; - } - - /** - * 修改配置文件中的剪映的草稿位置 - * @param {剪映的草稿位置} value - * @returns - */ - async ModifySampleSetting(value) { - try { - value = JSON.parse(value); - // 当前的配置文件的内容就是global.config的内容 - // 直接修改 global.config的内容即可 - // 传入的value是一个对象,需要遍历其中的所有属性,并将属性值进行修改 - for (let key in value) { - this.global.config[key] = value[key]; - } - await fspromises.writeFile(define.config_path, JSON.stringify(this.global.config)); - return { - code: 1, - message: "保存成功" - } - } catch (error) { - return { - cdoe: "0", - message: "保存失败,错误信息如下:" + '\n' + error.toString() - } - } - } - - /** - * 检查机器码是不是存在 - * @param {*} value - * @returns - */ - async CheckMachineId(value) { - try { - // 判断机器码是不是存在 - let res = await axios.post('http://api.yu-zhile.com/GetMachineStatus', { - machineId: value - }) - - // let res = await axios.get('http://lapi.laitool.cn/api/Machine/GetMachineStatus?machineId=' + value); - if (res.status != 200) { - throw new Error("请求错误"); - } - if (res.data.code == 0) { - throw new Error(res.data.message); - } - - this.global.endTime = res.data.endTime; - this.global.permissions = res.data.permissions; - this.global.CheckMachineId = true; - return { - code: 1, - } - - } catch (error) { - return { - code: 0, - message: error.toString() - } - } - } - - /** - * 获取选择角色场景模式的options - * @returns - */ - async GetRoleSceneModeOptions() { - return tagDefine.getTagSelectModel(); - } - - /** - * 获取生图的类别(sd,mj,d3) - * @returns - */ - async GetImageGenerateCategory() { - return ImageSetting.GetImageGenerateCategory(); - } - - //#region SD设置 - - //#endregion - - //#region MJ设置 - - /** - * 获取指定的配置文件里面指定的属性的数据 - * @param {*} value 执行方法必要的信息 - * 0 define中的指定属性(指定的配置文件) - * 1 要获取的什么属性信息 property,property 为null,赶回当前配置文件的所有数据 - * 2 是不是要校验属性不存在 - * 3 属性没有找到的默认值 - */ - async GetDefineConfigJsonByProperty(value) { - return ImageSetting.GetDefineConfigJsonByProperty(value); - } - - - /** - * 保存指定的配置文件里面指定的属性的数据 - * @param {*} value - * 0 define中的指定属性(指定的配置文件) - * 1 要获取的什么属性信息 property,property 为null,赶回当前配置文件的所有数据 - * 2 要写入的值 - * 3 是不是要校验属性不存在 - */ - async SaveDefineConfigJsonByProperty(value) { - return ImageSetting.SaveDefineConfigJsonByProperty(value); - } - - //#endregion -} \ No newline at end of file + //#endregion +} diff --git a/src/main/setting/ttsSetting.js b/src/main/setting/ttsSetting.js deleted file mode 100644 index 9aa9e86..0000000 --- a/src/main/setting/ttsSetting.js +++ /dev/null @@ -1,59 +0,0 @@ -import { errorMessage } from '../generalTools' -import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' - -export class TTSSetting { - constructor() {} - - /** - * 初始化TTS服务 - */ - async InitService() { - if (!this.softService) { - this.softService = await SoftwareService.getInstance() - } - } - - /** - * 获取TTS配置 - */ - async GetTTSCOnfig() { - try { - await this.InitService() - let res = this.softService.GetSoftWarePropertyData('ttsSetting') - if (res.data == null) { - // 没有数据,需要初始化 - res.data = { - selectModel: 'edge-tts', - edgeTTS: { - value: 'zh-CN-XiaoxiaoNeural', - gender: 'Female', - label: '晓晓', - lang: 'zh-CN', - saveSubtitles: true, - pitch: '0', // 语调 - rate: '10', // 倍速 - volumn: '0' // 音量 - } - } - } else { - res.data = JSON.parse(res.data) - } - return res - } catch (error) { - return errorMessage('获取TTS配置失败,错误信息如下:' + error.toString(), 'TTS_GetTTSCOnfig') - } - } - /** - * 保存TTS配置 - * @param {*} data 要保存的数据 - */ - async SaveTTSConfig(data) { - try { - await this.InitService() - let res = this.softService.SaveSoftwarePropertyData('ttsSetting', JSON.stringify(data)) - return res - } catch (error) { - return errorMessage('保存TTS配置失败,错误信息如下:' + error.toString(), 'TTS_SaveTTSConfig') - } - } -} diff --git a/src/main/setting/writeSetting.js b/src/main/setting/writeSetting.js index b15a4ee..01a7a2f 100644 --- a/src/main/setting/writeSetting.js +++ b/src/main/setting/writeSetting.js @@ -1,5 +1,7 @@ -import { errorMessage } from '../generalTools' +import { errorMessage, successMessage } from '../Public/generalTools' import { SoftwareService } from '../../define/db/service/SoftWare/softwareService' +import { isEmpty } from 'lodash' +import { ValidateJson } from '../../define/Tools/validate' /** * 文案的设置相关的类 @@ -20,7 +22,7 @@ export class WritingSetting { try { await this.InitService() let res = this.softService.GetSoftWarePropertyData('writeSetting') - if (res.data == null) { + if (isEmpty(res)) { res.data = JSON.stringify({ split_char: '。,“”‘’!?【】《》()…—:;.,\'\'""!?[]<>()...-:;', mrege_count: 3, @@ -32,9 +34,13 @@ export class WritingSetting { randomCount: 0 }) } else { - res.data = JSON.parse(res.data) + let tryP = ValidateJson(res) + if (!tryP) { + throw new Error('文案配置的数据格式不正确') + } + res = JSON.parse(res) } - return res + return successMessage(res, '获取文案配置成功', 'WritingSetting_GetWritingConfig') } catch (error) { return errorMessage( '获取文案配置失败,错误信息如下:' + error.toString(), diff --git a/src/main/tools.js b/src/main/tools.js index c90647e..fc49b32 100644 --- a/src/main/tools.js +++ b/src/main/tools.js @@ -1,346 +1,348 @@ -const fspromises = require("fs").promises; -const { net } = require('electron'); -import path from "path"; -const util = require('util'); -const { spawn, exec } = require('child_process'); -const execAsync = util.promisify(exec); -import { define } from "../define/define"; -import { get, has, set } from "lodash"; -import { basicApi } from "../api/apiBasic"; +const fspromises = require('fs').promises +const { net } = require('electron') +import path from 'path' +const util = require('util') +const { spawn, exec } = require('child_process') +const execAsync = util.promisify(exec) +import { define } from '../define/define' +import { get, has, set } from 'lodash' +import { basicApi } from '../api/apiBasic' export class Tools { - constructor() { } + constructor() {} - /** - * 判断判断文件夹或者是文件是不是存在 - * @param {文件或者是文件夹的路径} filePath - * @returns + /** + * 判断判断文件夹或者是文件是不是存在 + * @param {文件或者是文件夹的路径} filePath + * @returns */ - async checkExists(filePath) { - try { - await fspromises.access(filePath); - return true; // 文件或目录存在 - } catch (error) { - return false; // 文件或目录不存在 - } + async checkExists(filePath) { + try { + await fspromises.access(filePath) + return true // 文件或目录存在 + } catch (error) { + return false // 文件或目录不存在 } + } - /** - * 获取指定的文件,然后将其转换为base64 - * @param {*} filePath 文件地址 - * @returns - */ - async readFileBase64(filePath) { - try { - let data = await fspromises.readFile(filePath); - return data.toString('base64'); - } - catch (error) { - throw new Error(error); - } + /** + * 获取指定的文件,然后将其转换为base64 + * @param {*} filePath 文件地址 + * @returns + */ + async readFileBase64(filePath) { + try { + let data = await fspromises.readFile(filePath) + return data.toString('base64') + } catch (error) { + throw new Error(error) } + } + /** + * 判断json文件是不是存在,不存在的话,协议一个空的json文件 + * @param {*} filePath 文件地址 + */ + async checkJsonFileExistsOrCreate(filePath, defaultValue = '{}') { + try { + if (!(await this.checkExists(filePath))) { + // 判断传入的json文件的父文件夹是不是存在,不存在的话,创建 + let parentFolder = path.dirname(filePath) + if (!(await this.checkExists(parentFolder))) { + // 创建文件夹 + await fspromises.mkdir(parentFolder, { recursive: true }) + } + await fspromises.writeFile(filePath, defaultValue) + } + } catch (error) { + throw new Error(error) + } + } - /** - * 判断json文件是不是存在,不存在的话,协议一个空的json文件 - * @param {*} filePath 文件地址 - */ - async checkJsonFileExistsOrCreate(filePath, defaultValue = "{}") { - try { - if (!(await this.checkExists(filePath))) { - // 判断传入的json文件的父文件夹是不是存在,不存在的话,创建 - let parentFolder = path.dirname(filePath); - if (!(await this.checkExists(parentFolder))) { - // 创建文件夹 - await fspromises.mkdir(parentFolder, { recursive: true }); - } - await fspromises.writeFile(filePath, defaultValue); + /** + * 删除目标图片,然后将原图片的exif信息删除,然后将原图片复制到目标图片地址 + * @param {*} source 原图片地址 + * @param {*} target 目标图片地址 + */ + async deletePngAndDeleteExifData(source, target) { + try { + let exiftool = path.join(define.package_path, 'exittool/exiftool.exe') + await this.deleteFileOrDirectory(target) + let script = `"${exiftool}" -all= -overwrite_original "${source}" -o "${target}"` + const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' }) + } catch (error) { + throw error + } + } + + /** + * 复制文件或者是文件夹到指定的位置 + * @param {*} src 源文件或者是文件夹 + * @param {*} dest 目标文件或者是文件夹 + */ + async copyFileOrDirectory(src, dest) { + try { + let stats = await fspromises.stat(src) + if (stats.isDirectory()) { + await fspromises.mkdir(dest, { recursive: true }) + let items = await fspromises.readdir(src) + let promises = items.map((item) => + this.copyFileOrDirectory(path.join(src, item), path.join(dest, item)) + ) + await Promise.all(promises) + } else if (stats.isFile()) { + await fspromises.copyFile(src, dest) + } + } catch (error) { + throw new Error(error) + } + } + + /** + * 延时多少秒 + * @param {*} time + * @returns + */ + async delay(time) { + return new Promise((resolve) => setTimeout(resolve, time)) + } + + /** + * 判断指定路径的文件夹是不是存在,如果不存在则创建 + */ + async checkFolderExistsOrCreate(folderPath) { + try { + if (!(await this.checkExists(folderPath))) { + await fspromises.mkdir(folderPath, { recursive: true }) + } + } catch (error) { + throw new Error(error) + } + } + + /** + * 获取指定路径下面的指定的拓展的文件 + * @param {*} folderPath + * @param {*} extensions + * @returns + */ + async getFilesWithExtensions(folderPath, extensions) { + try { + let entries = await fspromises.readdir(folderPath, { withFileTypes: true }) + let files = [] + // 使用Promise.all来并行处理所有的stat调用 + const fileStats = await Promise.all( + entries.map(async (entry) => { + const entryPath = path.join(folderPath, entry.name) + if (entry.isFile()) { + return { + name: entry.name, + path: entryPath, + isFile: true } - } catch (error) { - throw new Error(error); - } - } - - /** - * 删除目标图片,然后将原图片的exif信息删除,然后将原图片复制到目标图片地址 - * @param {*} source 原图片地址 - * @param {*} target 目标图片地址 - */ - async deletePngAndDeleteExifData(source, target) { - try { - let exiftool = path.join(define.package_path, 'exittool/exiftool.exe'); - await this.deleteFileOrDirectory(target); - let script = `"${exiftool}" -all= -overwrite_original "${source}" -o "${target}"`; - const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' }); - } catch (error) { - throw error; - } - } - - /** - * 复制文件或者是文件夹到指定的位置 - * @param {*} src 源文件或者是文件夹 - * @param {*} dest 目标文件或者是文件夹 - */ - async copyFileOrDirectory(src, dest) { - try { - let stats = await fspromises.stat(src); - if (stats.isDirectory()) { - await fspromises.mkdir(dest, { recursive: true }); - let items = await fspromises.readdir(src); - let promises = items.map(item => this.copyFileOrDirectory(path.join(src, item), path.join(dest, item))); - await Promise.all(promises); - } else if (stats.isFile()) { - await fspromises.copyFile(src, dest); + } else { + return { + isFile: false } + } + }) + ) + + // 过滤出文件并且满足扩展名要求的文件 + files = fileStats.filter( + (fileStat) => + fileStat.isFile && extensions.includes(path.extname(fileStat.name).toLowerCase()) + ) + + // 对files数组进行排序,基于文件名 + files.sort((a, b) => a.name.localeCompare(b.name)) + + // 返回文件名数组 + return files.map((fileStat) => path.join(folderPath, fileStat.name)) + } catch (error) { + throw new Error(error) + } + } + + /** + * 去除字符串中的所有标点符号,包括英文和中文的标点,以及省略号。 + * + * @param {string} sentence 待处理的字符串。 + * @return {string} 去除标点后的字符串。 + */ + removePunctuationIncludingEllipsis(sentence) { + // 扩展正则表达式以包含中文标点符号和省略号 + // 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符 + const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g + + // 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串 + return sentence.replace(punctuationRegExp, '') + } + + /** + * 将一个数组写到一个txt文件中 + * @param {数据数组} dataArray + * @param {txt文件地址} filePath + */ + async writeArrayToFile(dataArray, filePath) { + try { + // 将数组转换为字符串,每个元素后面加上换行符 + const dataString = dataArray.join('\n') + // 使用fs.writeFile异步写入文件 + await fspromises.writeFile(filePath, dataString) + return dataString + } catch (error) { + throw new Error(error) + } + } + + /** + * 删除指定的文件夹和文件地址(删除文件夹会删除文件夹里面的所有的文件) + * @param {文件或者文件夹的地址} dir + */ + async deleteFileOrDirectory(dir) { + let isExist = await this.checkExists(dir) + if (isExist) { + let stats = await fspromises.stat(dir) + if (stats.isDirectory()) { + let items = await fspromises.readdir(dir) + let promises = items.map((item) => this.deleteFileOrDirectory(path.join(dir, item))) + await Promise.all(promises) + await fspromises.rmdir(dir) + } else if (stats.isFile()) { + await fspromises.unlink(dir) + } + } + } + + /** + * 获取指定的json文件,并获取指定的属性的值(传入的属性值为null,返回全部。) + * 传入的property不为null,返回全部的json文件,检查是不是要判断传入的属性是不是存在。 + * 要检查属性是不是存在,传入的checkProperty为true,不检查传入的checkProperty为false + * checkProperty为true。若值不存在会抛出错误。为false。不存在直接返回null + * @param {*} filePath json文件地址 + * @param {*} property 属性名称 + * @param {*} defaultValue 默认值(默认为null) + * @param {*} checkProperty 是否检查属性是不是存在(默认为true,若为false,不存在直接返回设置的默认值) + * @returns + */ + async getJsonFilePropertyValue(filePath, property, defaultValue = null, checkProperty = true) { + try { + let isExist = await this.checkExists(filePath) + if (!isExist) { + throw new Error('文件不存在,请先添加') + } + let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8')) + let d = null + if (property) { + if (checkProperty) { + if (!has(config, property)) { + throw new Error(`${property} 属性不存在,请检查是不是少了什么步骤`) + } } - catch (error) { - throw new Error(error); + d = get(config, property, defaultValue) + } else { + d = config + } + return d + } catch (error) { + throw error + } + } + + /** + * 通过地址获取指定地址的json文件,然后写入指定的属性的值(判断是不是要检查属性是不是存在) + * @param {*} filePath 要写入的文件地址 + * @param {*} property 写入的属性名 + * @param {*} value 写入的属性的值 + * @param {*} checkProperty 是否检查属性是不是存在(默认为false,不检查直接写入-覆盖或新增。若需要检查。值不存在,会抛出错误) + */ + async writeJsonFilePropertyValue(filePath, property, value, checkProperty = false) { + try { + let isExist = await this.checkExists(filePath) + if (!isExist) { + throw new Error('文件不存在,请先添加') + } + let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8')) + if (checkProperty) { + if (!has(config, property)) { + throw new Error(`${property} 属性不存在,添加失败`) } + } + set(config, property, value) + await fspromises.writeFile(filePath, JSON.stringify(config)) + } catch (error) { + throw error } + } - /** - * 延时多少秒 - * @param {*} time - * @returns - */ - async delay(time) { - return new Promise(resolve => setTimeout(resolve, time)); + /** + * 获取指定文件夹下面特定条件的文件夹 + * @param {指定的文件夹目录} parentFolder + * @param {查询条件 start end include} condition + * @param {查询的值} value + * @returns + */ + async getSubFolderList(parentFolder, condition, value) { + try { + // console.log(value); + let folders = await fspromises.readdir(parentFolder, { withFileTypes: true }) + folders = folders.filter((item) => item.isDirectory()).map((item) => item.name) + + if (condition == 'start') { + folders = folders.filter((item) => item.startsWith(value)) + } else if (condition == 'end') { + folders = folders.filter((item) => item.endsWith(value)) + } else if (condition == 'include') { + //包含过滤 + folders = folders.filter((item) => item.includes(value)) + } else { + throw new Error('条件参数错误') + } + return folders + } catch (error) { + throw error } + } - /** - * 判断指定路径的文件夹是不是存在,如果不存在则创建 - */ - async checkFolderExistsOrCreate(folderPath) { - try { - if (!(await this.checkExists(folderPath))) { - await fspromises.mkdir(folderPath, { recursive: true }); - } - } catch (error) { - throw new Error(error); - } + /** + * 使用chromium下载文件 + * @param {*} url + * @param {*} filePath + * @returns + */ + async downloadFileUrl(url, filePath) { + try { + let data = await basicApi.downloadFileByURL(url) + await this.checkFolderExistsOrCreate(path.dirname(filePath)) + await fspromises.writeFile(filePath, data.data) + } catch (error) { + throw error } + } - /** - * 获取指定路径下面的指定的拓展的文件 - * @param {*} folderPath - * @param {*} extensions - * @returns - */ - async getFilesWithExtensions(folderPath, extensions) { - try { - let entries = await fspromises.readdir(folderPath, { withFileTypes: true }); - let files = []; - // 使用Promise.all来并行处理所有的stat调用 - const fileStats = await Promise.all(entries.map(async (entry) => { - const entryPath = path.join(folderPath, entry.name); - if (entry.isFile()) { - return { - name: entry.name, - path: entryPath, - isFile: true, - }; - } else { - return { - isFile: false, - }; - } - })); - - // 过滤出文件并且满足扩展名要求的文件 - files = fileStats.filter(fileStat => fileStat.isFile && extensions.includes(path.extname(fileStat.name).toLowerCase())); - - // 对files数组进行排序,基于文件名 - files.sort((a, b) => a.name.localeCompare(b.name)); - - // 返回文件名数组 - return files.map(fileStat => path.join(folderPath, fileStat.name)); - } catch (error) { - throw new Error(error); - } + /** + * 将base64保存为图片,然后删除图片的exif信息 + * @param {*} base64 图片的base64数据 + * @param {*} filePath 保存文件地址 + * @param {*} isDeleteExif 是不是要删除exif信息(默认为true) + */ + async saveBase64ToImage(base64, filePath, isDeleteExif = true) { + try { + let base64Data = base64.replace(/^data:image\/\w+;base64,/, '') + let dataBuffer = Buffer.from(base64Data, 'base64') + if (isDeleteExif) { + // 先将图片写道一个缓存位置,清除数据的时候,将原图片的exif信息删除,然后将原图片复制到目标图片地址 + let dir = path.dirname(filePath) + let tempFilePath = path.join(dir, `temp_${Date.now()}.png`) + await fspromises.writeFile(tempFilePath, dataBuffer) + this.deletePngAndDeleteExifData(tempFilePath, filePath) + } else { + await fspromises.writeFile(filePath, dataBuffer) + } + return filePath + } catch (error) { + throw error } - - /** - * 去除字符串中的所有标点符号,包括英文和中文的标点,以及省略号。 - * - * @param {string} sentence 待处理的字符串。 - * @return {string} 去除标点后的字符串。 - */ - removePunctuationIncludingEllipsis(sentence) { - // 扩展正则表达式以包含中文标点符号和省略号 - // 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符 - const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g; - - // 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串 - return sentence.replace(punctuationRegExp, ''); - } - - - /** - * 将一个数组写到一个txt文件中 - * @param {数据数组} dataArray - * @param {txt文件地址} filePath - */ - async writeArrayToFile(dataArray, filePath) { - try { - // 将数组转换为字符串,每个元素后面加上换行符 - const dataString = dataArray.join('\n'); - // 使用fs.writeFile异步写入文件 - await fspromises.writeFile(filePath, dataString); - return dataString - } catch (error) { - throw new Error(error); - } - } - - /** - * 删除指定的文件夹和文件地址(删除文件夹会删除文件夹里面的所有的文件) - * @param {文件或者文件夹的地址} dir - */ - async deleteFileOrDirectory(dir) { - let isExist = await this.checkExists(dir); - if (isExist) { - let stats = await fspromises.stat(dir); - if (stats.isDirectory()) { - let items = await fspromises.readdir(dir); - let promises = items.map(item => this.deleteFileOrDirectory(path.join(dir, item))); - await Promise.all(promises); - await fspromises.rmdir(dir); - } else if (stats.isFile()) { - await fspromises.unlink(dir); - } - } - } - - /** - * 获取指定的json文件,并获取指定的属性的值(传入的属性值为null,返回全部。) - * 传入的property不为null,返回全部的json文件,检查是不是要判断传入的属性是不是存在。 - * 要检查属性是不是存在,传入的checkProperty为true,不检查传入的checkProperty为false - * checkProperty为true。若值不存在会抛出错误。为false。不存在直接返回null - * @param {*} filePath json文件地址 - * @param {*} property 属性名称 - * @param {*} defaultValue 默认值(默认为null) - * @param {*} checkProperty 是否检查属性是不是存在(默认为true,若为false,不存在直接返回设置的默认值) - * @returns - */ - async getJsonFilePropertyValue(filePath, property, defaultValue = null, checkProperty = true) { - try { - let isExist = await this.checkExists(filePath); - if (!isExist) { - throw new Error("文件不存在,请先添加"); - } - let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8')); - let d = null; - if (property) { - if (checkProperty) { - if (!has(config, property)) { - throw new Error(`${property} 属性不存在,请检查是不是少了什么步骤`); - } - } - d = get(config, property, defaultValue) - - } else { - d = config; - } - return d; - } catch (error) { - throw error; - } - } - - /** - * 通过地址获取指定地址的json文件,然后写入指定的属性的值(判断是不是要检查属性是不是存在) - * @param {*} filePath 要写入的文件地址 - * @param {*} property 写入的属性名 - * @param {*} value 写入的属性的值 - * @param {*} checkProperty 是否检查属性是不是存在(默认为false,不检查直接写入-覆盖或新增。若需要检查。值不存在,会抛出错误) - */ - async writeJsonFilePropertyValue(filePath, property, value, checkProperty = false) { - try { - let isExist = await this.checkExists(filePath); - if (!isExist) { - throw new Error("文件不存在,请先添加"); - } - let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8')); - if (checkProperty) { - if (!has(config, property)) { - throw new Error(`${property} 属性不存在,添加失败`); - } - } - set(config, property, value); - await fspromises.writeFile(filePath, JSON.stringify(config)); - } catch (error) { - throw error; - } - } - - /** - * 获取指定文件夹下面特定条件的文件夹 - * @param {指定的文件夹目录} parentFolder - * @param {查询条件 start end include} condition - * @param {查询的值} value - * @returns - */ - async getSubFolderList(parentFolder, condition, value) { - try { - // console.log(value); - let folders = await fspromises.readdir(parentFolder, { withFileTypes: true }); - folders = folders.filter(item => item.isDirectory()) - .map(item => item.name) - - if (condition == "start") { - folders = folders.filter(item => item.startsWith(value)); - } else if (condition == "end") { - folders = folders.filter(item => item.endsWith(value)); - } else if (condition == "include") { - //包含过滤 - folders = folders.filter(item => item.includes(value)); - } else { - throw new Error("条件参数错误"); - } - return folders; - } catch (error) { - throw error; - } - } - - /** - * 使用chromium下载文件 - * @param {*} url - * @param {*} filePath - * @returns - */ - async downloadFileUrl(url, filePath) { - try { - let data = await basicApi.downloadFileByURL(url); - await fspromises.writeFile(filePath, data.data); - } catch (error) { - throw error; - } - } - - /** - * 将base64保存为图片,然后删除图片的exif信息 - * @param {*} base64 图片的base64数据 - * @param {*} filePath 保存文件地址 - * @param {*} isDeleteExif 是不是要删除exif信息(默认为true) - */ - async saveBase64ToImage(base64, filePath, isDeleteExif = true) { - try { - let base64Data = base64.replace(/^data:image\/\w+;base64,/, ""); - let dataBuffer = Buffer.from(base64Data, 'base64'); - if (isDeleteExif) { - // 先将图片写道一个缓存位置,清除数据的时候,将原图片的exif信息删除,然后将原图片复制到目标图片地址 - let dir = path.dirname(filePath); - let tempFilePath = path.join(dir, `temp_${Date.now()}.png`); - await fspromises.writeFile(tempFilePath, dataBuffer); - this.deletePngAndDeleteExifData(tempFilePath, filePath); - } else { - await fspromises.writeFile(filePath, dataBuffer); - } - return filePath; - } catch (error) { - throw error; - } - } -} \ No newline at end of file + } +} diff --git a/src/model/Setting/mjSetting.d.ts b/src/model/Setting/mjSetting.d.ts new file mode 100644 index 0000000..630bfaf --- /dev/null +++ b/src/model/Setting/mjSetting.d.ts @@ -0,0 +1,84 @@ +import { MJImageType, MJRobotType } from "../../define/enum/mjEnum" + +declare namespace MJSetting { + type ActionRemoteMJSetting = { + id?: string + accountId?: string + channelId?: string + coreSize: number + guildId?: string + enable: boolean + mjBotChannelId?: string + nijiBotChannelId?: string + queueSize: number // 队列大小 + remark?: string + timeoutMinutes: number + userAgent: string + userToken?: string + createTime?: Date + updateTime?: Date + } + + + type BrowserMJ = { + id: string + serviceId: string + channelId: string + mjBotId: string + nijBotId: string + token: string + userAgent: string + userAgentCustom: boolean + createTime: Date + updateTime: Date + version: string + } + + type RemoteMJ = { + id: string + accountId: string | null + channelId: string + coreSize: number + guildId: string + enable: boolean // 是否启用 + mjBotChannelId: string | null + nijiBotChannelId: string | null + queueSize: number + remark: string + remixAutoSubmit: boolean + timeoutMinutes: number + userAgent: string + userToken: string + createTime: Date + updateTime: Date + version: string + } + + type APIMj = { + id: string + mjApiUrl: string + mjSpeed: string // MJ出图的速度模式 + apiKey: string + createTime: Date + updateTime: Date + version: string + } + + type MjSetting = { + id: string + type: MJImageType + requestModel: MJImageType + selectRobot: MJRobotType + imageScale: string + imageModel: string + imageSuffix: string + taskCount: number + spaceTime: number + createTime: Date + updateTime: Date + version: string + apiSetting: APIMj + remoteSetting: RemoteMJ + browserSetting: BrowserMJ + } +} diff --git a/src/model/Setting/ttsSetting.d.ts b/src/model/Setting/ttsSetting.d.ts new file mode 100644 index 0000000..d1b2d2d --- /dev/null +++ b/src/model/Setting/ttsSetting.d.ts @@ -0,0 +1,18 @@ +declare namespace TTSSettingModel { + type EdgeTTSSetting = { + value: string // 人物值 + gender: string // 人物性别 + label: string // 人物名称 + lang: string // 语言 + saveSubtitles: boolean // 是否生成字幕 + pitch: number // 语调 + rate: number // 倍速 + volumn: number // 音量 + } + + type TTSSetting = { + selectModel: string // 选择模式 + edgeTTS: EdgeTTSSetting // edgeTTS设置 + } + function SetTTSSettingDefault(): TTSSetting +} diff --git a/src/model/book.d.ts b/src/model/book.d.ts new file mode 100644 index 0000000..3924acd --- /dev/null +++ b/src/model/book.d.ts @@ -0,0 +1,232 @@ +import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType } from "../define/enum/bookEnum" +import { MJAction } from "../define/enum/bookEnum" +import { MJImageType } from "../define/enum/mjEnum" + +declare namespace Book { + + type SelectBook = { + id?: string, + no?: number, + name?: string, + bookFolderPath?: string, + type?: BookType, + oldVideoPath?: string, + srtPath?: string, + audioPath?: string, + imageFolder?: string, + draftSrtStyle?: string | null // 草稿字幕样式 + backgroundMusic?: string | null // 背景音乐ID + friendlyReminder?: string | null // 友情提示 + subtitlePosition?: string, + updateTime?: Date, + createTime?: Date, + version?: string, + imageStyle?: string[] | null // 软件内置的样式 + autoAnalyzeCharacter?: string | null // 自动分析角色设置 + customizeImageStyle?: string[] | null // 自定义的样式 + videoConfig?: string | null // 合成视频设置 + prefixPrompt?: string | null // 前缀 + suffixPrompt?: string | null // 后缀 + } + + type SelectBookTask = { + no?: number, + id?: string, + bookId?: string, + name?: string, + generateVideoPath?: string, + srtPath?: string, + audioPath?: string, + draftSrtStyle?: string | null // 草稿字幕样式 + backgroundMusic?: string | null // 背景音乐ID + friendlyReminder?: string | null // 友情提示 + imageFolder?: string, + customizeImageStyle?: string[], + prefixPrompt?: string, + suffixPrompt?: string, + styleList?: BookStyle[] | DefineBookStyle[] // 样式列表, + status?: BookTaskStatus, + errorMsg?: string, + version?: string, + imageCategory?: BookImageCategory + imageStyle?: string[] | null // 软件内置的样式 + autoAnalyzeCharacter?: string | null // 自动分析角色设置 + customizeImageStyle?: string[] | null // 自定义的样式 + videoConfig?: string | null // 合成视频设置 + prefixPrompt?: string | null // 前缀 + suffixPrompt?: string | null // 后缀 + isAuto?: boolean // 是否标记全自动 + subImageFolder?: string[] | null // 子图片文件夹地址,多个 + } + + // 字幕相关 + type Subtitle = { + startTime: number + endTime: number + srtValue: string + id: string + } + + // 返回的MJ消息 + type MJMessage = { + id?: string + mjApiUrl?: string + progress: number + category: MJImageType + imageClick?: string // 图片点击(显示的小的) + imageShow?: string // 图片实际的地址 + messageId?: string // 消息ID(可以是MJ中的,也可以是API中的) + action?: MJAction // 动作(生图,反推之类) + status: string // 状态 + message?: string // 消息 + } + + type WebuiConfig = { + sampler_name: string // 采样器名称 + negative_prompt: string // 负面提示 + batch_size: number // 批次大小 + steps: number // 步数 + cfg_scale: number // 提示词相关性 + denoising_strength: number // 降噪强度 + width: number // 宽度 + height: number // 高度 + seed: number // 种子 + init_images: string // 初始图片(垫图的图片地址) + id: string + } + + type SDConfig = { + checkpoints: string // 大模型 + api: string // api地址 + model: string // 生图方式 + webuiConfig: WebuiConfig + id: string + } + + type ReversePrompt = { + id?: string + bookTaskDetailId?: string + prompt?: string + promptCN?: string + isSelect?: boolean + } + + type SelectBookTaskDetail = { + id?: string + no?: number + name?: string + bookId?: string + bookTaskId?: string + videoPath?: string // 视频地址 + audioPath?: string // 音频地址 + word?: string // 文案 + oldImage?: string // 旧图片(用于SD的图生图) + afterGpt?: string // GPT生成的文案 + startTime?: number // 开始时间 + endTime?: number // 结束时间 + timeLimit?: string // 事件实现(0 -- 3000) + subValue?: string // 包含的字幕数据 + characterTags?: string[] // 角色标签 + gptPrompt?: string // GPT提示词 + mjMessage?: MJMessage // MJ消息 + outImagePath?: string // 输出图片地址 + subImagePath?: string[] // 子图片地址 + imageLock?: boolean // 图片锁 + reversePrompt?: ReversePrompt[] // 反推的提示词数据 + prompt?: string // 提示 + adetailer?: boolean // 是否开启修脸 + sdConifg?: SDConfig // SD配置 + subtitlePosition?: string // 字幕位置 + status?: BookTaskStatus + createTime?: Date + updateTime?: Date + } + + + + type QueryBookTaskCondition = { + id?: string + no?: number + name?: string + bookId?: string + bookTaskId?: string + page?: number + pageSize?: number + } + + type QueryBookTaskDetailCondition = { + id?: string + name?: string + bookId?: string + bookTaskId?: string + page?: number + pageSize?: number + } + + type QueryBookBackTaskCondition = { + id: string + bookId: string + bookTaskId: string + name: string + type: string + status: string + executeType: string + count: number + } + + type UpdateBookTaskListStatus = { + id: string + status: BookBackTaskStatus + errorMessage?: string, + startTime?: number + endTime?: number + } + + type BookTask = { + id: string + bookId: string + bookTaskId: string + bookTaskDetailId: string + name: string // 任务名称,小说名+批次名+分镜名 + type: BookBackTaskType + status: BookBackTaskStatus + errorMessage?: string + executeType: TaskExecuteType // 任务执行类型,手动还是自动 + createTime: Date + updateTime: Date + startTime?: number + endTime?: number + } + + // 获取小说文案的参数 + type GetVideoFrameTextParams = { + id: string, + type: SubtitleSavePositionType, + videoPath: string, + subtitlePosition?: string + } + + type BookStyle = { + id?: string + label?: string + key?: string + value?: string + children?: string + type?: string + prompt?: string + image_url?: string + cref_cw?: number + lora?: string + chinese_prompt?: string + lora_weight?: number + show_image?: string + isShow?: boolean + } + + type DefineBookStyle = { + chinese_style?: string + english_style?: string + id?: string + image?: string + } +} diff --git a/src/model/generalResponse.d.ts b/src/model/generalResponse.d.ts new file mode 100644 index 0000000..8e98cac --- /dev/null +++ b/src/model/generalResponse.d.ts @@ -0,0 +1,33 @@ +import { DialogType } from "../define/enum/bookEnum" +import { ResponseMessageType } from "../define/enum/softwareEnum" +import { MJ } from "./mj" +import { TranslateModel } from "./translate" + +declare namespace GeneralResponse { + type SuccessItem = { + code: number + message?: string + data: any + } + + type ErrorItem = { + code: number + message: string + data?: any + } + + type ProgressResponse = { + total: number, // 总数 + current: number, // 当前进度 + } + + // 主线程主动返回前端的消息类 + type MessageResponse = { + code: number, + id: string, // 消息的唯一标识(可以和前端相关联) + type: ResponseMessageType, + dialogType?: DialogType = DialogType.MESSAGE, + message?: string, + data?: MJ.MJResponseToFront | Buffer | string | TranslateModel.TranslateResponseMessageModel | ProgressResponse + } +} diff --git a/src/model/image.d.ts b/src/model/image.d.ts new file mode 100644 index 0000000..e2f90b3 --- /dev/null +++ b/src/model/image.d.ts @@ -0,0 +1,19 @@ + +declare namespace ImageModel { + + type ProcessImageParams = { + imageBase64: string // 需要处理的图片的base64 + maskBase64: string // 处理蒙板的base64 + type: string // 返回数据的类型(是直接写道输出文件地址,还是返回Buffer数组) + inputFilePath: string // 输入文件的地址(待处理的) + maskPath: string // 蒙板文件的地址 + outFilePath: string // 输出文件的地址 + } + + type RemoveWatermarkSetting = { + selectModel: string // 选择的模式 + iopaint: { + url: string // iopaint的地址 + } + } +} \ No newline at end of file diff --git a/src/model/mj.d.ts b/src/model/mj.d.ts new file mode 100644 index 0000000..da8f322 --- /dev/null +++ b/src/model/mj.d.ts @@ -0,0 +1,30 @@ +import { MJRespoonseType } from "../define/enum/mjEnum" + +declare namespace MJ { + + // MJ的API进行反推的参数 + type APIDescribeParams = { + image: string // 图片的地址(可以是网络,也可以是本地) + taskId: string // 任务ID + } + + type MJResponseToFront = { + code: number, // 返回前端的码 0/1 + id: string, // 对应分镜的ID + type: MJRespoonseType, // 返回前端的操作类型 + mjType: MJAction, // 执行MJ的类型 + category: MJImageType, // 调用MJ分类 + message_id?: string, // 返回消息的id,就是任务ID + image_click?: string, // 预览的图片(再使用浏览器模式的时候需要,其他都是null) + image_show?: string, // 实际下载的图片的地址 + image_path?: string, //实际下载的图片的地址 + prompt?: string, // 提示词消息 + progress: number, // 实现的进程 + message?: string // 消息 + status: string + mj_api_url?: string // 请求的MJ地址 + outImagePath?: string // 输出的图片地址 + subImagePath?: string[] // 子图片地址 + } + +} \ No newline at end of file diff --git a/src/model/task.d.ts b/src/model/task.d.ts new file mode 100644 index 0000000..79e151c --- /dev/null +++ b/src/model/task.d.ts @@ -0,0 +1,18 @@ + +declare namespace TaskModal { + type Task = { + id: string + bookId: string + bookTaskId: string + bookTaskDetailId: string + name: string // 任务名称,小说名+批次名+分镜名 + type: BookBackTaskType + status: BookBackTaskStatus + errorMessage: string | null + executeType: TaskExecuteType // 任务执行类型,手动还是自动 + createTime: Date + updateTime: Date + startTime: number + endTime: number + } +} \ No newline at end of file diff --git a/src/model/translate.d.ts b/src/model/translate.d.ts new file mode 100644 index 0000000..df38472 --- /dev/null +++ b/src/model/translate.d.ts @@ -0,0 +1,56 @@ +import { TranslateAPIType, TranslateType } from "../define/enum/translate" + +declare namespace TranslateModel { + type TranslateNowReturnParams = { + text: string // 需要翻译的文本 + from: string // 源语言 + to: string // 目标语言 + isSplit: false // 是否需要分割 + } + + type TranslateNowIPCParams = { + text: string // 需要翻译的文本 + from: string // 源语言 + to: string // 目标语言 + isSplit: false // 是否需要分割 + bookTaskDetailId?: string // 小说分镜ID + reversePromptId?: string // 反推提示词ID + windowId?: number // 窗口ID + type: TranslateType // 翻译类型 + } + + type TranslateResponseMessageModel = { + bookTaskDetailId: string // 分镜ID + reversePromptId: string // 反推提示词ID + from: string // 源语言 + to: string // 目标语言 to 是 zh 值重新保存到 promptCN 中,to 是 en ,需要重新覆盖prompt和prom + prompt: string // 英文数据 + promptCN: string // 中文翻译数据(但是也可以存储英文) + progress: number // 索引(翻译到了哪,用于进度条) + total: number // 总数 + } + + + type TranslateReturnDataModel = { + src: string // 字符串 + dst: string // 翻译结果 + } + + type TranslateReturnModel = { + to: string // 目标语言 + data: TranslateReturnDataModel[] // 翻译结果 + } + + type subTranslateModel = { + name: TranslateAPIType // 翻译API名称 + translation_business: string // 翻译业务 + translation_app_id: string // 翻译APP ID + translation_secret: string // 翻译密钥 + } + + type TranslateModel = { + selectModel: TranslateAPIType // 选择的模型 + translation_auto: boolean // 是否自动翻译 + translates: subTranslateModel[] // 可用的翻译API + } +} \ No newline at end of file diff --git a/src/preload/book.js b/src/preload/book.js index a6f0f77..9aea4a6 100644 --- a/src/preload/book.js +++ b/src/preload/book.js @@ -9,6 +9,10 @@ const book = { AddOrModifyBook: async (book) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_OR_MODIFY_BOOK, book), + // 保存小说任务的风格 + SaveImageStyle: async (styleList, bookTaskId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.SAVE_IMAGE_STYLE, styleList, bookTaskId), + //#region 一键反推 // 获取小说数据(通过传递的参数进行筛选) GetBookData: async (bookQuery) => @@ -18,7 +22,11 @@ const book = { GetBookTaskData: async (bookTaskCondition) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_BOOK_TASK_DATA, bookTaskCondition), - // 获取小说的分镜 + // 获取小说的详细数据 + GetBookTaskDetail: async (bookTaskId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_BOOK_TASK_DETAIL, bookTaskId), + + // 获取小说的分镜数据 GetFrameData: async (bookId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_FRAME_DATA, bookId), @@ -39,7 +47,112 @@ const book = { // 获取当前中的视频所有的字幕 GetVideoFrameText: async (value) => - await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, value) + await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, value), + + //#endregion + + //#region 一键反推的单个任务 + + // 开始计算分镜数据 + ComputeStoryboard: async (bookId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.COMPUTE_STORYBOARD, bookId), + + // 开始执行分镜,切分视频 + Framing: async (bookId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.FRAMING, bookId), + + // 获取文案信息 + GetCopywriting: async (bookId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_COPYWRITING, bookId), + + // 去除所有水印 + RemoveWatermark: async (bookId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_WATERMARK, bookId), + + // 添加单句推理的 + AddReversePrompt: async (bookTaskDetailIds, type) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT, bookTaskDetailIds, type), + + // 将反推的数据指定的位置的提示词写入到GPT提示词中 + ReversePromptToGptPrompt: async (bookId, bookTaskId, index) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.REVERSE_PROMPT_TO_GPT_PROMPT, + bookId, + bookTaskId, + index + ), + + // 单个重选反推的提示词 + SingleReverseToGptPrompt: async (bookTaskDetailId, index) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.SINGLE_REVERSE_TO_GPT_PROMPT, + bookTaskDetailId, + index + ), + + // 删除掉所有的反推数据 + RemoveReverseData: async (bookTaskDetailIds) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_REVERSE_DATA, bookTaskDetailIds), + //#endregion + + // 将小说任务的所有的图片进行锁定和解锁 + ImageLockOperation: async (id, lockType, operateBookType) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.IMAGE_LOCK_OPERATION, + id, + lockType, + operateBookType + ), + + // 下载或者使用指定的图片并裁剪 + DownloadImageUrlAndSplit: async (bookTaskDetailId, imageUrl) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.DOWNLOAD_IMAGE_AND_SPLIT, + bookTaskDetailId, + imageUrl + ), + + // 一拆四,将一个任务拆分成四个任务,并且复制对应的图片 + OneToFourBookTask: async (bookTaskId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK, bookTaskId), + + //#region 小说批次任务相关 + + // 重置小说批次数据 + ReSetBookTask: async (bookTaskId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.RESET_BOOK_TASK, bookTaskId), + + // 删除小说批次数据 + DeleteBookTask: async (bookTaskId) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.DELETE_BOOK_TASK, bookTaskId), + + // 一键生成所有图片 + GenerateImageAll: async (bookTaskId, imageCategory) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.GENERATE_IMAGE_ALL, bookTaskId, imageCategory), + + // 高清图片前检查 + CheckImageFileSize: async (id, fileSize, operateBookType) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE, + id, + fileSize, + operateBookType + ), + + // 高清图片任务 + HDImage: async (id, scale, operateBookType) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.HD_IMAGE, id, scale, operateBookType), + + // 讲小说视频相关的设置添加到小说任务批次 + UseBookVideoDataToBookTask: async (id, operateBookType) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.USE_BOOK_VIDEO_DATA_TO_BOOK_TASK, + id, + operateBookType + ), + + // 添加数据到剪映草稿 + AddJianyingDraft: async (id, operateBookType) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, id, operateBookType) //#endregion } diff --git a/src/preload/db.ts b/src/preload/db.ts new file mode 100644 index 0000000..3b10289 --- /dev/null +++ b/src/preload/db.ts @@ -0,0 +1,20 @@ +import { ipcRenderer } from 'electron' +import { DEFINE_STRING } from '../define/define_string' +import { Book } from '../model/book' + +const db = { + //#region 小说相关的修改 + + // 修改小说人物的数据 + UpdateBookTaskData: async (bookTaskId: string, data: Book.SelectBookTask) => { + return await ipcRenderer.invoke(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DATA, bookTaskId, data) + }, + + // 修改小说详细任务的数据 + UpdateBookTaskDetailData: async (bookTaskDetailId: string, data: Book.SelectBookTaskDetail) => { + return await ipcRenderer.invoke(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DETAIL_DATA, bookTaskDetailId, data) + } + + //endregion +} +export { db } diff --git a/src/preload/discord.js b/src/preload/discord.js index 577b629..b509a4b 100644 --- a/src/preload/discord.js +++ b/src/preload/discord.js @@ -1,5 +1,5 @@ import { ipcRenderer } from 'electron' -import { DEFINE_STRING } from '../define/define_string.js'; +import { DEFINE_STRING } from '../define/define_string'; // Custom APIs for renderer const discord = { // 创建MJ消息 diff --git a/src/preload/index.js b/src/preload/index.js index a3a46f1..d3773a8 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -1,6 +1,6 @@ import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' -import { DEFINE_STRING } from '../define/define_string.js' +import { DEFINE_STRING } from '../define/define_string' import { discord } from './discord.js' import { mj } from './mj.js' import { sd } from './sd.js' @@ -12,6 +12,8 @@ import { book } from './book.js' import { tts } from './tts.js' import { write } from './write.js' import { gpt } from './gpt.js' +import { db } from './db' +import { translate } from './translate.js' // Custom APIs for renderer let events = [] @@ -308,12 +310,6 @@ const api = { // 添加翻译添加翻译任务到队列中 TranslatePrompt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.TRANSLATE_PROMPT, value)), - // 添加事件监听 - setEventListen: (value, callback) => { - ipcRenderer.on(value[0], (event, value) => { - callback(value) - }) - }, // 打开discord窗口 OpenDiscordWindow: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MAIN.OPEN_DISCORD_WINDOW)), @@ -413,6 +409,12 @@ const api = { callback(await ipcRenderer.invoke(DEFINE_STRING.GET_VIDEO_GENERATE_CONFIG)), // 移除事件 removeEventListen: (eventName) => ipcRenderer.removeAllListeners(eventName), + // 添加事件监听 + setEventListen: (value, callback) => { + ipcRenderer.on(value[0], (event, value) => { + callback(value) + }) + }, // 一键自动化的条件检测 AutoConditionCheck: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.AUTO_CONDITION_CHECK, value)), @@ -447,10 +449,7 @@ const api = { // 打开全局通知框 showGlobalNotificationDialog: (value) => - ipcRenderer.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, value), - - // 打开全局的消息框 - showGlobalMessage: (value) => ipcRenderer.send(DEFINE_STRING.SHOW_GLOABAL_MESSAGE, value), + ipcRenderer.send(DEFINE_STRING.SHOW_GLOBAL_MAIN_NOTIFICATION, value), // 知道文件地址,获取文件base64编码 GetFileBase64: async (value, callback) => @@ -479,6 +478,8 @@ if (process.contextIsolated) { contextBridge.exposeInMainWorld('tts', tts) contextBridge.exposeInMainWorld('write', write) contextBridge.exposeInMainWorld('gpt', gpt) + contextBridge.exposeInMainWorld('translate', translate) + contextBridge.exposeInMainWorld('db', db) contextBridge.exposeInMainWorld('darkMode', { toggle: (value) => ipcRenderer.invoke('dark-mode:toggle', value) }) @@ -499,4 +500,6 @@ if (process.contextIsolated) { window.tts = tts window.write = write window.gpt = gpt + window.db = db + window.translate = translate } diff --git a/src/preload/mj.js b/src/preload/mj.js index 17d625f..6f8de10 100644 --- a/src/preload/mj.js +++ b/src/preload/mj.js @@ -1,57 +1,81 @@ -import { ipcRenderer } from "electron"; -import { DEFINE_STRING } from "../define/define_string"; +import { ipcRenderer } from 'electron' +import { DEFINE_STRING } from '../define/define_string' const mj = { - // 保存文案信息到mj的配置文件 - SvaeMJWordSrt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.SAVE_WORD_SRT, value)), - // 获取MJ配置文件的字幕信息 - GetMJConfigSrtInformation: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION)), + // 保存文案信息到mj的配置文件 + SvaeMJWordSrt: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.SAVE_WORD_SRT, value)), + // 获取MJ配置文件的字幕信息 + GetMJConfigSrtInformation: async (callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION)), - // 获取标签集的基础信息 - GetTagDataByTypeAndProperty: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, value)), - // 保存数据到标签集中 - SaveTagPropertyData: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, value)), - // 删除指定的标签数据 - DeleteTagPropertyData: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, value)), - // 获取选择标签的模式option列表 - GetTagSelectModel: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_TAG_SELECT_MODEL)), + // 获取标签集的基础信息 + GetTagDataByTypeAndProperty: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, value)), + // 保存数据到标签集中 + SaveTagPropertyData: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, value)), + // 删除指定的标签数据 + DeleteTagPropertyData: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, value)), + // 获取选择标签的模式option列表 + GetTagSelectModel: async (callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_TAG_SELECT_MODEL)), - // 将翻译任务添加到后台队列中 - TranslateReturnNowTask: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, value)), - // MJ原创生图 - OriginalMJImageGenerate: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, value)), + // 将翻译任务添加到后台队列中 + TranslateReturnNowTask: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, value)), + // MJ原创生图 + OriginalMJImageGenerate: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, value)), - // 获取当前对话频道的机器人列表 - GetChannelRobots: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, value)), + // 获取当前对话频道的机器人列表 + GetChannelRobots: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, value)), - // 获取MJ生图的方式 - GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)), + // 获取MJ生图的方式 + GetMJGenerateCategory: async (callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)), - // MJ生成的图片分割 - ImageSplit: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.IMAGE_SPLIT, value)), + // MJ生成的图片分割 + ImageSplit: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.IMAGE_SPLIT, value)), - // 添加MJ敏感词 - AddMjBadPrompt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, value)), + // 添加MJ敏感词 + AddMjBadPrompt: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, value)), - // 添加MJ敏感词检查 - MJBadPromptCheck: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, value)), + // 添加MJ敏感词检查 + MJBadPromptCheck: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, value)), - // 获取已经生图完成的数据,并获取图片 - GetGeneratedMJImageAndSplit: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, value)), + // 获取已经生图完成的数据,并获取图片 + GetGeneratedMJImageAndSplit: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, value)), - // 给图片链接,下载指定的图片并分割保存 - DownloadImageUrlAndSplit: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, value)), + // 给图片链接,下载指定的图片并分割保存 + DownloadImageUrlAndSplit: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, value)), - // 获取图片的所有的分割尺寸 - GetMJImageScale: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE)), + // 获取图片的所有的分割尺寸 + GetMJImageScale: async (callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE)), - // 获取所有的MJ生图模型 - GetMJImageRobotModel: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL)), + // 获取所有的MJ生图模型 + GetMJImageRobotModel: async (callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL)), - // 自动匹配用户 - AutoMatchUser: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.AUTO_MATCH_USER, value)), + // 自动匹配用户 + AutoMatchUser: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.AUTO_MATCH_USER, value)), + + // 合并提示词 + MergePrompt: async (id, mergeType) => + await ipcRenderer.invoke(DEFINE_STRING.MJ.MJ_MERGE_PROMPT, id, mergeType), + + // 单个出图 + AddMJGenerateImageTask: async (id, operateBookType) => + await ipcRenderer.invoke(DEFINE_STRING.MJ.ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK, id, operateBookType) } -export { - mj -} \ No newline at end of file +export { mj } diff --git a/src/preload/prompt.js b/src/preload/prompt.js index 0837918..8b89b5a 100644 --- a/src/preload/prompt.js +++ b/src/preload/prompt.js @@ -1,20 +1,20 @@ -import { ipcRenderer } from "electron" -import { DEFINE_STRING } from "../define/define_string" - +import { ipcRenderer } from 'electron' +import { DEFINE_STRING } from '../define/define_string' const prompt = { - // 获取所有的排序选项 - GetPromptSortOptions: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.GET_SORT_OPTIONS)), + // 获取所有的排序选项 + GetPromptSortOptions: async (callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.GET_SORT_OPTIONS)), - // 保存提示词排序数据 - SavePromptSort: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.SAVE_PROMPT_SORT_DATA, value)), + // 保存提示词排序数据 + SavePromptSort: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.SAVE_PROMPT_SORT_DATA, value)), - // 获取提示词排序数据 - GetPromptSort: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.GET_PROMPT_SORT_DATA)), + // 获取提示词排序数据 + GetPromptSort: async () => await ipcRenderer.invoke(DEFINE_STRING.PROMPT.GET_PROMPT_SORT_DATA), - // 获取提示词文件数据(txt,指定路径) - OpenPromptFileTxt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, value)), + // 获取提示词文件数据(txt,指定路径) + OpenPromptFileTxt: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, value)) } -export { - prompt -} \ No newline at end of file +export { prompt } diff --git a/src/preload/sd.js b/src/preload/sd.js index 5fd5725..5f5aeba 100644 --- a/src/preload/sd.js +++ b/src/preload/sd.js @@ -1,14 +1,17 @@ -import { ipcRenderer } from "electron" -import { DEFINE_STRING } from "../define/define_string" - +import { ipcRenderer } from 'electron' +import { DEFINE_STRING } from '../define/define_string' const sd = { - // 加载当前链接的SD服务数据 - LoadSDServiceData: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, value)), + // 加载当前链接的SD服务数据 + LoadSDServiceData: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, value)), - // 文生图,单张 - txt2img: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SD.TXT2IMG, value)), + // 文生图,单张 + txt2img: async (value, callback) => + callback(await ipcRenderer.invoke(DEFINE_STRING.SD.TXT2IMG, value)), + + // 合并提示词 + MergePrompt: async (id, mergeType) => + await ipcRenderer.invoke(DEFINE_STRING.SD.SD_MERGE_PROMPT, id, mergeType) } -export { - sd -} \ No newline at end of file +export { sd } diff --git a/src/preload/setting.js b/src/preload/setting.js index 65eff7f..5dedeb4 100644 --- a/src/preload/setting.js +++ b/src/preload/setting.js @@ -59,7 +59,17 @@ const setting = { // 删除指定的MJ账号 DeleteRemoteMJSetting: async (value) => - await ipcRenderer.invoke(DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING, value) + await ipcRenderer.invoke(DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING, value), + + //#endregion + + //#region 去除水印设置 + + GetWaterMarkSetting: async () => + await ipcRenderer.invoke(DEFINE_STRING.SETTING.GET_WATER_MARK_SETTING), + + SaveWaterMarkSetting: async (value) => + await ipcRenderer.invoke(DEFINE_STRING.SETTING.SAVE_WATER_MARK_SETTING, value) //#endregion } diff --git a/src/preload/translate.js b/src/preload/translate.js new file mode 100644 index 0000000..2ff2271 --- /dev/null +++ b/src/preload/translate.js @@ -0,0 +1,30 @@ +import { ipcRenderer } from 'electron' +import { DEFINE_STRING } from '../define/define_string' + +const translate = { + // 打开指定的文件 + OpenFile: async (value, callback) => + callback(ipcRenderer.send(DEFINE_STRING.SYSTEM.OPEN_FILE, value)), + + // 打开指定的文件夹 + OpenFolder: (value) => ipcRenderer.invoke(DEFINE_STRING.OPEN_FOLDER, value), + + // 获取翻译设置 + GetTranslateSetting: () => ipcRenderer.invoke(DEFINE_STRING.TRANSLATE.GET_TRANSLATE_SETTING), + + // 重置翻译设置 + ResetTranslateSetting: () => ipcRenderer.invoke(DEFINE_STRING.TRANSLATE.RESET_TRANSLATE_SETTING), + + // 保存翻译设置 + SaveTranslateSetting: (value) => + ipcRenderer.invoke(DEFINE_STRING.TRANSLATE.SAVE_TRANSLATE_SETTING, value), + + // 直接翻译返回 + TranslateNowReturn: (value) => + ipcRenderer.invoke(DEFINE_STRING.TRANSLATE.TRANSLATE_NOW_RETURN, value) + + // 将翻译加入队列中(这个是加入到内存队列,不是数据库队列,主要是这个不是很耗时) + + // 将翻译加入数据库队列中 +} +export { translate } diff --git a/src/preload/tts.js b/src/preload/tts.js index 9e88d67..b57302b 100644 --- a/src/preload/tts.js +++ b/src/preload/tts.js @@ -6,6 +6,9 @@ const tts = { GetTTSCOnfig: async () => await ipcRenderer.invoke(DEFINE_STRING.TTS.GET_TTS_CONFIG), // 保存TTS配置 - SaveTTSConfig: async (data) => await ipcRenderer.invoke(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, data) + SaveTTSConfig: async (data) => await ipcRenderer.invoke(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, data), + + // 生成音频 + GenerateAudio: async (text) => await ipcRenderer.invoke(DEFINE_STRING.TTS.GENERATE_AUDIO, text) } export { tts } diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index 2d9acdd..ed61494 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -6,7 +6,7 @@ - + diff --git a/src/renderer/src/components/Backstep/BatchSaveImageSetting.vue b/src/renderer/src/components/Backstep/BatchSaveImageSetting.vue index cbc2858..e87a3aa 100644 --- a/src/renderer/src/components/Backstep/BatchSaveImageSetting.vue +++ b/src/renderer/src/components/Backstep/BatchSaveImageSetting.vue @@ -80,7 +80,7 @@ diff --git a/src/renderer/src/components/Book/Components/DatatableAfterGpt.vue b/src/renderer/src/components/Book/Components/DatatableAfterGpt.vue new file mode 100644 index 0000000..140288a --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatableAfterGpt.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/renderer/src/components/Book/Components/DatatableGptPromptButton.vue b/src/renderer/src/components/Book/Components/DatatableGptPromptButton.vue new file mode 100644 index 0000000..86ab1c8 --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatableGptPromptButton.vue @@ -0,0 +1,276 @@ + + + diff --git a/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue b/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue new file mode 100644 index 0000000..18db8e6 --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue @@ -0,0 +1,128 @@ + + + diff --git a/src/renderer/src/components/Book/Components/DatatableHeaderImage.vue b/src/renderer/src/components/Book/Components/DatatableHeaderImage.vue new file mode 100644 index 0000000..867fbf4 --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatableHeaderImage.vue @@ -0,0 +1,144 @@ + + + diff --git a/src/renderer/src/components/Book/Components/DatatableHeaderPrompt.vue b/src/renderer/src/components/Book/Components/DatatableHeaderPrompt.vue new file mode 100644 index 0000000..11f3b7a --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatableHeaderPrompt.vue @@ -0,0 +1,157 @@ + + + diff --git a/src/renderer/src/components/Book/Components/DatatablePrompt.vue b/src/renderer/src/components/Book/Components/DatatablePrompt.vue new file mode 100644 index 0000000..713b8fc --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatablePrompt.vue @@ -0,0 +1,234 @@ + + + diff --git a/src/renderer/src/components/ReverseManage/Components/ManageBookButton.vue b/src/renderer/src/components/Book/Components/ManageBookButton.vue similarity index 98% rename from src/renderer/src/components/ReverseManage/Components/ManageBookButton.vue rename to src/renderer/src/components/Book/Components/ManageBookButton.vue index e8e1621..82b9f83 100644 --- a/src/renderer/src/components/ReverseManage/Components/ManageBookButton.vue +++ b/src/renderer/src/components/Book/Components/ManageBookButton.vue @@ -41,9 +41,9 @@ import { ref, onMounted, defineComponent, onUnmounted, h, toRaw, computed } from 'vue' import { useMessage, NButton, NIcon, useDialog, NPopover, NSwitch, NDropdown } from 'naive-ui' import { AddSharp, ReloadSharp, ResizeSharp, SwapHorizontalSharp } from '@vicons/ionicons5' -import AddBook from './AddBook.vue' +import AddBook from '../Components/AddBook.vue' import { useSoftwareStore } from '../../../../../stores/software' -import { useReverseManageStore } from '../../../../../stores/reverseManage.js' +import { useReverseManageStore } from '../../../../../stores/reverseManage.ts' import { BookType } from '../../../../../define/enum/bookEnum' export default defineComponent({ diff --git a/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue b/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue new file mode 100644 index 0000000..709c4fb --- /dev/null +++ b/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue @@ -0,0 +1,679 @@ + + + + + diff --git a/src/renderer/src/components/Book/Components/ManageBookOldImage.vue b/src/renderer/src/components/Book/Components/ManageBookOldImage.vue new file mode 100644 index 0000000..c16a8a8 --- /dev/null +++ b/src/renderer/src/components/Book/Components/ManageBookOldImage.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/renderer/src/components/ReverseManage/Components/ManageBookShowLogger.vue b/src/renderer/src/components/Book/Components/ManageBookShowLogger.vue similarity index 96% rename from src/renderer/src/components/ReverseManage/Components/ManageBookShowLogger.vue rename to src/renderer/src/components/Book/Components/ManageBookShowLogger.vue index 796d427..8b45599 100644 --- a/src/renderer/src/components/ReverseManage/Components/ManageBookShowLogger.vue +++ b/src/renderer/src/components/Book/Components/ManageBookShowLogger.vue @@ -2,7 +2,7 @@
+ + + + + + + + + + + + + + + + + + + + + + {{ + type == 'bookTsk' ? '应用主小说相关数据' : '当前就是用的主小说的数据' + }} + 保存数据 + +
注意:在生成草稿前要先保存数据
+
+ 注意:在生成草稿前要先保存数据,当前会生成全部的草稿,全部会使用上面的参数 +
+ + 生成草稿 + +
+ + + diff --git a/src/renderer/src/components/Book/Components/Setting.vue b/src/renderer/src/components/Book/Components/Setting.vue new file mode 100644 index 0000000..1f7f8b5 --- /dev/null +++ b/src/renderer/src/components/Book/Components/Setting.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/renderer/src/components/Book/MJReverse/MJReversePrompt.vue b/src/renderer/src/components/Book/MJReverse/MJReversePrompt.vue new file mode 100644 index 0000000..dfd8b9a --- /dev/null +++ b/src/renderer/src/components/Book/MJReverse/MJReversePrompt.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue b/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue new file mode 100644 index 0000000..ec543ed --- /dev/null +++ b/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue @@ -0,0 +1,159 @@ + + + diff --git a/src/renderer/src/components/Book/MJReverse/ReSelectReversePrompt.vue b/src/renderer/src/components/Book/MJReverse/ReSelectReversePrompt.vue new file mode 100644 index 0000000..0686f1e --- /dev/null +++ b/src/renderer/src/components/Book/MJReverse/ReSelectReversePrompt.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/renderer/src/components/Book/MJReverse/SelectMJReversePrompt.vue b/src/renderer/src/components/Book/MJReverse/SelectMJReversePrompt.vue new file mode 100644 index 0000000..eaec5ea --- /dev/null +++ b/src/renderer/src/components/Book/MJReverse/SelectMJReversePrompt.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/renderer/src/components/ReverseManage/ManageBook.vue b/src/renderer/src/components/Book/ManageBook.vue similarity index 91% rename from src/renderer/src/components/ReverseManage/ManageBook.vue rename to src/renderer/src/components/Book/ManageBook.vue index e994600..2b506a5 100644 --- a/src/renderer/src/components/ReverseManage/ManageBook.vue +++ b/src/renderer/src/components/Book/ManageBook.vue @@ -23,10 +23,9 @@ import { ref, onMounted, defineComponent, onUnmounted, toRaw, reactive, computed import { useMessage, NButton, NDataTable, NTag, NEllipsis } from 'naive-ui' import ManageBookButton from './Components/ManageBookButton.vue' import { useSoftwareStore } from '../../../../stores/software' -import { useReverseManageStore } from '../../../../stores/reverseManage.js' +import { useReverseManageStore } from '../../../../stores/reverseManage.ts' import { BookType } from '../../../../define/enum/bookEnum' import BookListAction from './Components/BookListAction.vue' -import { useRouter } from 'vue-router' export default defineComponent({ components: { @@ -41,7 +40,6 @@ export default defineComponent({ let softwareStore = useSoftwareStore() let reverseManageStore = useReverseManageStore() let data = computed(() => reverseManageStore.GetBookData()) - const router = useRouter() let maxHeight = ref(0) // 分页设置 const paginationReactive = (() => { @@ -69,7 +67,6 @@ export default defineComponent({ async function GetBookByCondition() { try { - debugger let res = await reverseManageStore.GetBookDataFromDB({ page: paginationReactive.page, pageSize: paginationReactive.pageSize @@ -84,6 +81,7 @@ export default defineComponent({ } pagination.value.pageCount = res_count + debugger // 开始获取批次任务 let bookTask = await reverseManageStore.GetBookTaskDataFromDB({ bookId: reverseManageStore.selectBook.id @@ -113,6 +111,7 @@ export default defineComponent({ // 监听窗口的大小变化 window.addEventListener('resize', getMaxHeight) }) + // 计算当前程序界面的高度,减去上下的高度,设置maxHegiht function getMaxHeight() { let height = window.innerHeight @@ -193,11 +192,20 @@ export default defineComponent({ rowProps: (row) => { return { style: 'cursor: pointer;', - onClick: () => { - debugger - message.info(row.id) + onClick: async () => { reverseManageStore.selectBook = row - router.push({ name: 'manage_book', params: { id: row.id } }) + softwareStore.spin.spinning = true + softwareStore.spin.tip = '正在加载数据' + + // 加载任务列表 + let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({ + bookId: reverseManageStore.selectBook.id + }) + softwareStore.spin.spinning = false + if (bookTaskRes.code == 0) { + message.error(bookTaskRes.message) + return + } } } } diff --git a/src/renderer/src/components/Book/ManageBookDetail.vue b/src/renderer/src/components/Book/ManageBookDetail.vue new file mode 100644 index 0000000..6ce9d61 --- /dev/null +++ b/src/renderer/src/components/Book/ManageBookDetail.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/src/renderer/src/components/Book/ManageBookTask.vue b/src/renderer/src/components/Book/ManageBookTask.vue new file mode 100644 index 0000000..822516f --- /dev/null +++ b/src/renderer/src/components/Book/ManageBookTask.vue @@ -0,0 +1,225 @@ + + + diff --git a/src/renderer/src/components/ReverseManage/ReverseManage.vue b/src/renderer/src/components/Book/ReverseManage.vue similarity index 92% rename from src/renderer/src/components/ReverseManage/ReverseManage.vue rename to src/renderer/src/components/Book/ReverseManage.vue index cddbfeb..2565ce7 100644 --- a/src/renderer/src/components/ReverseManage/ReverseManage.vue +++ b/src/renderer/src/components/Book/ReverseManage.vue @@ -24,7 +24,7 @@ import { useMessage, NSplit, NSpin, NFloatButton, NIcon, NBadge } from 'naive-ui import ManageBook from './ManageBook.vue' import ManageBookTask from './ManageBookTask.vue' import { useSoftwareStore } from '../../../../stores/software' -import { useReverseManageStore } from '../../../../stores/reverseManage.js' +import { useReverseManageStore } from '../../../../stores/reverseManage.ts' import { Layers } from '@vicons/ionicons5' export default defineComponent({ @@ -38,6 +38,11 @@ export default defineComponent({ onMounted(() => {}) + onUnmounted(() => { + debugger + reverseManageStore.selectBookTaskDetail = undefined + }) + return { isShowTask, softwareStore, diff --git a/src/renderer/src/components/Clip/AddDraft.vue b/src/renderer/src/components/Clip/AddDraft.vue index e9afd8f..9717954 100644 --- a/src/renderer/src/components/Clip/AddDraft.vue +++ b/src/renderer/src/components/Clip/AddDraft.vue @@ -10,7 +10,7 @@ diff --git a/src/renderer/src/components/ReverseManage/ManageBookDetail.vue b/src/renderer/src/components/ReverseManage/ManageBookDetail.vue deleted file mode 100644 index 78c473c..0000000 --- a/src/renderer/src/components/ReverseManage/ManageBookDetail.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/src/renderer/src/components/ReverseManage/ManageBookTask.vue b/src/renderer/src/components/ReverseManage/ManageBookTask.vue deleted file mode 100644 index ece105d..0000000 --- a/src/renderer/src/components/ReverseManage/ManageBookTask.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/src/renderer/src/components/Setting/Components/AddMultiRemoteMj.vue b/src/renderer/src/components/Setting/Components/AddMultiRemoteMj.vue index 5feeb99..b2a5b1e 100644 --- a/src/renderer/src/components/Setting/Components/AddMultiRemoteMj.vue +++ b/src/renderer/src/components/Setting/Components/AddMultiRemoteMj.vue @@ -45,7 +45,6 @@ export default defineComponent({ onMounted(async () => { document.getElementById('AddMultiRemoteMj').style.height = props.height - 60 + 'px' - debugger let remoteMjSettingRes = await window.setting.GetRemoteMJSettings() if (remoteMjSettingRes.code == 0) { message.error('获取代理MJ配置失败') @@ -58,7 +57,6 @@ export default defineComponent({ * 添加和编辑账号 */ async function ManageAccount(remote = null) { - debugger if (remote == null) { settingStore.ResetActionRemoteMJ() } else { diff --git a/src/renderer/src/components/Setting/Setting.vue b/src/renderer/src/components/Setting/Setting.vue index 229cb4b..e4df3f6 100644 --- a/src/renderer/src/components/Setting/Setting.vue +++ b/src/renderer/src/components/Setting/Setting.vue @@ -80,6 +80,20 @@ placeholder="GPT Key" /> + + + + - - - - - - - - - - - - + 翻译设置 h(TranslateSetting), + style: `width : ${dialogWidth}px; height : ${dialogHeight}px`, + maskClosable: false + }) + } + return { formRef, ChangeMode, @@ -476,7 +453,6 @@ export default defineComponent({ handleValidateButtonClick, rules, SwitchTranslate, - translation_options, gpt_model_options, gpt_options, show, @@ -487,7 +463,40 @@ export default defineComponent({ SelectProjectFolder, AddGptPrompt, character_select_model_options, - softwareStore + softwareStore, + ModifyTranslateSetting, + laiApiOptions: [ + { + label: '主站点', + value: LaiAPIType.MAIN + }, + { + label: '香港站点', + value: LaiAPIType.HK_PROXY + }, + { + label: '香港站点', + value: LaiAPIType.HK_PROXY + } + ], + translation_options: [ + { + label: '百度翻译', + value: TranslateAPIType.BAIDU + }, + { + label: '腾讯翻译', + value: TranslateAPIType.TENCENT + }, + { + label: '火山翻译', + value: TranslateAPIType.VOLCENGINE + }, + { + label: '阿里翻译', + value: TranslateAPIType.ALI + } + ] } } }) diff --git a/src/renderer/src/components/ReverseManage/Components/ManageBookReverseTable.vue b/src/renderer/src/components/Setting/SubtitleSetting.vue similarity index 88% rename from src/renderer/src/components/ReverseManage/Components/ManageBookReverseTable.vue rename to src/renderer/src/components/Setting/SubtitleSetting.vue index 61900d3..74ce5ab 100644 --- a/src/renderer/src/components/ReverseManage/Components/ManageBookReverseTable.vue +++ b/src/renderer/src/components/Setting/SubtitleSetting.vue @@ -1,5 +1,5 @@ diff --git a/src/renderer/src/components/Setting/WatermarkSetting.vue b/src/renderer/src/components/Setting/WatermarkSetting.vue new file mode 100644 index 0000000..a8791e4 --- /dev/null +++ b/src/renderer/src/components/Setting/WatermarkSetting.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/src/renderer/src/components/TTS/EdgeTTS.vue b/src/renderer/src/components/TTS/EdgeTTS.vue index 59995a0..56ef43e 100644 --- a/src/renderer/src/components/TTS/EdgeTTS.vue +++ b/src/renderer/src/components/TTS/EdgeTTS.vue @@ -4,29 +4,39 @@ label-placement="left" label-width="auto" require-mark-placement="right-hanging" - :model="edgeTTs" + :model="settingStore.ttsSetting.edgeTTS" > - + - + - + - + - 复选框 + + 复选框 +
diff --git a/src/renderer/src/components/TTS/TTSHome.vue b/src/renderer/src/components/TTS/TTSHome.vue index ecdd2f2..3956bb1 100644 --- a/src/renderer/src/components/TTS/TTSHome.vue +++ b/src/renderer/src/components/TTS/TTSHome.vue @@ -41,20 +41,16 @@ - +
@@ -81,19 +77,14 @@
- +
+ + diff --git a/src/renderer/src/components/Watermark/GetWaterMask.vue b/src/renderer/src/components/Watermark/GetWaterMask.vue index 76040cc..2684d7c 100644 --- a/src/renderer/src/components/Watermark/GetWaterMask.vue +++ b/src/renderer/src/components/Watermark/GetWaterMask.vue @@ -406,7 +406,6 @@ export default defineComponent({ } async function SaveMask() { - // 判断当前是不是又蒙板 if (canvasHistory.length <= 1) { message.error('请先绘制蒙板') diff --git a/src/renderer/src/main.js b/src/renderer/src/main.js index 3a777c0..c14b4e9 100644 --- a/src/renderer/src/main.js +++ b/src/renderer/src/main.js @@ -87,14 +87,14 @@ const routes = [ component: () => import('./components/Setting/MJSetting.vue') }, { - path: '/reverse_management', - name: 'reverse_management', - component: () => import('./components/ReverseManage/ReverseManage.vue') + path: '/book_management', + name: 'book_management', + component: () => import('./components/Book/ReverseManage.vue') }, { path: '/manage_book/:id', name: 'manage_book', - component: () => import('./components/ReverseManage/ManageBookDetail.vue') + component: () => import('./components/Book/ManageBookDetail.vue') }, { path: '/test_options', @@ -102,9 +102,9 @@ const routes = [ component: () => import('./components/VideoSubtitle/VideoCanvas.vue') }, { - path : "/TTS_Services", - name : "TTS_Services", - component : () => import('./components/TTS/TTSHome.vue') + path: '/TTS_Services', + name: 'TTS_Services', + component: () => import('./components/TTS/TTSHome.vue') } ] }, diff --git a/src/stores/image.js b/src/stores/image.ts similarity index 100% rename from src/stores/image.js rename to src/stores/image.ts diff --git a/src/stores/prompt.js b/src/stores/prompt.js deleted file mode 100644 index 156fad7..0000000 --- a/src/stores/prompt.js +++ /dev/null @@ -1,68 +0,0 @@ -import { defineStore } from "pinia"; - -export const usePromptStore = defineStore('prompt', { - state: () => ({ - // 提示词排序 - prompt_sort: [], - selectStyle: [], - suffix: null, // 后缀 - prefix: null, // 前缀 - image_generate_category: null, // 选择生图的分类(sd,mj,d3) - - isInit: false - }), - getters: { - // 获取提示词排序 - GetPromptSort() { - return this.prompt_sort; - }, - // 获取选中的风格 - GetSelectStyle() { - return this.selectStyle - }, - - // 获取前缀 - GetPrefix() { - return this.prefix; - }, - - // 获取后缀 - GetSuffix() { - return this.suffix; - }, - - // 获取生图分类 - GetImageGenerateCategory() { - return this.image_generate_category; - } - }, - actions: { - - // 更新提示词排序 - InitPromptSort(value) { - - this.prompt_sort = value; - }, - - // 更新 - UpdateSelectStyleSort(value) { - this.selectStyle = value; - }, - - // 更新前缀 - UpdatePrefix(value) { - this.prefix = value; - }, - - // 更新后缀 - UpdateSuffix(value) { - this.suffix = value; - }, - - // 更新生图分类 - UpdateImageGenerateCategory(value) { - this.image_generate_category = value; - } - - } -}); \ No newline at end of file diff --git a/src/stores/prompt.ts b/src/stores/prompt.ts new file mode 100644 index 0000000..1459f1e --- /dev/null +++ b/src/stores/prompt.ts @@ -0,0 +1,65 @@ +import { defineStore } from 'pinia' + +export const usePromptStore = defineStore('prompt', { + state: () => ({ + // 提示词排序 + prompt_sort: [], + selectStyle: [], + suffix: null, // 后缀 + prefix: null, // 前缀 + image_generate_category: null, // 选择生图的分类(sd,mj,d3) + + isInit: false + }), + getters: { + // 获取提示词排序 + GetPromptSort() { + return this.prompt_sort + }, + // 获取选中的风格 + GetSelectStyle() { + return this.selectStyle + }, + + // 获取前缀 + GetPrefix() { + return this.prefix + }, + + // 获取后缀 + GetSuffix() { + return this.suffix + }, + + // 获取生图分类 + GetImageGenerateCategory() { + return this.image_generate_category + } + }, + actions: { + // 更新提示词排序 + InitPromptSort(value) { + this.prompt_sort = value + }, + + // 更新 + UpdateSelectStyleSort(value) { + this.selectStyle = value + }, + + // 更新前缀 + UpdatePrefix(value) { + this.prefix = value + }, + + // 更新后缀 + UpdateSuffix(value) { + this.suffix = value + }, + + // 更新生图分类 + UpdateImageGenerateCategory(value) { + this.image_generate_category = value + } + } +}) diff --git a/src/stores/reverseManage.js b/src/stores/reverseManage.ts similarity index 80% rename from src/stores/reverseManage.js rename to src/stores/reverseManage.ts index e75ad31..e57936c 100644 --- a/src/stores/reverseManage.js +++ b/src/stores/reverseManage.ts @@ -1,7 +1,8 @@ import { messageDark, useMessage } from 'naive-ui' import { defineStore } from 'pinia' -import { errorMessage, successMessage } from '../main/generalTools' +import { errorMessage, successMessage } from '../main/Public/generalTools' import { BookTaskStatus } from '../define/enum/bookEnum' +import { Book } from '../model/book' // 系统相关设置 export const useReverseManageStore = defineStore('reverseManage', { @@ -18,9 +19,9 @@ export const useReverseManageStore = defineStore('reverseManage', { audioPath: null, imageFolder: null, subtitlePosition: null - }, // 当前选中的小说 + } as Book.SelectBook, // 当前选中的小说 - bookTaskData: [], // 当前显示的所有小说任务数据 + bookTaskData: [] as Book.SelectBookTask[], // 当前显示的所有小说任务数据 selectBookTask: { no: null, @@ -30,12 +31,18 @@ export const useReverseManageStore = defineStore('reverseManage', { generateVideoPath: null, srtPath: null, audioPath: null, + draftSrtStyle: null, // 草稿字幕样式 + backgroundMusic: null, // 背景音乐ID + friendlyReminder: null, // 友情提示 imageFolder: null, styleList: null, prefix: null, status: BookTaskStatus.WAIT, errorMsg: null - } // 当前选中的小说任务 + } as Book.SelectBookTask// 当前选中的小说任务 + , + selectBookTaskDetail: [] as Book.SelectBookTaskDetail[] // 当前选中的小说任务的详细数据 + }), getters: { // 获取小说数据 @@ -55,6 +62,7 @@ export const useReverseManageStore = defineStore('reverseManage', { async GetBookDataFromDB(condition) { try { debugger + //@ts-ignore let res = await window.book.GetBookData(condition) if (res.code == 0) { throw new Error(res.message) @@ -73,7 +81,7 @@ export const useReverseManageStore = defineStore('reverseManage', { // 获取小说任务数据 async GetBookTaskDataFromDB(condition) { try { - debugger + //@ts-ignore let res = await window.book.GetBookTaskData(condition) if (res.code == 0) { throw new Error(res.message) @@ -82,6 +90,24 @@ export const useReverseManageStore = defineStore('reverseManage', { this.bookTaskData = res.data.bookTasks this.selectBookTask = res.data.bookTasks[0] } else { + this.bookTaskData = [] + this.selectBookTask = { + no: null, + id: null, + bookId: null, + name: null, + generateVideoPath: null, + srtPath: null, + audioPath: null, + draftSrtStyle: null, // 草稿字幕样式 + backgroundMusic: null, // 背景音乐ID + friendlyReminder: null, // 友情提示 + imageFolder: null, + styleList: null, + prefix: null, + status: BookTaskStatus.WAIT, + errorMsg: null + } as Book.SelectBookTask throw new Error('没有找到对应的子批次数据,请先创建') } return successMessage(true) @@ -134,6 +160,7 @@ export const useReverseManageStore = defineStore('reverseManage', { async SetBookType(value = false) { try { if (this.bookType.length <= 0 || value) { + //@ts-ignore let _bookType = await book.GetBookType() if (_bookType.code == 0) { throw new Error(_bookType.message) @@ -164,9 +191,11 @@ export const useReverseManageStore = defineStore('reverseManage', { let save_res = null if (book == null) { // 保存this.selectBook + //@ts-ignore save_res = await window.book.AddOrModifyBook({ ...this.selectBook }) } else { // 保存传入的数据 + //@ts-ignore save_res = await window.book.AddOrModifyBook({ ...book }) } debugger diff --git a/src/stores/setting.js b/src/stores/setting.js deleted file mode 100644 index e6e9126..0000000 --- a/src/stores/setting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { defineStore } from 'pinia' - -// 系统相关设置 -export const useSettingStore = defineStore('setting', { - state: () => ({ - actionRemoteMJ: { - id: null, - accountId: null, - channelId: null, - coreSize: 3, - guildId: null, - enable: true, - mjBotChannelId: null, - nijiBotChannelId: null, - queueSize: 10, - remark: null, - timeoutMinutes: 10, - userAgent: - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', - userToken: null, - createTime: null, - updateTime: null - } - }), - getters: {}, - actions: { - /** - * 重置MJ代理模式的数据 - */ - ResetActionRemoteMJ() { - this.actionRemoteMJ = { - id: null, - accountId: null, - channelId: null, - coreSize: 3, - guildId: null, - enable: true, - mjBotChannelId: null, - nijiBotChannelId: null, - queueSize: 10, - remark: null, - timeoutMinutes: 10, - userAgent: - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', - userToken: null, - createTime: null, - updateTime: null - } - } - } -}) diff --git a/src/stores/setting.ts b/src/stores/setting.ts new file mode 100644 index 0000000..601981b --- /dev/null +++ b/src/stores/setting.ts @@ -0,0 +1,65 @@ +import { defineStore } from 'pinia' + +// 系统相关设置 +export const useSettingStore = defineStore('setting', { + state: () => ({ + actionRemoteMJ: { + id: undefined, + accountId: undefined, + channelId: undefined, + coreSize: 3, + guildId: undefined, + enable: true, + mjBotChannelId: undefined, + nijiBotChannelId: undefined, + queueSize: 10, + remark: undefined, + timeoutMinutes: 10, + userAgent: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', + userToken: undefined, + createTime: undefined, + updateTime: undefined + } as MJSetting.ActionRemoteMJSetting, + // tts 配置 + ttsSetting: { + selectModel: 'edge-tts', + edgeTTS: { + value: 'zh-CN-XiaoxiaoNeural', + gender: 'Female', + label: '晓晓', + lang: 'zh-CN', + saveSubtitles: true, + pitch: 0, // 语调 + rate: 10, // 倍速 + volumn: 0 // 音量 + } + } as TTSSettingModel.TTSSetting + }), + getters: {}, + actions: { + /** + * 重置MJ代理模式的数据 + */ + ResetActionRemoteMJ() { + this.actionRemoteMJ = { + id: undefined, + accountId: undefined, + channelId: undefined, + coreSize: 3, + guildId: undefined, + enable: true, + mjBotChannelId: undefined, + nijiBotChannelId: undefined, + queueSize: 10, + remark: undefined, + timeoutMinutes: 10, + userAgent: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', + userToken: undefined, + createTime: undefined, + updateTime: undefined + } as MJSetting.ActionRemoteMJSetting + } + } +}) diff --git a/src/stores/software.js b/src/stores/software.ts similarity index 97% rename from src/stores/software.js rename to src/stores/software.ts index be7c4ee..73e0900 100644 --- a/src/stores/software.js +++ b/src/stores/software.ts @@ -13,6 +13,7 @@ export const useSoftwareStore = defineStore('software', { reverse_show_book_striped: false, // 是否显示斑马纹(反推界面) reverse_data_table_size: 'small' // 反推界面表格大小 }, + show_logger: false, // 是否显示日志 componentSize: [], // 组件尺寸(通用的选项) SoftColor: null // 按钮颜色 }), diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a109154 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "strict": false, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "target": "es2021", // 你可以设置目标版本 + }, + "include": [ + "src", + "./package.json" +, "src/renderer/src/components/Book/Components/.vue" ] +}