diff --git a/.gitignore b/.gitignore index 210c838..131a398 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ resources/scripts/build* resources/scripts/dist resources/scripts/model resources/scripts/Temp +resources/image/Temp* resources/package/ffmpeg* resources/config* *Lai.exe* diff --git a/electron.vite.config.mjs b/electron.vite.config.mjs index b972453..eff8ee5 100644 --- a/electron.vite.config.mjs +++ b/electron.vite.config.mjs @@ -5,9 +5,7 @@ import Jsx from '@vitejs/plugin-vue-jsx' export default defineConfig({ main: { - plugins: [externalizeDepsPlugin(), bytecodePlugin({ - exclude: ['src/main/discord/discordScript.js'] - })] + plugins: [externalizeDepsPlugin(), bytecodePlugin()] }, discord: { plugins: [externalizeDepsPlugin(), bytecodePlugin()] diff --git a/package-lock.json b/package-lock.json index e87ff94..7c2e53b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "2.2.2", + "version": "2.2.4", "lockfileVersion": 3, "requires": true, "packages": { @@ -21,6 +21,7 @@ "axios": "^1.6.5", "compressing": "^1.10.0", "crypto-js": "^4.2.0", + "electron-store": "^9.0.0", "electron-updater": "^6.1.7", "fluent-ffmpeg": "^2.1.2", "highlight.js": "^11.9.0", @@ -2548,7 +2549,9 @@ }, "node_modules/abbrev": { "version": "1.1.1", - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/acorn": { "version": "8.11.3", @@ -2594,6 +2597,42 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ajv-keywords": { "version": "3.5.2", "dev": true, @@ -2612,6 +2651,7 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2894,6 +2934,15 @@ "node": ">= 4.0.0" } }, + "node_modules/atomically": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/atomically/-/atomically-2.0.3.tgz", + "integrity": "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==", + "dependencies": { + "stubborn-fs": "^1.2.5", + "when-exit": "^2.1.1" + } + }, "node_modules/awesome-js": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/awesome-js/-/awesome-js-2.0.0.tgz", @@ -2914,6 +2963,7 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "devOptional": true, "license": "MIT" }, "node_modules/base64-js": { @@ -3014,6 +3064,7 @@ }, "node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3272,6 +3323,7 @@ }, "node_modules/chalk": { "version": "4.1.2", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -3286,6 +3338,7 @@ }, "node_modules/chownr": { "version": "2.0.0", + "devOptional": true, "license": "ISC", "engines": { "node": ">=10" @@ -3298,6 +3351,7 @@ }, "node_modules/ci-info": { "version": "3.9.0", + "dev": true, "funding": [ { "type": "github", @@ -3455,8 +3509,73 @@ }, "node_modules/concat-map": { "version": "0.0.1", + "devOptional": true, "license": "MIT" }, + "node_modules/conf": { + "version": "12.0.0", + "resolved": "https://registry.npmmirror.com/conf/-/conf-12.0.0.tgz", + "integrity": "sha512-fIWyWUXrJ45cHCIQX+Ck1hrZDIf/9DR0P0Zewn3uNht28hbt5OfGUq8rRWsxi96pZWPyBEd0eY9ama01JTaknA==", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "atomically": "^2.0.2", + "debounce-fn": "^5.1.2", + "dot-prop": "^8.0.2", + "env-paths": "^3.0.0", + "json-schema-typed": "^8.0.1", + "semver": "^7.5.4", + "uint8array-extras": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/conf/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/config-file-ts": { "version": "0.2.6", "dev": true, @@ -3676,6 +3795,20 @@ "version": "1.11.10", "license": "MIT" }, + "node_modules/debounce-fn": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/debounce-fn/-/debounce-fn-5.1.2.tgz", + "integrity": "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.3.4", "license": "MIT", @@ -3887,6 +4020,31 @@ "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", + "integrity": "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ==", + "dependencies": { + "type-fest": "^3.8.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dotenv": { "version": "9.0.2", "dev": true, @@ -4109,6 +4267,32 @@ "node": ">= 10.0.0" } }, + "node_modules/electron-store": { + "version": "9.0.0", + "resolved": "https://registry.npmmirror.com/electron-store/-/electron-store-9.0.0.tgz", + "integrity": "sha512-7LZ2dR3N3bF93G2c4x+1NZ/8fpsKv6bKrMxeOQWLqdRbxvopxVqy9QXQy9axSV2O3P1kVGTj1q2wq5/W4isiOg==", + "dependencies": { + "conf": "^12.0.0", + "type-fest": "^4.18.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-store/node_modules/type-fest": { + "version": "4.18.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.18.2.tgz", + "integrity": "sha512-+suCYpfJLAe4OXS6+PPXjW3urOS4IoP9waSiLuXfLgqZODKw/aWwASvzqE886wA0kQgGy0mIWyhd87VpqIy6Xg==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.628", "license": "ISC" @@ -4615,7 +4799,6 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "dev": true, "license": "MIT" }, "node_modules/fast-diff": { @@ -4810,6 +4993,7 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", + "devOptional": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -4820,6 +5004,7 @@ }, "node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", + "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -4830,10 +5015,12 @@ }, "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": { @@ -4926,6 +5113,7 @@ }, "node_modules/glob": { "version": "7.2.3", + "devOptional": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -4955,6 +5143,7 @@ }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", + "devOptional": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -4963,6 +5152,7 @@ }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", + "devOptional": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -5091,6 +5281,7 @@ }, "node_modules/has-flag": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5155,6 +5346,7 @@ }, "node_modules/hosted-git-info": { "version": "4.1.0", + "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" @@ -5165,6 +5357,7 @@ }, "node_modules/hosted-git-info/node_modules/lru-cache": { "version": "6.0.0", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -5175,6 +5368,7 @@ }, "node_modules/hosted-git-info/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/html-encoding-sniffer": { @@ -5334,6 +5528,7 @@ }, "node_modules/inflight": { "version": "1.0.6", + "devOptional": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -5680,6 +5875,11 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/json-schema-typed/-/json-schema-typed-8.0.1.tgz", + "integrity": "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true, @@ -6017,6 +6217,17 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "1.0.1", "license": "MIT", @@ -6032,6 +6243,7 @@ }, "node_modules/minimatch": { "version": "5.1.6", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -6049,6 +6261,7 @@ }, "node_modules/minipass": { "version": "5.0.0", + "devOptional": true, "license": "ISC", "engines": { "node": ">=8" @@ -6056,6 +6269,7 @@ }, "node_modules/minizlib": { "version": "2.1.2", + "devOptional": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -6067,6 +6281,7 @@ }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", + "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -6077,10 +6292,12 @@ }, "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" @@ -6219,6 +6436,8 @@ "node_modules/nopt": { "version": "5.0.0", "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "abbrev": "1" }, @@ -8839,6 +9058,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9198,6 +9418,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "license": "MIT" @@ -9736,6 +9964,11 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/stubborn-fs": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/stubborn-fs/-/stubborn-fs-1.2.5.tgz", + "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" + }, "node_modules/sumchecker": { "version": "3.0.1", "license": "Apache-2.0", @@ -9748,6 +9981,7 @@ }, "node_modules/supports-color": { "version": "7.2.0", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -9780,6 +10014,7 @@ }, "node_modules/tar": { "version": "6.2.0", + "devOptional": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -9835,6 +10070,7 @@ }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", + "devOptional": true, "license": "ISC" }, "node_modules/temp-file": { @@ -9922,6 +10158,7 @@ }, "node_modules/text-table": { "version": "0.2.0", + "dev": true, "license": "MIT" }, "node_modules/timm": { @@ -10053,6 +10290,17 @@ "node": ">=14.17" } }, + "node_modules/uint8array-extras": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-0.3.0.tgz", + "integrity": "sha512-erJsJwQ0tKdwuqI0359U8ijkFmfiTcq25JvvzRVc1VP+2son1NJRXhxcAKJmAW3ajM8JSGAfsAXye8g4s+znxA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici-types": { "version": "5.26.5", "license": "MIT" @@ -10094,7 +10342,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -10368,8 +10615,14 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/when-exit": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/when-exit/-/when-exit-2.1.2.tgz", + "integrity": "sha512-u9J+toaf3CCxCAzM/484qNAxQE75rFdVgiFEEV8Xps2gzYhf0tx73s1WXDQhkwV17E3MxRMz40m7Ekd2/121Lg==" + }, "node_modules/which": { "version": "2.0.2", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" diff --git a/package.json b/package.json index 1f34f1a..701a4b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "2.2.2", + "version": "2.2.4", "description": "An Electron application with Vue", "main": "./out/main/index.js", "author": "example.com", @@ -29,6 +29,7 @@ "axios": "^1.6.5", "compressing": "^1.10.0", "crypto-js": "^4.2.0", + "electron-store": "^9.0.0", "electron-updater": "^6.1.7", "fluent-ffmpeg": "^2.1.2", "highlight.js": "^11.9.0", @@ -72,7 +73,8 @@ ], "extraResources": [ "resources/package/**", - "resources/image/**", + "resources/image/style/**", + "resources/image/zhanwei.png", "resources/scripts/model/**", "resources/scripts/Lai.exe", "resources/scripts/discordScript.js", diff --git a/resources/image/c_s/36069560-f403-47a4-923a-db1269e91d40.png b/resources/image/c_s/36069560-f403-47a4-923a-db1269e91d40.png new file mode 100644 index 0000000..6446f84 Binary files /dev/null and b/resources/image/c_s/36069560-f403-47a4-923a-db1269e91d40.png differ diff --git a/resources/image/c_s/4e634611-e833-47b0-8aa6-a836933dc851.png b/resources/image/c_s/4e634611-e833-47b0-8aa6-a836933dc851.png new file mode 100644 index 0000000..8975d5c Binary files /dev/null and b/resources/image/c_s/4e634611-e833-47b0-8aa6-a836933dc851.png differ diff --git a/resources/image/c_s/67a962df-12e3-4188-a201-e2643590a479.png b/resources/image/c_s/67a962df-12e3-4188-a201-e2643590a479.png new file mode 100644 index 0000000..5bd644f Binary files /dev/null and b/resources/image/c_s/67a962df-12e3-4188-a201-e2643590a479.png differ diff --git a/resources/image/c_s/c8fc2c21-54ea-4d07-b7f8-af64744aba70.png b/resources/image/c_s/c8fc2c21-54ea-4d07-b7f8-af64744aba70.png new file mode 100644 index 0000000..1a8c41e Binary files /dev/null and b/resources/image/c_s/c8fc2c21-54ea-4d07-b7f8-af64744aba70.png differ diff --git a/resources/image/c_s/e8503820-e13c-430a-bb22-0ab0c0468eec.png b/resources/image/c_s/e8503820-e13c-430a-bb22-0ab0c0468eec.png new file mode 100644 index 0000000..3aec420 Binary files /dev/null and b/resources/image/c_s/e8503820-e13c-430a-bb22-0ab0c0468eec.png differ diff --git a/resources/scripts/Lai.py b/resources/scripts/Lai.py index 7fdd336..56b5add 100644 --- a/resources/scripts/Lai.py +++ b/resources/scripts/Lai.py @@ -11,7 +11,7 @@ import shotSplit sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") -# sys.argv = ["C:\\Users\\27698\\Desktop\\LAITool\\resources\\scripts\\Lai.exe","-c","D:/父爱如山倒/scripts/output_crop_00001.json"] +# sys.argv = ["C:\\Users\\27698\\Desktop\\LAITool\\resources\\scripts\\Lai.exe","-c","C:/Users/27698/Desktop/测试/123/scripts/output_crop_00001.json"] print(sys.argv) if len(sys.argv) < 2: diff --git a/resources/scripts/__pycache__/getgrame.cpython-310.pyc b/resources/scripts/__pycache__/getgrame.cpython-310.pyc index e0d6613..3294654 100644 Binary files a/resources/scripts/__pycache__/getgrame.cpython-310.pyc and b/resources/scripts/__pycache__/getgrame.cpython-310.pyc differ diff --git a/resources/scripts/__pycache__/iamge_to_video.cpython-310.pyc b/resources/scripts/__pycache__/iamge_to_video.cpython-310.pyc index 32344f7..eb2b492 100644 Binary files a/resources/scripts/__pycache__/iamge_to_video.cpython-310.pyc and b/resources/scripts/__pycache__/iamge_to_video.cpython-310.pyc differ diff --git a/resources/scripts/__pycache__/public_tools.cpython-310.pyc b/resources/scripts/__pycache__/public_tools.cpython-310.pyc index 0b8a975..5938e07 100644 Binary files a/resources/scripts/__pycache__/public_tools.cpython-310.pyc and b/resources/scripts/__pycache__/public_tools.cpython-310.pyc differ diff --git a/resources/scripts/iamge_to_video.py b/resources/scripts/iamge_to_video.py index 9c7f8f0..b0f059a 100644 --- a/resources/scripts/iamge_to_video.py +++ b/resources/scripts/iamge_to_video.py @@ -376,11 +376,19 @@ class ImageToVideo: proportion_height = img_height / 3200 proportion_width = img_height / 3200 - if offset["name"] == "KFTypePositionY": + key_frame = offset["name"] + offset_list = ["KFTypePositionY", "KFTypePositionX"] + real_key_frame = key_frame + + if key_frame == "KFTypeRandom": + # 随机获取 offset_list 中的一个数据 + real_key_frame = offset_list[np.random.randint(0, 2)] + + if real_key_frame == "KFTypePositionY": offsetValue = offset["up_down"] * proportion_height - elif offset["name"] == "KFTypePositionX": + elif real_key_frame == "KFTypePositionX": offsetValue = offset["left_right"] * proportion_width - elif offset["name"] == "KFTypeScale": + elif real_key_frame == "KFTypeScale": offsetValue = offset["scale"] else: return ValueError("关键帧没有设置正确的参数") @@ -402,7 +410,7 @@ class ImageToVideo: end_offset, self.fps, self.video_size, - offset["name"], + real_key_frame, ) video_arr.append(video_path) print(video_path) @@ -418,7 +426,7 @@ class ImageToVideo: print(number) filtered_data = [item for item in config_data if item["no"] == number] - + # print(filtered_data) cmd = [ self.ffprobe_path, diff --git a/resources/scripts/install.bat b/resources/scripts/install.bat new file mode 100644 index 0000000..799d252 --- /dev/null +++ b/resources/scripts/install.bat @@ -0,0 +1,2 @@ +@echo off +pyinstaller Lai.spec \ No newline at end of file diff --git a/resources/tmp/Clip/keyframe_tmp.json b/resources/tmp/Clip/keyframe_tmp.json index 0e0c8c6..a1d7f3f 100644 --- a/resources/tmp/Clip/keyframe_tmp.json +++ b/resources/tmp/Clip/keyframe_tmp.json @@ -4,7 +4,6 @@ { "curveType": "Line", "graphID": "", - "id": "389CE3AB-19DF-4EC8-B712-454C224D0D92", "left_control": { "x": 0.0, "y": 0.0 @@ -21,7 +20,6 @@ { "curveType": "Line", "graphID": "", - "id": "3F5A033A-9088-4AED-BBF6-50DEBE67BCA5", "left_control": { "x": 0.0, "y": 0.0 diff --git a/src/api/apiBasic.js b/src/api/apiBasic.js new file mode 100644 index 0000000..d393ec9 --- /dev/null +++ b/src/api/apiBasic.js @@ -0,0 +1,116 @@ + +const { net } = require('electron'); + +let basicApi = { + /** + * 使用electron的net模块实现的get方法 + * @param {*} url 请求的url + * @param {*} headers 请求头 + * @returns + */ + get: (url, headers = {}) => { + return new Promise((resolve, reject) => { + const request = net.request({ url, method: 'GET', headers }); + request.on('response', (response) => { + let data = ''; + response.on('data', (chunk) => { + data += chunk; + }); + response.on('end', () => { + // 结束的时候检查请求的状态码,是不是成功的请求,不是返回错误,有些其他的状态码也是成功的请求,并且返回错误提示 + if (response.statusCode != 200) { + reject(new Error(`请求失败,状态码:${response.statusCode},错误信息:${response.statusMessage}`)); + return; + } + + let parsedData; + if (response.headers['content-type'].includes('application/json')) { + parsedData = JSON.parse(data); + } else { + parsedData = data; + } + + resolve({ + data: parsedData, + status: response.statusCode, + statusText: response.statusMessage, + headers: response.headers, + }); + }); + response.on('error', (error) => { + reject(error); + }); + }); + request.on('error', (error) => { + console.log('request error', error); + reject(error); + }); + request.end(); + }); + }, + /** + * 使用electron的net模块实现的post方法 + * @param {*} url 请求的url + * @param {*} data 传输的数据(json格式) + * @param {*} headers 请求头 + * @returns + */ + post: (url, data = {}, headers = {}) => { + return new Promise((resolve, reject) => { + const request = net.request({ + method: 'POST', + url: url, + headers: { + 'Content-Type': 'application/json', + ...headers + } + }); + + request.write(JSON.stringify(data)); + + request.on('response', (response) => { + let responseData = ''; + + response.on('data', (chunk) => { + responseData += chunk; + }); + + response.on('end', () => { + if (response.statusCode != 200) { + reject(new Error(`请求失败,状态码:${response.statusCode},错误信息:${response.statusMessage}`)); + return; + } + + let parsedData; + if (response.headers['content-type'].includes('application/json')) { + parsedData = JSON.parse(responseData); + } else { + parsedData = responseData; + } + + resolve({ + data: parsedData, + status: response.statusCode, + statusText: response.statusMessage, + headers: response.headers + }); + }); + + response.on('error', (error) => { + console.log('error', error); + reject(error); + }); + }); + request.on('error', (error) => { + console.log('request error', error); + reject(error); + }); + request.end(); + + }); + } +} + +export { + basicApi +} \ No newline at end of file diff --git a/src/api/sdApi.js b/src/api/sdApi.js new file mode 100644 index 0000000..af6c5f2 --- /dev/null +++ b/src/api/sdApi.js @@ -0,0 +1,90 @@ +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; + } + + /** + * 获取当前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); + } + + /** + * 获取当前连接的所有的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; + } + } + + 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; + } + } +} diff --git a/src/define/define.js b/src/define/define.js index 8a2399b..7e971fc 100644 --- a/src/define/define.js +++ b/src/define/define.js @@ -15,6 +15,7 @@ if (!app.isPackaged) { scripts_path: path.join(__dirname, "../../resources/scripts"), package_path: path.join(__dirname, "../../resources/package"), image_path: path.join(__dirname, "../../resources/image"), + temp_sd_image: path.join(__dirname, "../../resources/image/TempSDImage"), draft_temp_path: path.join(__dirname, "../../resources/tmp/temp.zip"), clip_speed_temp_path: path.join(__dirname, "../../resources/tmp/Clip/speeds_tmp.json"), add_canvases_temp_path: path.join(__dirname, "../../resources/tmp/Clip/canvases_tmp.json"), @@ -45,6 +46,7 @@ if (!app.isPackaged) { package_path: path.join(__dirname, "../../../resources/package"), discordScript: path.join(__dirname, "../../../resources/scripts/discordScript.js"), image_path: path.join(__dirname, "../../../resources/image"), + temp_sd_image: path.join(__dirname, "../../../resources/image/TempSDImage"), draft_temp_path: path.join(__dirname, "../../../resources/tmp/temp.zip"), clip_speed_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/speeds_tmp.json"), add_canvases_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/canvases_tmp.json"), diff --git a/src/define/define_string.js b/src/define/define_string.js index 22d7d61..ea429c0 100644 --- a/src/define/define_string.js +++ b/src/define/define_string.js @@ -1,4 +1,5 @@ export const DEFINE_STRING = { + GET_FILE_BASE64: "GET_FILE_BASE64", SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY: "SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY", GET_DEFINE_CONFIG_JSON_BY_PROPERTY: "GET_DEFINE_CONFIG_JSON_BY_PROPERTY", GET_IMAGE_GENERATE_CATEGORY: "GET_IMAGE_GENERATE_CATEGORY", @@ -134,6 +135,10 @@ export const DEFINE_STRING = { NORMAL_PERMISSION: "NORMAL_PERMISSION", AUTO_SAVE_IMAGE_PERMISSION: "AUTO_SAVE_IMAGE_PERMISSION", }, + SD: { + LOAD_SD_SERVICE_DATA: "LOAD_SD_SERVICE_DATA", + TXT2IMG: "TXT2IMG", + }, MJ: { SAVE_WORD_SRT: "SAVE_WORD_SRT", GET_MJ_CONFIG_SRT_INFORMATION: "GET_MJ_CONFIG_SRT_INFORMATION", diff --git a/src/define/setting/clipSetting.js b/src/define/setting/clipSetting.js index eb99d2b..1602b5b 100644 --- a/src/define/setting/clipSetting.js +++ b/src/define/setting/clipSetting.js @@ -39,6 +39,9 @@ export const ClipSetting = { }, { label: "缩放", value: "KFTypeScale" + }, { + label: "随机", + value: "KFTypeRandom" }] } }, diff --git a/src/define/setting/sdSettingDefine.js b/src/define/setting/sdSettingDefine.js index a63517c..9508e40 100644 --- a/src/define/setting/sdSettingDefine.js +++ b/src/define/setting/sdSettingDefine.js @@ -1,6 +1,9 @@ import { get } from "lodash"; import { define } from "../define"; let fspromises = require("fs").promises; +import { Tools } from "../../main/tools"; +import { errorMessage } from "../../main/generalTools"; +let tools = new Tools(); // Create a shared object export const SdSettingDefine = { @@ -42,6 +45,26 @@ export const SdSettingDefine = { return get(setting, "webui", null); } else return get(setting.webui, property, null); + }, + + + /** + * 保存一个大的属性添加到配置文件中 + * @param {*} property + * @param {*} value + */ + SavePropertyValue: async function (property, value) { + try { + global.fileQueue.enqueue(async () => { + // 复制文件到指定的文件夹 + await tools.writeJsonFilePropertyValue(define.sd_setting, property, value); + + }) + } catch (error) { + throw error; + } } + + }; diff --git a/src/define/tagDefine.js b/src/define/tagDefine.js index 9bd7057..849b830 100644 --- a/src/define/tagDefine.js +++ b/src/define/tagDefine.js @@ -2,11 +2,14 @@ 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(); } /** @@ -54,6 +57,30 @@ export class TagDefine { 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 @@ -75,6 +102,22 @@ export class TagDefine { 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, []); @@ -96,7 +139,7 @@ export class TagDefine { if (tag.some(item => item.label == value.label)) { throw new Error("已存在相同名称的数据,请修改名称后再保存"); } - value.key = uuidv4(); + value.key = tmp_key; value.value = value.key; tag.push(value); } @@ -107,7 +150,6 @@ export class TagDefine { code: 1, message: "保存成功" } - } catch (error) { return { code: 0, diff --git a/src/main/IPCEvent/globalIpc.js b/src/main/IPCEvent/globalIpc.js new file mode 100644 index 0000000..d34b09e --- /dev/null +++ b/src/main/IPCEvent/globalIpc.js @@ -0,0 +1,34 @@ +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(); + +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) + } + }); +} + +export { + GlobalIpc +} \ No newline at end of file diff --git a/src/main/IPCEvent/sdIpc.js b/src/main/IPCEvent/sdIpc.js index 67fb65e..81e41e3 100644 --- a/src/main/IPCEvent/sdIpc.js +++ b/src/main/IPCEvent/sdIpc.js @@ -12,6 +12,12 @@ function SdIpc() { // 获取图片样式菜单 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)); + + // 文生图,单张 + ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value)); } export { SdIpc diff --git a/src/main/Original/MJOriginalImageGenerate.js b/src/main/Original/MJOriginalImageGenerate.js index 8eecd88..4551f70 100644 --- a/src/main/Original/MJOriginalImageGenerate.js +++ b/src/main/Original/MJOriginalImageGenerate.js @@ -9,6 +9,7 @@ import path from 'path' import sharp from 'sharp' import { define } from "../../define/define"; import { AwesomeRegx } from "awesome-js"; +import { checkStringValueAddSuffix } from "../generalTools"; /** * MJ原创生图的类 @@ -196,13 +197,15 @@ export class MJOriginalImageGenerate { // 判断存放的文件夹是不是存在,不存在的话创建 let outputDir = path.join(this.global.config.project_path, `data\\MJOriginalImage`); - await this.tools.checkFolderExistsOrCreate(outputDir); - let fileExist = await this.tools.checkExists(outputDir); if (!fileExist) { await this.tools.createDirectory(outputDir); } + // 判断该当前tmp\output_crop_00001文件夹是不是存在,不存在创建 + let output_crop_00001 = path.join(this.global.config.project_path, `tmp\\output_crop_00001`); + await this.tools.checkFolderExistsOrCreate(output_crop_00001); + // 检查this.global中是不是又mj队列,没有的话创建一个 if (!this.global.mjGenerateQuene) { @@ -210,7 +213,7 @@ export class MJOriginalImageGenerate { } let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false); - // let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data); + let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data); // 替换风格的逻辑 let current_task = null; @@ -219,11 +222,17 @@ export class MJOriginalImageGenerate { const element = data[i]; let tasK_id = `${batch}_${element.name}_${element.id}`; + let old_prompt = element.prompt; + // 拼接提示词 + // 图生图的链接 + // 获取风格词 + let prompt = " " + image_styles + old_prompt; + + this.global.mjGenerateQuene.enqueue(async () => { try { this.global.mjGenerateQuene.setCurrentCreateItem(element) // 开始进行mj生图 - let prompt = element.prompt; current_task = element.name; // 判断窗口是不是开启 diff --git a/src/main/Original/OriginalImageGenerate.js b/src/main/Original/OriginalImageGenerate.js index 15f0388..2db1091 100644 --- a/src/main/Original/OriginalImageGenerate.js +++ b/src/main/Original/OriginalImageGenerate.js @@ -12,7 +12,6 @@ const { spawn, exec } = require('child_process'); const execAsync = util.promisify(exec); const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符 let fspromises = require("fs").promises; -import { MD5 } from "crypto-js"; import { ImageStyleDefine } from "../../define/iamgeStyleDefine"; @@ -64,26 +63,21 @@ export class OriginalImageGenerate { let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false); let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data); - // - console.log(image_styles); - 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 = image_styles + 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; - - + let prompt = sd_setting.webui.prompt + image_styles + ',' + 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 { // 开始请求 @@ -91,7 +85,7 @@ export class OriginalImageGenerate { "prompt": prompt, "negative_prompt": imageJson.webui_config.negative_prompt, "seed": seed, - "sampler_name": imageJson.webui_config.sampler_name, + "sampler_name": sd_setting.webui.sampler_name, // 提示词相关性 "cfg_scale": sd_setting.webui.cfg_scale, "width": sd_setting.webui.width, @@ -258,7 +252,15 @@ export class OriginalImageGenerate { if (element.outImagePath && file_regex.test(element.outImagePath)) { // 删除 "file://" 开头 element.outImagePath = decodeURI(element.outImagePath); - element.outImagePath = element.outImagePath.replace(/^file:\/\//, '').replace(/\?time=.*$/, ''); + // 判断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++) { @@ -308,9 +310,7 @@ export class OriginalImageGenerate { 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"; diff --git a/src/main/Public/SD.js b/src/main/Public/SD.js index 995ddd3..ed6414f 100644 --- a/src/main/Public/SD.js +++ b/src/main/Public/SD.js @@ -6,19 +6,112 @@ import { ImageStyleDefine } from "../../define/iamgeStyleDefine"; import { cloneDeep } from 'lodash'; let fspromises = require("fs").promises; const sharp = require('sharp'); -// const { -// createCanvas, -// loadImage -// } = require('canvas'); import { SdSettingDefine } from "../../define/setting/sdSettingDefine"; import { PublicMethod } from "./publicMethod"; import { Tools } from "../tools"; +import { errorMessage, successMessage } from "../generalTools"; +import { SdApi } from "../../api/sdApi"; +const { v4: uuidv4 } = require('uuid'); export class SD { constructor(global) { this.global = global; this.pm = new PublicMethod(global); this.tools = new Tools(); + this.sdApi = new SdApi(); + } + + /** + * 获取当前SD服务器所有的lora信息 + */ + async GetAllLoras(baseURL = null) { + try { + let data = await this.sdApi.getAllLoras(baseURL); + return successMessage(data); + } catch (error) { + return errorMessage(error.toString()); + } + } + + /** + * 获取所有的checkpoint模型 + * @param {*} baseURL + * @returns + */ + async GetAllSDModel(baseURL = null) { + try { + let data = await this.sdApi.getAllSDModel(baseURL); + return successMessage(data); + } catch (error) { + return errorMessage(error.toString()); + } + } + + /** + * 获取所有的采样器 + * @param {*} baseURL + * @returns + */ + async GetAllSamplers(baseURL = null) { + try { + let data = await this.sdApi.getAllSamplers(baseURL); + return successMessage(data); + } catch (error) { + return errorMessage(error.toString()); + } + } + + /** + * 加载所有的SD数据 + * @param {*} baseURL + * @returns + */ + async LoadSDServiceData(baseURL = null) { + try { + // 加载大模型 + let sd_model = await this.GetAllSDModel(baseURL); + // 往sd_model中添加一个默认的选项 + sd_model.data.data.unshift({ + title: "无", + name: "无", + description: "无", + }) + // 加载Lora + let lora = await this.GetAllLoras(baseURL); + lora.data.data.unshift({ + Key: "无", + name: "无", + description: "无", + }) + // 加载采样器 + let sampler = await this.GetAllSamplers(baseURL); + sampler.data.data.unshift({ + name: "无", + description: "无", + }) + + if (!(sd_model.code & lora.code & sampler.code)) { + throw new Error("获取SD数据错误,请检查SD WEBUI链接!"); + } + + for (let i = 0; i < lora.data.data.length; i++) { + delete lora.data.data[i].metadata; + } + let data = { + sd_model: sd_model.data.data, + lora: lora.data.data, + sampler: sampler.data.data + } + // 处理当前获取的数据,保存到配置文件中 + await SdSettingDefine.SavePropertyValue("sd_model", data.sd_model); + await SdSettingDefine.SavePropertyValue("lora", data.lora); + await SdSettingDefine.SavePropertyValue("sampler", data.sampler); + + return successMessage(data); + + } catch (error) { + return errorMessage(error.toString()); + } } /** @@ -34,7 +127,10 @@ export class SD { data: style } } catch (error) { - + return { + code: 0, + message: "不可能出现错误" + } } } @@ -106,6 +202,37 @@ export class SD { } } + /** + * 单张生图 + * @param {*} value 0 生图的参数,1 图片的表示,用于保存 ,2 baseUrl + * @returns + */ + async txt2img(value) { + try { + value = JSON.parse(value); + let data = value[0]; + let res = await this.sdApi.txt2img(data); + // 将base· 64的图片转换为图片 + // 将当前的图片保存到指定的文件夹中,然后返回文件路径,并且可以复制到指定的文件,删除exif信息 + let image_paths = []; + for (let i = 0; res.data.images && i < res.data.images.length; i++) { + const element = res.data.images[i]; + let image_data = { + base64: element + } + // 将保存图片添加到队列中 + let image_name = `sd_${Date.now()}_${uuidv4()}.png`; + let image_path = path.join(define.temp_sd_image, image_name); + image_path = await this.tools.saveBase64ToImage(element, image_path); + image_data["image_path"] = image_path; + image_paths.push(image_data); + } + return successMessage(image_paths); + } catch (error) { + return errorMessage("生图失败,错误信息如下:" + error.toString()); + } + } + /** * 生成一次图片的方法。可以区分模式 * @param {图片名称 } image @@ -120,7 +247,6 @@ export class SD { let image_json = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8')); let image_path = ""; let target_image_path = ""; - if (image_json.name) { image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/tmp_${image_json.name}`) target_image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/${image_json.name}`) @@ -128,25 +254,20 @@ export class SD { image_path = image.replaceAll("input_crop", task_list.out_folder).split(".png")[0] + "_tmp.png"; target_image_path = image.replaceAll("input_crop", task_list.out_folder); } - - // let prompt = ""; - // // 拼接提示词 - // if (task_list.image_style != null) { - // prompt += `((${task_list.image_style})),`; - // } - // if (task_list.lora != null) { - // prompt += `${task_list.lora},`; - // } - // let image_styles = await ImageStyleDefine.getImageStyleStringByIds(task_list.image_style_list ? task_list.image_style_list : []); - - // prompt = `${prompt}, ${image_styles}, ${imageJson.webui_config.prompt}`; - let prompt = imageJson.webui_config.prompt; - + let image_styles = await ImageStyleDefine.getImageStyleStringByIds(task_list.image_style_list ? task_list.image_style_list : []); + let prompt = sd_setting.webui.prompt + image_styles; + // 拼接提示词 + if (task_list.image_style != null) { + prompt += `((${task_list.image_style})), `; + } + if (task_list.lora != null) { + prompt += `${task_list.lora}, `; + } + prompt += imageJson.webui_config.prompt; // 判断当前是不是有开修脸修手 let ADetailer = { args: sd_setting.adetailer }; - if (model == "img2img") { let web_api = this.global.config.webui_api_url + 'sdapi/v1/img2img' let sd_config = imageJson["webui_config"]; @@ -154,7 +275,6 @@ export class SD { sd_config.seed = seed; let im = await fspromises.readFile(image, 'binary'); sd_config.init_images = [new Buffer.from(im, 'binary').toString('base64')]; - if (imageJson.adetailer) { let ta = { ADetailer: ADetailer @@ -163,13 +283,11 @@ export class SD { } sd_config.height = sd_setting.webui.height; sd_config.width = sd_setting.webui.width; - const response = await axios.post(web_api, sd_config); let info = JSON.parse(response.data.info); if (seed == -1) { seed = info.seed; } - // 目前是单图出图 let images = response.data.images; let imageData = Buffer.from(images[0].split(",", 1)[0], 'base64'); @@ -253,22 +371,4 @@ export class SD { throw error; } } - - /** - *文生图 - * @param {SD 请求的地址} url - * @param {SD请求的body} body - */ - async txt2img(url, body) { - - } - - /** - *图生图 - * @param {SD 请求的地址} url - * @param {SD请求的body} body - */ - async img2img(url, body) { - - } } \ No newline at end of file diff --git a/src/main/Public/clipDraft.js b/src/main/Public/clipDraft.js index 53bdee6..8a0b543 100644 --- a/src/main/Public/clipDraft.js +++ b/src/main/Public/clipDraft.js @@ -662,12 +662,12 @@ export class ClipDraft { * 通过 key_frame 返回关键帧数据 * @param {*} key_frame 关键帧配置 */ - async GetFrameData(key_frame) { - if (key_frame.key_frame == "KFTypePositionY") { + async GetFrameData(key_frame_key, key_frame) { + if (key_frame_key == "KFTypePositionY") { return key_frame.up_down_key_frame; - } else if (key_frame.key_frame == "KFTypePositionX") { + } else if (key_frame_key == "KFTypePositionX") { return key_frame.left_right_key_frame; - } else if (key_frame.key_frame == "KFTypeScale") { + } else if (key_frame_key == "KFTypeScale") { return key_frame.scale_key_frame; } else { return { @@ -683,7 +683,8 @@ export class ClipDraft { */ async AddKeyFarme() { let img_nodes = (await this.find_draft_node(this.draft_json.tracks, "type", "video")).segments; - let key_frame_tmp_data = JSON.parse(await fspromises.readFile(define.add_keyframe_tmp_path, "utf-8")); + let key_frame_tmp_data = await fspromises.readFile(define.add_keyframe_tmp_path, "utf-8"); + // 添加关键帧 // 将最后一个数据修改为背景音乐的最后时间 this.srt_information[this.srt_information.length - 1].end_time = this.audios_duration_time / 1000; @@ -693,34 +694,42 @@ export class ClipDraft { if (key_frame_setting == null) { return; } - let key_frame_pos = await this.GetFrameData(key_frame_setting); let isFixedSpeed = key_frame_setting.isFixedSpeed; - let key_frame_time = key_frame_setting.key_frame_time * 1000000; let isDown = true; - let scale_rate = key_frame_pos.default_scale / 100; // 获取通用的关键帧配置,然后添加到每一个图片上面(可以设置时间。当前图片的持续实现小于设置的时间。会计算不要过快) for (let i = 0; i < img_nodes.length; i++) { let element = img_nodes[i]; let image_duartion = cloneDeep(element.source_timerange.duration); + let key_frame = key_frame_setting.key_frame; + // 做随机处理 + let real_key_frame = key_frame; + let key_frame_list = ["KFTypePositionY", "KFTypePositionX", "KFTypeScale"]; + if (key_frame == "KFTypeRandom") { + // 随机获取 key_frame_list 里面的一个数据 + const randomIndex = Math.floor(Math.random() * key_frame_list.length); + real_key_frame = key_frame_list[randomIndex]; + } + + let key_frame_pos = await this.GetFrameData(real_key_frame, key_frame_setting); + + let key_frame_time = key_frame_setting.key_frame_time * 1000000; + let scale_rate = key_frame_pos.default_scale / 100; let up_pos = Math.abs(key_frame_pos.start_position); let down_pos = Math.abs(key_frame_pos.end_position); + let key_frame_tmp = JSON.parse(key_frame_tmp_data) - if (key_frame_setting.key_frame == "KFTypePositionY") { - - + if (real_key_frame == "KFTypePositionY") { // 勾选了匀速。需要计算时间(计算比例) - if (isFixedSpeed && image_duartion < key_frame_time) { let time_rate = image_duartion / key_frame_time; up_pos = up_pos * time_rate; down_pos = down_pos * time_rate; } - let key_frame_tmp = cloneDeep(key_frame_tmp_data) let up_pos_rate = isDown ? (up_pos / this.draft_json.canvas_config.height) : (0 - up_pos / this.draft_json.canvas_config.height) key_frame_tmp.id = uuidv4(); - key_frame_tmp.keyframe_list[0].id == uuidv4(); + key_frame_tmp.keyframe_list[0].id = uuidv4(); // Corrected assignment key_frame_tmp.keyframe_list[0].values = [up_pos_rate]; let dow_pos_rate = isDown ? (0 - down_pos / this.draft_json.canvas_config.height) : (down_pos / this.draft_json.canvas_config.height) @@ -728,22 +737,20 @@ export class ClipDraft { key_frame_tmp.keyframe_list[1].time_offset = image_duartion; key_frame_tmp.keyframe_list[1].values = [dow_pos_rate]; - key_frame_tmp.property_type = key_frame_setting.key_frame; + key_frame_tmp.property_type = real_key_frame; // 修改缩放倍率 element.clip.scale.x = scale_rate; element.clip.scale.y = scale_rate; element.clip.transform.y = dow_pos_rate; isDown = !isDown; - element.common_keyframes.push(key_frame_tmp); - } else if (key_frame_setting.key_frame == "KFTypePositionX") { + } else if (real_key_frame == "KFTypePositionX") { // 勾选了匀速。需要计算时间(计算比例) if (isFixedSpeed && image_duartion < key_frame_time) { let time_rate = image_duartion / key_frame_time; up_pos = up_pos * time_rate; down_pos = down_pos * time_rate; } - let key_frame_tmp = cloneDeep(key_frame_tmp_data) let up_pos_rate = isDown ? (up_pos / this.draft_json.canvas_config.width) : (0 - up_pos / this.draft_json.canvas_config.width) key_frame_tmp.id = uuidv4(); key_frame_tmp.keyframe_list[0].id == uuidv4(); @@ -754,16 +761,15 @@ export class ClipDraft { key_frame_tmp.keyframe_list[1].time_offset = image_duartion; key_frame_tmp.keyframe_list[1].values = [dow_pos_rate]; - key_frame_tmp.property_type = key_frame_setting.key_frame; + key_frame_tmp.property_type = real_key_frame; // 修改缩放倍率 element.clip.scale.x = scale_rate; element.clip.scale.y = scale_rate; element.clip.transform.x = dow_pos_rate; isDown = !isDown; - element.common_keyframes.push(key_frame_tmp); } - else if (key_frame_setting.key_frame == "KFTypeScale") { + else if (real_key_frame == "KFTypeScale") { if (isFixedSpeed && image_duartion < key_frame_time) { let time_rate = image_duartion / key_frame_time; // 计算方式和上面的不同 @@ -774,7 +780,6 @@ export class ClipDraft { } // 修改上面的数据,添加Y轴缩放 - let key_frame_tmp = cloneDeep(key_frame_tmp_data) let up_pos_rate = isDown ? up_pos / 100 : down_pos / 100; key_frame_tmp.id = uuidv4(); key_frame_tmp.keyframe_list[0].id == uuidv4(); @@ -785,7 +790,7 @@ export class ClipDraft { key_frame_tmp.keyframe_list[1].time_offset = image_duartion; key_frame_tmp.keyframe_list[1].values = [dow_pos_rate]; - key_frame_tmp.property_type = key_frame_setting.key_frame + "X"; + key_frame_tmp.property_type = real_key_frame + "X"; // 修改上面的数据,添加Y轴缩放 // 修改缩放倍率 @@ -794,15 +799,15 @@ export class ClipDraft { element.clip.transform.x = 0; element.common_keyframes.push(key_frame_tmp); - key_frame_tmp = cloneDeep(key_frame_tmp) + + key_frame_tmp = JSON.parse(JSON.stringify(key_frame_tmp)) key_frame_tmp.id = uuidv4(); key_frame_tmp.keyframe_list[0].id == uuidv4(); key_frame_tmp.keyframe_list[1].id = uuidv4(); - key_frame_tmp.property_type = key_frame_setting.key_frame + "Y"; - element.common_keyframes.push(key_frame_tmp); - + key_frame_tmp.property_type = real_key_frame + "Y"; isDown = !isDown; } + element.common_keyframes.push(key_frame_tmp); } } diff --git a/src/main/backPrompt/imageGenerate.js b/src/main/backPrompt/imageGenerate.js index 1571cce..888d1b1 100644 --- a/src/main/backPrompt/imageGenerate.js +++ b/src/main/backPrompt/imageGenerate.js @@ -12,7 +12,6 @@ const sharp = require('sharp'); const execAsync = util.promisify(exec); const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符 let fspromises = require("fs").promises; -import { MD5 } from "crypto-js"; import { ImageSetting } from "../../define/setting/imageSetting"; @@ -257,7 +256,7 @@ export class ImageGenerate { if (images.length <= 0) { throw new Error("未检测到抽帧图片。请检查"); } - if (images.length > auto_save_image.save_match_count) { + if (auto_save_image.save_match_count && auto_save_image.auto_match && images.length > auto_save_image.save_match_count) { png_files = await this.tools.getFilesWithExtensions(auto_save_image.main_save_folder, '.png'); } this.global.requestQuene.enqueue(async () => { diff --git a/src/main/discord/discordApi.js b/src/main/discord/discordApi.js index 9550ab1..973cf0d 100644 --- a/src/main/discord/discordApi.js +++ b/src/main/discord/discordApi.js @@ -1,14 +1,6 @@ const axios = require('axios'); -// import { Midjourney as mApi} from "midjourney"; -// const MJapi2 = require('midjourney'); -// const { midjourney } = require('midjourney') -// const Midjourney = require('midjourney'); -// ES5 的模块引入方式 const fetch = require("node-fetch"); -// ES6 的模块引入方式 -// import fetch from "node-fetch"; - export class DiscordAPI { constructor(mj_setting) { // https://discord.com/api/v9/channels/1208362852482809939/messages?limit=20 diff --git a/src/main/func.js b/src/main/func.js index b552748..7e0ff83 100644 --- a/src/main/func.js +++ b/src/main/func.js @@ -134,7 +134,7 @@ async function ReGenerateImageOne(window, value) { let image_styles = await ImageStyleDefine.getImageStyleStringByIds(value[1].image_style_list ? value[1].image_style_list : []); - let prompt = image_styles; + let prompt = sd_setting.webui.prompt + image_styles; // 拼接提示词 if (value[1].image_style != null) { prompt += `((${value[1].image_style})),`; @@ -143,6 +143,8 @@ async function ReGenerateImageOne(window, value) { prompt += `${value[1].lora},`; } prompt += value[1].prompt; + + let model = value[1].model; // 判断当前是不是有开修脸修手 @@ -624,8 +626,12 @@ async function SaveSDConfig(value) { try { let sd_config = JSON.parse((await fspromises.readFile(define.sd_setting, "utf-8")).toString()); global.config.webui_api_url = value.webui_api_url || value.webui_api_url == '' ? value.webui_api_url : global.config.webui_api_url; + sd_config.setting.webui_api_url = value.webui_api_url || value.webui_api_url == "" ? value.webui_api_url : sd_config.setting.webui_api_url; sd_config.setting.type = value.type ? value.type : sd_config.setting.type; + sd_config.setting.batch_size = value.batch_size ? value.batch_size : sd_config.setting.batch_size; + sd_config.setting.style_weight = value.style_weight ? value.style_weight : sd_config.setting.style_weight; + sd_config.webui.prompt = value.prompt || value.prompt == "" ? value.prompt : sd_config.webui.prompt; sd_config.webui.negative_prompt = value.negative_prompt || value.negative_prompt == "" ? value.negative_prompt : sd_config.webui.negative_prompt; sd_config.webui.denoising_strength = value.denoising_strength || value.denoising_strength == "" ? value.denoising_strength : sd_config.webui.denoising_strength; @@ -635,8 +641,6 @@ async function SaveSDConfig(value) { sd_config.webui.height = value.height ? value.height : sd_config.webui.height; sd_config.webui.cfg_scale = value.cfg_scale ? value.cfg_scale : sd_config.webui.cfg_scale; sd_config.webui.adetailer = value.adetailer ? value.adetailer : sd_config.webui.adetailer; - sd_config.setting.batch_size = value.batch_size ? value.batch_size : sd_config.setting.batch_size; - sd_config.setting.style_weight = value.style_weight ? value.style_weight : sd_config.setting.style_weight; await fspromises.writeFile(define.sd_setting, JSON.stringify(sd_config)); return { diff --git a/src/main/generalTools.js b/src/main/generalTools.js new file mode 100644 index 0000000..a468f24 --- /dev/null +++ b/src/main/generalTools.js @@ -0,0 +1,102 @@ +/** + * 判断字符串的值是不是存在,不是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 {*} code + * @param {*} data + * @param {*} message + * @returns + */ +function successMessage(data, message = null) { + return { + code: 1, + data: data, + message: message + } +} + +/** + * 返回失败的消息,包含code,message + * @param {*} code + * @param {*} message + * @returns + */ +function errorMessage(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 225be32..7c216f9 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,3 +1,8 @@ +import fspromises from "fs/promises"; +import { v4 as uuidv4 } from 'uuid'; +import { version } from '../../package.json' + + import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme } from 'electron' import path, { join } from 'path' import { electronApp, optimizer, is } from '@electron-toolkit/utils' @@ -6,10 +11,6 @@ import { define } from '../define/define.js' import { func } from './func.js' import { AsyncQueue } from "./quene.js" import { DEFINE_STRING } from '../define/define_string.js' -const fspromises = require("fs").promises; -const { v4: uuidv4 } = require('uuid'); -const { version } = require('../../package.json') - import { Tools } from './tools.js' import { ImageGenerate } from './backPrompt/imageGenerate.js' import { Setting } from './setting/setting.js' @@ -27,12 +28,14 @@ import { OriginalImageGenerateIpc } from './IPCEvent/originalImageGenerateIpc' import { SdIpc } from './IPCEvent/sdIpc.js' import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js' import { MainIpc } from './IPCEvent/mainIpc.js' +import { GlobalIpc } from "./IPCEvent/globalIpc.js"; let tools = new Tools(); let imageGenerate = new ImageGenerate(global); let setting = new Setting(global); + async function InitData(gl) { let res = await setting.getSettingDafultData(); gl.config = res; @@ -55,11 +58,17 @@ function removeIpcHandler(hash) { async function createWindow(hash = "ShowMessage", data, url = null) { // Create the browser window. + await InitData(global); global.currentHash = hash; + // 判断当前是不是有设置的宽高,用的话记忆 + let isRe = global.config.window_wh_bm_remember && hash == "ShowMessage" && global.config.window_wh_bm; + let mainWindow = new BrowserWindow({ - width: 900, - height: 670, + width: isRe ? global.config.window_wh_bm.width : 900, + height: isRe ? global.config.window_wh_bm.height : 675, + x: isRe ? global.config.window_wh_bm.x : 100, + y: isRe ? global.config.window_wh_bm.y : 100, title: 'LAITool', icon: '../../resources/icon.ico', show: false, @@ -104,10 +113,16 @@ async function createWindow(hash = "ShowMessage", data, url = null) { } } - mainWindow.on("closed", () => { + mainWindow.on("close", async () => { // 判断指定的窗口,移除指定的监听 removeIpcHandler(hash); global.newWindow = global.newWindow.filter(item => item.id != mainWindow.id) + // 判断当前的是不是开启了记录功能 + if (global.config.window_wh_bm_remember && hash == "ShowMessage") { + let window_wh_bm = mainWindow.getBounds(); + // 记录到文件中 + await setting.ModifySampleSetting(JSON.stringify({ window_wh_bm: window_wh_bm })) + } }) // 创建一个新的窗口,添加对应的监听 @@ -120,7 +135,6 @@ async function createWindow(hash = "ShowMessage", data, url = null) { }) setIpcHandler(hash); - await InitData(global); return mainWindow; } @@ -141,6 +155,8 @@ app.whenReady().then(async () => { optimizer.watchWindowShortcuts(window) }) + // + global.newWindow = []; mainWindow = createWindow('ShowMessage', null) @@ -173,9 +189,12 @@ app.whenReady().then(async () => { } // 判断动态文件是不是存在 - tools.checkJsonFileExistsOrCreate(path.join(define.dynamic_setting)); + tools.checkJsonFileExistsOrCreate(path.normalize(define.dynamic_setting)); // 判断标签文件是不是存在 - tools.checkJsonFileExistsOrCreate(path.join(define.tag_setting)); + tools.checkJsonFileExistsOrCreate(path.normalize(define.tag_setting)); + // 判断SD图片缓存文件是不是存在(不存在创建) + tools.checkFolderExistsOrCreate(path.normalize(define.temp_sd_image)); + tools.checkFolderExistsOrCreate(path.normalize(path.join(define.image_path, "c_s"))); app.on('activate', function () { // On macOS it's common to re-create a window in the app when the @@ -204,6 +223,7 @@ SdIpc(); MjIpc(); MainIpc(createWindow); OriginalImageGenerateIpc(); +GlobalIpc(); ipcMain.handle('dark-mode:toggle', (event, value) => { diff --git a/src/main/setting/setting.js b/src/main/setting/setting.js index 5d926b4..ae8d201 100644 --- a/src/main/setting/setting.js +++ b/src/main/setting/setting.js @@ -162,6 +162,9 @@ export class Setting { 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) { @@ -226,6 +229,7 @@ export class Setting { 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; } diff --git a/src/main/tools.js b/src/main/tools.js index 6827595..2be6805 100644 --- a/src/main/tools.js +++ b/src/main/tools.js @@ -25,6 +25,21 @@ export class Tools { } } + /** + * 获取指定的文件,然后将其转换为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文件 @@ -301,7 +316,6 @@ export class Tools { method: 'GET', url: url }); - request.on('response', (response) => { const chunks = []; response.on('data', (chunk) => chunks.push(chunk)); @@ -323,4 +337,29 @@ export class Tools { request.end(); }); } + + /** + * 将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/preload/discordIndex.js b/src/preload/discord.js similarity index 51% rename from src/preload/discordIndex.js rename to src/preload/discord.js index 4c771c9..577b629 100644 --- a/src/preload/discordIndex.js +++ b/src/preload/discord.js @@ -1,9 +1,7 @@ -import { contextBridge, ipcRenderer } from 'electron' +import { ipcRenderer } from 'electron' import { DEFINE_STRING } from '../define/define_string.js'; // Custom APIs for renderer - -let events = []; -const api = { +const discord = { // 创建MJ消息 CreateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.CREATE_MESSAGE, value), @@ -13,16 +11,7 @@ const api = { // MJ消息删除 DeleteMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.DELETE_MESSAGE, value), } -// Use `contextBridge` APIs to expose Electron APIs to -// renderer only if context isolation is enabled, otherwise -// just add to the DOM global. -if (process.contextIsolated) { - try { - contextBridge.exposeInMainWorld('api', api) - } catch (error) { - console.error(error) - } -} else { - window.api = api -} +export { + discord +} diff --git a/src/preload/index.js b/src/preload/index.js index 1a5d62b..10cf00f 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -1,6 +1,9 @@ import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' import { DEFINE_STRING } from '../define/define_string.js'; +import { discord } from './discord.js'; +import { mj } from './mj.js'; +import { sd } from './sd.js'; // Custom APIs for renderer let events = []; @@ -389,56 +392,11 @@ const api = { // 打开全局通知框 showGlobalNotificationDialog: (value) => ipcRenderer.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, value), + + // 知道文件地址,获取文件base64编码 + GetFileBase64: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_FILE_BASE64, value)), } -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)), - // 获取标签集的基础信息 - 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)), - // 获取当前对话频道的机器人列表 - 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生成的图片分割 - 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敏感词检查 - 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)), - - // 给图片链接,下载指定的图片并分割保存 - DownloadImageUrlAndSplit : async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, value)), -} - -const discord = { - // 创建MJ消息 - CreateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.CREATE_MESSAGE, value), - - // MJ消息更新 - UpdateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, value), - - // MJ消息删除 - DeleteMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.DELETE_MESSAGE, value), -} // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise // just add to the DOM global. @@ -448,6 +406,7 @@ if (process.contextIsolated) { contextBridge.exposeInMainWorld('api', api) contextBridge.exposeInMainWorld('mj', mj) contextBridge.exposeInMainWorld('discord', discord) + contextBridge.exposeInMainWorld("sd", sd) contextBridge.exposeInMainWorld('darkMode', { toggle: (value) => ipcRenderer.invoke('dark-mode:toggle', value), }) @@ -459,5 +418,6 @@ if (process.contextIsolated) { window.api = api; window.mj = mj; window.discord = discord; + window.sd = sd; } diff --git a/src/preload/mj.js b/src/preload/mj.js new file mode 100644 index 0000000..8a2bdb4 --- /dev/null +++ b/src/preload/mj.js @@ -0,0 +1,44 @@ +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)), + // 获取标签集的基础信息 + 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)), + // 获取当前对话频道的机器人列表 + 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生成的图片分割 + 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敏感词检查 + 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)), + + // 给图片链接,下载指定的图片并分割保存 + DownloadImageUrlAndSplit: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, value)), +} + +export { + mj +} \ No newline at end of file diff --git a/src/preload/sd.js b/src/preload/sd.js new file mode 100644 index 0000000..5fd5725 --- /dev/null +++ b/src/preload/sd.js @@ -0,0 +1,14 @@ +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)), + + // 文生图,单张 + txt2img: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SD.TXT2IMG, value)), +} +export { + sd +} \ No newline at end of file diff --git a/src/renderer/src/components/Backstep/CopyWriting.vue b/src/renderer/src/components/Backstep/CopyWriting.vue index a4cfcad..c1b70ed 100644 --- a/src/renderer/src/components/Backstep/CopyWriting.vue +++ b/src/renderer/src/components/Backstep/CopyWriting.vue @@ -1,623 +1,702 @@ \ No newline at end of file + + onMounted(async () => { + // 初始化加载项目下面的分镜好的文案 + // 并判断是不是有洗稿后的文件。一并加载 + await window.api.GetProjectWord((value) => { + debugger + data.value = value.data + }) + + let div = document.getElementById('word_option') + if (window.config.theme == 'dark') { + div.style.backgroundColor = '#24242c' + } else { + div.style.backgroundColor = '#FFF' + } + }) + + /** + * 替换洗稿后的所有数据 + */ + async function ReplaceText() { + // 开始替换 + for (let i = 0; i < data.value.length; i++) { + data.value[i].after_gpt = data.value[i].after_gpt.replaceAll( + replace_before.value, + replace_after.value + ) + } + } + + /** + * 重新计算指定索引值的时间 + * @param {data value 值的索引} index + */ + function modifyTime(index) { + let s_v = data.value[index].subValue + let len = s_v.length + if (len > 0) { + let s_t = s_v[0].start_time + let e_t = s_v[len - 1].end_time + data.value[index].start_time = s_t + data.value[index].end_time = e_t + data.value[index].timeLimit = `${s_t} -- ${e_t}` + } else { + data.value[index].start_time = null + data.value[index].end_time = null + data.value[index].timeLimit = `` + } + } + + /** + * 判断当前行的子数据的长度是不是大于0。不大于0 的话下面的字幕往上替补 + * @param {data value 的索引} index + */ + function moveUp(index) { + let s_v = data.value[index].subValue + if (s_v.length == 0) { + // 将下面所有的字幕数据往上移动 + for (let i = index; i < data.value.length; i++) { + // const element = data.value[i]; + if (data.value[i + 1] != null) { + data.value[i].subValue = data.value[i + 1].subValue + } else { + // 检查当前这个是不是为空。为空直接删除 + // 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除) + if ( + data.value[i].word == null || + data.value[i].word == '' || + data.value[i].after_gpt == null || + data.value[i].after_gpt == '' + ) { + // 直接删除 + data.value.splice(i, 1) + } + } + } + } + } + + /** + * 字幕文件向上 + */ + async function Upper(row, item, isM) { + // 向上合并。 + // 当前item。向上的话。判断是不是第一个。是第一个的话。直接网上一个合并。 + // 不是第一个的话。将上面的所有的合并。 + // 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。 + // 向下合并。逻辑相同。 + + if (row.lastId == null || row.lastId == '') { + message.error('当前是第一个。不能向上合并') + return + } + //判断当前行是不是第一个 + let itemId = item.id + let itemIndex = row.subValue.findIndex((item) => item.id == itemId) + let thisIndex = data.value.findIndex((item) => item.id == row.id) + if (itemIndex < 0 && isM) { + debugger + message.error('数据错误') + return + } else { + // 先获取上一行的数据 + let lastRowIndex = data.value.findIndex((item) => item.id == row.lastId) + //将当前及之前的数据向上合并 + for (let i = 0; i <= itemIndex; i++) { + const element = row.subValue[i] + data.value[lastRowIndex].subValue.push(element) + } + + // 删除指定的数据 + data.value[lastRowIndex + 1].subValue.splice(0, itemIndex + 1) + moveUp(thisIndex) + + // 判断当前行的文案和洗稿后文件是不是还有。(没有的话直接删除) + if ( + data.value[thisIndex].subValue == null && + (row.word == null || row.word == '' || row.after_gpt == null || row.after_gpt == '') + ) { + // 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId) + if (data.value[thisIndex + 1] != null) { + data.value[thisIndex + 1].lastId = row.lastId + } + // 直接删除 + data.value.splice(thisIndex, 1) + // 调整下面的所有的编号 + for (let i = thisIndex; i < data.value.length; i++) { + data.value[i].no = data.value[i].no - 1 + } + } + // 合并之后重新计算时间(计算上一个和当前的) + modifyTime(lastRowIndex) + modifyTime(thisIndex) + } + } + + /** + * 字幕文件向下 + */ + async function Down(row, item) { + // 向下合并。逻辑相同。 + // 当前item。。判断是不是最后一个。是最后一个的话。直接向下合并。 + // 不是第一个的话。将下面的所有的合并。 + // 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。 + //判断当前行是不是第一个 + let itemId = item.id + let itemIndex = row.subValue.findIndex((item) => item.id == itemId) + let thisIndex = data.value.findIndex((item) => item.id == row.id) + // 判断下一个是不是存在 + if (data.value[thisIndex + 1] == null) { + message.error('当前已经是最后一个。不能向下合并') + return + } + + if (itemIndex < 0) { + message.error('数据错误 2222') + return + } else { + // 获取下一行数据 + let nextRowIndex = data.value.findIndex((item) => item.lastId == row.id) + + //将当前以及下面的所有进行合并 + for (let i = row.subValue.length - 1; i >= itemIndex; i--) { + const element = row.subValue[i] + data.value[nextRowIndex].subValue.unshift(element) + } + + // 删除指定的数据 + data.value[thisIndex].subValue.splice(itemIndex) + + // 判断该当前字幕是不是为空(为空的话。下面的往上移动) + moveUp(thisIndex) + + // 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除) + if ( + data.value[thisIndex].subValue == null && + (row.word == null || row.word == '' || row.after_gpt == null || row.after_gpt == '') + ) { + // 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId) + if (data.value[nextRowIndex] != null) { + data.value[nextRowIndex].lastId = row.lastId + } + // 直接删除 + data.value.splice(thisIndex, 1) + // 调整下面的所有的编号 + for (let i = thisIndex; i < data.value.length; i++) { + data.value[i].no = data.value[i].no - 1 + } + } + // 合并之后重新计算时间(计算上一个和当前的) + modifyTime(nextRowIndex) + modifyTime(thisIndex) + } + } + + function removePunctuationIncludingEllipsis(sentence) { + // 扩展正则表达式以包含中文标点符号和省略号 + // 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符 + const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g + + // 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串 + return sentence.replace(punctuationRegExp, '') + } + + /** + * 对齐当前列即下面的数据 + */ + async function AlignNextWord(row) { + // 判断是不是已经到到导入过字幕了 + dialog.create({ + type: 'warning', + title: '警告', + showIcon: true, + content: '会自动对齐下面的数据。没有对齐成功就要手动调整在自动对齐', + style: `width : 400px;`, + maskClosable: false, + positiveText: '确定', + negativeText: '取消', + onPositiveClick: async () => { + // 下对齐 + let itemId = row.id + let thisIndex = data.value.findIndex((item) => item.id == itemId) + if (thisIndex < 0) { + message.error('数据错误') + return + } + + let nextSrt = [] + // 先将当前行下面的所有的数据拿出来 + for (let i = thisIndex; i < data.value.length; i++) { + const element = data.value[i] + nextSrt = nextSrt.concat(element.subValue) + } + let init_num = thisIndex + // let data + let tmp_str = '' + for (let i = 0; i < nextSrt.length; ) { + const element = nextSrt[i] + let current_text = `第 ${thisIndex + 1} 数据。字幕:${ + element.srt_value + }与文案不匹配。亲检查上下文。` + tmp_str += element.srt_value + + if ( + removePunctuationIncludingEllipsis(data.value[thisIndex].after_gpt).startsWith( + removePunctuationIncludingEllipsis(tmp_str) + ) + ) { + // data.value[thisIndex].subValue.push(element); + if (init_num < thisIndex || i > 0) { + Upper(data.value[thisIndex + 1], element, false) + } + i++ + } else { + if ( + removePunctuationIncludingEllipsis(data.value[thisIndex + 1].after_gpt).startsWith( + removePunctuationIncludingEllipsis(element.srt_value) + ) + ) { + thisIndex++ + tmp_str = '' + } else { + window.api.showGlobalMessageDialog({ + code: 0, + message: current_text + }) + return + } + } + } + } + }) + } + + /** + * 将原本的文案替换洗稿后 + */ + async function SwitchOldWord(row) { + data.value.forEach((item) => { + if (item.no == row.no) { + item.after_gpt = item.word + } + }) + } + + /** + * 单句写稿 + */ + async function AIModifyOneWord(row) { + await window.api.AIModifyOneWord([row.no, row.word], (value) => { + if (value.code != 1) { + message.error('未知错误') + return + } + // 修改数据 + let filter_data = data.value.filter((item) => item.no == value.data.no) + filter_data[0].after_gpt = value.data.content + }) + } + + /** + * 一键洗稿,AI修改选择的 + */ + async function AIModifyWord() { + let word_data = [] + for (let i = 0; i < selectKey.value.length; i++) { + const item = selectKey.value[i] + let obj = { + no: data.value[item - 1].no, + word: data.value[item - 1].word + } + word_data.push(obj) + } + for (let i = 0; i < word_data.length; i++) { + const item = word_data[i] + await AIModifyOneWord(item) + } + + // 洗完稿之后全局提示 + window.api.showGlobalMessageDialog({ code: 1, message: '一键洗稿完成' }) + } + + /** + * 保存洗稿后文件 + */ + async function SaveNewWord() { + let new_data_word = [] + // 先填充没有洗稿的 + for (let i = 0; i < data.value.length; i++) { + const item = data.value[i] + if (!item.after_gpt) { + item.after_gpt = item.word + } + new_data_word.push(item.after_gpt) + } + textData = new_data_word + // 开始保存 + await window.api.SaveNewWord(new_data_word, (value) => { + if (value.code != 1) { + message.error('未知错误') + return + } + message.success('保存成功,并写入到了剪贴板') + }) + } + + /** + * 保存字幕文案信息,用于自动生成视频 + */ + async function SaveCopywritingInformation() { + await window.api.SaveCopywritingInformation( + [toRaw(data.value), 'srt_time_information'], + (value) => { + if (value.code != 1) { + message.error('未知错误') + return + } + message.success('保存成功') + } + ) + } + + /** + * 导入字幕并进行分镜输出,显示时间,将分镜给他传进去 + */ + async function ImportSrtAndGetTime() { + if (textData.length <= 0) { + message.error('请先保存洗稿后文件,不洗稿也要保存一下') + return + } + await window.api.ImportSrtAndGetTime([toRaw(data.value)], (value) => { + if (value.code == 0) { + message.error(value.message) + return + } + // 写入时间 + for (let i = 0; i < value.data.length; i++) { + const item = value.data[i] + if (i >= data.value.length) { + item.no = i + 1 + item.timeLimit = `${item.start_time} -- ${item.end_time}` + data.value.push(item) + } else { + data.value[i].start_time = item.start_time + data.value[i].end_time = item.end_time + data.value[i].timeLimit = `${item.start_time} -- ${item.end_time}` + data.value[i].subValue = item.subValue + } + } + }) + } + /** + * 继续写被勾选的但是没有出来的 + */ + async function ContinueAIModifyWord() { + // 获取所有被选中的data + let word_data = [] + for (let i = 0; i < selectKey.value.length; i++) { + const item = selectKey.value[i] + + if (!data.value[item - 1].after_gpt) { + let obj = { + no: data.value[item - 1].no, + word: data.value[item - 1].word + } + word_data.push(obj) + } + } + for (let i = 0; i < word_data.length; i++) { + const item = word_data[i] + await AIModifyOneWord(item) + } + // 洗完稿之后全局提示 + window.api.showGlobalMessageDialog({ code: 1, message: '继续写稿完成' }) + } + + return { + data, + columns: createColumns({ + AIModifyOneWord, + SwitchOldWord + }), + rowKey: (row) => row.no, + handleCheck(rowKeys) { + selectKey.value = rowKeys + }, + pagination: false, + AIModifyWord, + AIModifyOneWord, + ContinueAIModifyWord, + ImportSrtAndGetTime, + SaveNewWord, + SaveCopywritingInformation, + replace_before, + replace_after, + ReplaceText + } + } +}) + diff --git a/src/renderer/src/components/Components/SelectImageStyle.vue b/src/renderer/src/components/Components/SelectImageStyle.vue index 3e53f56..f290c50 100644 --- a/src/renderer/src/components/Components/SelectImageStyle.vue +++ b/src/renderer/src/components/Components/SelectImageStyle.vue @@ -1,154 +1,281 @@ \ No newline at end of file + diff --git a/src/renderer/src/components/Components/ShowImageTag.vue b/src/renderer/src/components/Components/ShowImageTag.vue index f1a8b82..01e9e02 100644 --- a/src/renderer/src/components/Components/ShowImageTag.vue +++ b/src/renderer/src/components/Components/ShowImageTag.vue @@ -1,57 +1,72 @@ \ No newline at end of file + diff --git a/src/renderer/src/components/Original/Components/AddCharacterTag.vue b/src/renderer/src/components/Original/Components/AddCharacterTag.vue index 826acf2..58faa1e 100644 --- a/src/renderer/src/components/Original/Components/AddCharacterTag.vue +++ b/src/renderer/src/components/Original/Components/AddCharacterTag.vue @@ -1,173 +1,311 @@ \ No newline at end of file + diff --git a/src/renderer/src/components/Original/Components/AddStyleTags.vue b/src/renderer/src/components/Original/Components/AddStyleTags.vue index 5935cfa..4ac1e2b 100644 --- a/src/renderer/src/components/Original/Components/AddStyleTags.vue +++ b/src/renderer/src/components/Original/Components/AddStyleTags.vue @@ -1,144 +1,281 @@ \ No newline at end of file + diff --git a/src/renderer/src/components/Original/Components/CharacterTags.vue b/src/renderer/src/components/Original/Components/CharacterTags.vue index d787353..911e7bb 100644 --- a/src/renderer/src/components/Original/Components/CharacterTags.vue +++ b/src/renderer/src/components/Original/Components/CharacterTags.vue @@ -1,354 +1,468 @@