V 2.2.7 lama iopaint 去水印
This commit is contained in:
parent
8b011856c2
commit
f27ec08724
8
.gitignore
vendored
8
.gitignore
vendored
@ -4,6 +4,14 @@ out
|
|||||||
resources/scripts/build*
|
resources/scripts/build*
|
||||||
resources/scripts/dist
|
resources/scripts/dist
|
||||||
resources/scripts/model
|
resources/scripts/model
|
||||||
|
resources/scripts/lama/model
|
||||||
|
resources/scripts/lama/lama.7z
|
||||||
|
resources/scripts/lama/_internal
|
||||||
|
resources/scripts/lama/dist
|
||||||
|
resources/scripts/lama/build
|
||||||
|
resources/scripts/lama/lama_inpaint.exe
|
||||||
|
resources/scripts/virtual py
|
||||||
|
resources/logger
|
||||||
resources/scripts/Temp
|
resources/scripts/Temp
|
||||||
resources/image/Temp*
|
resources/image/Temp*
|
||||||
resources/package/ffmpeg-2023*
|
resources/package/ffmpeg-2023*
|
||||||
|
|||||||
244
package-lock.json
generated
244
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "laitool",
|
"name": "laitool",
|
||||||
"version": "2.2.6",
|
"version": "2.2.7",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "laitool",
|
"name": "laitool",
|
||||||
"version": "2.2.5",
|
"version": "2.2.6",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/alimt20181012": "^1.2.0",
|
"@alicloud/alimt20181012": "^1.2.0",
|
||||||
@ -19,6 +19,7 @@
|
|||||||
"7zip-min": "^1.4.4",
|
"7zip-min": "^1.4.4",
|
||||||
"awesome-js": "^2.0.0",
|
"awesome-js": "^2.0.0",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
|
"blob-to-buffer": "^1.2.9",
|
||||||
"compressing": "^1.10.0",
|
"compressing": "^1.10.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"electron-store": "^9.0.0",
|
"electron-store": "^9.0.0",
|
||||||
@ -33,13 +34,16 @@
|
|||||||
"node-machine-id": "^1.1.12",
|
"node-machine-id": "^1.1.12",
|
||||||
"node-reg": "^0.2.4",
|
"node-reg": "^0.2.4",
|
||||||
"npm": "^10.7.0",
|
"npm": "^10.7.0",
|
||||||
|
"paddle": "^1.0.0",
|
||||||
"sharp": "^0.33.2",
|
"sharp": "^0.33.2",
|
||||||
"systeminformation": "^5.22.10",
|
"systeminformation": "^5.22.10",
|
||||||
"tencentcloud-sdk-nodejs": "^4.0.821",
|
"tencentcloud-sdk-nodejs": "^4.0.821",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"wav-file-info": "^0.0.10",
|
"wav-file-info": "^0.0.10",
|
||||||
"winreg": "^1.2.5"
|
"winreg": "^1.2.5",
|
||||||
|
"winston": "^3.13.0",
|
||||||
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron-toolkit/eslint-config": "^1.0.1",
|
"@electron-toolkit/eslint-config": "^1.0.1",
|
||||||
@ -849,6 +853,14 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@colors/colors": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@colors/colors/-/colors-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.90"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@css-render/plugin-bem": {
|
"node_modules/@css-render/plugin-bem": {
|
||||||
"version": "0.15.12",
|
"version": "0.15.12",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -863,6 +875,16 @@
|
|||||||
"vue": "^3.0.11"
|
"vue": "^3.0.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@dabh/diagnostics": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
|
||||||
|
"dependencies": {
|
||||||
|
"colorspace": "1.1.x",
|
||||||
|
"enabled": "2.0.x",
|
||||||
|
"kuler": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@develar/schema-utils": {
|
"node_modules/@develar/schema-utils": {
|
||||||
"version": "2.6.5",
|
"version": "2.6.5",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -2201,6 +2223,11 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/triple-beam": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
|
||||||
|
},
|
||||||
"node_modules/@types/xml2js": {
|
"node_modules/@types/xml2js": {
|
||||||
"version": "0.4.14",
|
"version": "0.4.14",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -3036,6 +3063,25 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/blob-to-buffer": {
|
||||||
|
"version": "1.2.9",
|
||||||
|
"resolved": "https://registry.npmmirror.com/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz",
|
||||||
|
"integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/bluebird": {
|
"node_modules/bluebird": {
|
||||||
"version": "3.7.2",
|
"version": "3.7.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -3309,8 +3355,9 @@
|
|||||||
},
|
},
|
||||||
"node_modules/canvas": {
|
"node_modules/canvas": {
|
||||||
"version": "2.11.2",
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/canvas/-/canvas-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -3429,6 +3476,37 @@
|
|||||||
"color-support": "bin.js"
|
"color-support": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/colorspace": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/colorspace/-/colorspace-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^3.1.3",
|
||||||
|
"text-hex": "1.0.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/colorspace/node_modules/color": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.3",
|
||||||
|
"color-string": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/colorspace/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/colorspace/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -4401,6 +4479,11 @@
|
|||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/enabled": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/enabled/-/enabled-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
|
||||||
|
},
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -4839,6 +4922,11 @@
|
|||||||
"pend": "^1.2.0"
|
"pend": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fecha": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/fecha/-/fecha-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
|
||||||
|
},
|
||||||
"node_modules/file-entry-cache": {
|
"node_modules/file-entry-cache": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -4850,6 +4938,14 @@
|
|||||||
"node": "^10.12.0 || >=12.0.0"
|
"node": "^10.12.0 || >=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/file-stream-rotator": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.29.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/file-type": {
|
"node_modules/file-type": {
|
||||||
"version": "16.5.4",
|
"version": "16.5.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -4931,6 +5027,11 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fn.name": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/fn.name/-/fn.name-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||||
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.5",
|
"version": "1.15.5",
|
||||||
"funding": [
|
"funding": [
|
||||||
@ -5926,6 +6027,11 @@
|
|||||||
"version": "12.20.55",
|
"version": "12.20.55",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/kuler": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/kuler/-/kuler-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
|
||||||
|
},
|
||||||
"node_modules/lazy-val": {
|
"node_modules/lazy-val": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@ -6125,6 +6231,22 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/logform": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/logform/-/logform-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@colors/colors": "1.6.0",
|
||||||
|
"@types/triple-beam": "^1.3.2",
|
||||||
|
"fecha": "^4.2.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/long": {
|
"node_modules/long": {
|
||||||
"version": "5.2.3",
|
"version": "5.2.3",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
@ -6307,6 +6429,14 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@ -8927,6 +9057,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-hash": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-keys": {
|
"node_modules/object-keys": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -8946,6 +9084,14 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/one-time": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/one-time/-/one-time-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
||||||
|
"dependencies": {
|
||||||
|
"fn.name": "1.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.3",
|
"version": "0.9.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -8996,6 +9142,14 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/paddle": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/paddle/-/paddle-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-zqzikf2FmYZZNR2QBb3+B1nYF60jJBWke9B8WknDHLViY/ergS3/rgq8eVL5W5KIZ48Lj+NVN77J56bfqJFYzA==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pako": {
|
"node_modules/pako": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"license": "(MIT AND Zlib)"
|
"license": "(MIT AND Zlib)"
|
||||||
@ -9574,6 +9728,14 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-stable-stringify": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@ -9867,6 +10029,14 @@
|
|||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/stack-trace": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmmirror.com/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
|
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stat-mode": {
|
"node_modules/stat-mode": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -10182,6 +10352,11 @@
|
|||||||
"version": "1.13.0",
|
"version": "1.13.0",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
|
"node_modules/text-hex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/text-hex/-/text-hex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
|
||||||
|
},
|
||||||
"node_modules/text-table": {
|
"node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -10269,6 +10444,14 @@
|
|||||||
"version": "0.3.11",
|
"version": "0.3.11",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/triple-beam": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/triple-beam/-/triple-beam-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/truncate-utf8-bytes": {
|
"node_modules/truncate-utf8-bytes": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -10673,6 +10856,57 @@
|
|||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/winston": {
|
||||||
|
"version": "3.13.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/winston/-/winston-3.13.0.tgz",
|
||||||
|
"integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@colors/colors": "^1.6.0",
|
||||||
|
"@dabh/diagnostics": "^2.0.2",
|
||||||
|
"async": "^3.2.3",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"logform": "^2.4.0",
|
||||||
|
"one-time": "^1.0.0",
|
||||||
|
"readable-stream": "^3.4.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"stack-trace": "0.0.x",
|
||||||
|
"triple-beam": "^1.3.0",
|
||||||
|
"winston-transport": "^4.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston-daily-rotate-file": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==",
|
||||||
|
"dependencies": {
|
||||||
|
"file-stream-rotator": "^0.6.1",
|
||||||
|
"object-hash": "^3.0.0",
|
||||||
|
"triple-beam": "^1.4.1",
|
||||||
|
"winston-transport": "^4.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"winston": "^3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston-transport": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/winston-transport/-/winston-transport-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==",
|
||||||
|
"dependencies": {
|
||||||
|
"logform": "^2.3.2",
|
||||||
|
"readable-stream": "^3.6.0",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -10886,4 +11120,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "laitool",
|
"name": "laitool",
|
||||||
"version": "2.2.6",
|
"version": "2.2.7",
|
||||||
"description": "An Electron application with Vue",
|
"description": "An Electron application with Vue",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "example.com",
|
"author": "example.com",
|
||||||
@ -27,6 +27,7 @@
|
|||||||
"7zip-min": "^1.4.4",
|
"7zip-min": "^1.4.4",
|
||||||
"awesome-js": "^2.0.0",
|
"awesome-js": "^2.0.0",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
|
"blob-to-buffer": "^1.2.9",
|
||||||
"compressing": "^1.10.0",
|
"compressing": "^1.10.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"electron-store": "^9.0.0",
|
"electron-store": "^9.0.0",
|
||||||
@ -41,13 +42,16 @@
|
|||||||
"node-machine-id": "^1.1.12",
|
"node-machine-id": "^1.1.12",
|
||||||
"node-reg": "^0.2.4",
|
"node-reg": "^0.2.4",
|
||||||
"npm": "^10.7.0",
|
"npm": "^10.7.0",
|
||||||
|
"paddle": "^1.0.0",
|
||||||
"sharp": "^0.33.2",
|
"sharp": "^0.33.2",
|
||||||
"systeminformation": "^5.22.10",
|
"systeminformation": "^5.22.10",
|
||||||
"tencentcloud-sdk-nodejs": "^4.0.821",
|
"tencentcloud-sdk-nodejs": "^4.0.821",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"wav-file-info": "^0.0.10",
|
"wav-file-info": "^0.0.10",
|
||||||
"winreg": "^1.2.5"
|
"winreg": "^1.2.5",
|
||||||
|
"winston": "^3.13.0",
|
||||||
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron-toolkit/eslint-config": "^1.0.1",
|
"@electron-toolkit/eslint-config": "^1.0.1",
|
||||||
@ -78,6 +82,8 @@
|
|||||||
"resources/image/zhanwei.png",
|
"resources/image/zhanwei.png",
|
||||||
"resources/scripts/model/**",
|
"resources/scripts/model/**",
|
||||||
"resources/scripts/Lai.exe",
|
"resources/scripts/Lai.exe",
|
||||||
|
"resources/scripts/lama/lama_inpaint.exe",
|
||||||
|
"resources/scripts/lama/model/**",
|
||||||
"resources/scripts/discordScript.js",
|
"resources/scripts/discordScript.js",
|
||||||
"resources/tmp/**",
|
"resources/tmp/**",
|
||||||
"resources/icon.ico"
|
"resources/icon.ico"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
resources/scripts/lama/install.bat
Normal file
2
resources/scripts/lama/install.bat
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
pyinstaller -F --upx-dir="C:\\Users\\27698\\Desktop\\upx-4.2.4-win64\upx.exe" lama_inpaint.py
|
||||||
173
resources/scripts/lama/lama_inpaint.py
Normal file
173
resources/scripts/lama/lama_inpaint.py
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import io
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Union
|
||||||
|
import cv2
|
||||||
|
import torch
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||||
|
|
||||||
|
# 判断sys.argv 的长度,如果小于2,说明没有传入参数,设置初始参数
|
||||||
|
# if len(sys.argv) < 2:
|
||||||
|
# sys.argv = [
|
||||||
|
# "C:/Users/27698/Desktop/LAITool/resources/scripts/lama/lama_inpaint.exe",
|
||||||
|
# "-l",
|
||||||
|
# "C:\\Users\\27698\\Desktop\\测试\\mjTest\\data\\mask\\temp\\1717508661218.png",
|
||||||
|
# "C:\\Users\\27698\\Desktop\\测试\\mjTest\\data\\mask\\mask_temp_1717508662659.png",
|
||||||
|
# "C:\\Users\\27698\\Desktop\\测试\\mjTest\\data\\mask\\temp\\1717508564042.png",
|
||||||
|
# ]
|
||||||
|
print(sys.argv)
|
||||||
|
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
cript_directory = os.path.dirname(sys.executable)
|
||||||
|
elif __file__:
|
||||||
|
cript_directory = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
link_name = os.path.join(os.path.expanduser("~"), "big_lama.pt")
|
||||||
|
cu_name = os.path.join(cript_directory, "model\\big-lama.pt")
|
||||||
|
mode_pa = link_name
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
# # 判断model_path是否存在,如果不存在,设置默认值
|
||||||
|
if not os.path.exists(link_name):
|
||||||
|
os.system(f'mklink "{link_name}" "{cu_name}"')
|
||||||
|
print("Params: <runtime-config.json>")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def get_image(image):
|
||||||
|
if isinstance(image, Image.Image):
|
||||||
|
img = np.array(image)
|
||||||
|
elif isinstance(image, np.ndarray):
|
||||||
|
img = image.copy()
|
||||||
|
else:
|
||||||
|
raise Exception("Input image should be either PIL Image or numpy array!")
|
||||||
|
|
||||||
|
if img.ndim == 3:
|
||||||
|
img = np.transpose(img, (2, 0, 1)) # chw
|
||||||
|
elif img.ndim == 2:
|
||||||
|
img = img[np.newaxis, ...]
|
||||||
|
|
||||||
|
assert img.ndim == 3
|
||||||
|
|
||||||
|
img = img.astype(np.float32) / 255
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
def ceil_modulo(x, mod):
|
||||||
|
if x % mod == 0:
|
||||||
|
return x
|
||||||
|
return (x // mod + 1) * mod
|
||||||
|
|
||||||
|
|
||||||
|
def scale_image(img, factor, interpolation=cv2.INTER_AREA):
|
||||||
|
if img.shape[0] == 1:
|
||||||
|
img = img[0]
|
||||||
|
else:
|
||||||
|
img = np.transpose(img, (1, 2, 0))
|
||||||
|
|
||||||
|
img = cv2.resize(img, dsize=None, fx=factor, fy=factor, interpolation=interpolation)
|
||||||
|
|
||||||
|
if img.ndim == 2:
|
||||||
|
img = img[None, ...]
|
||||||
|
else:
|
||||||
|
img = np.transpose(img, (2, 0, 1))
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
def pad_img_to_modulo(img, mod):
|
||||||
|
channels, height, width = img.shape
|
||||||
|
out_height = ceil_modulo(height, mod)
|
||||||
|
out_width = ceil_modulo(width, mod)
|
||||||
|
return np.pad(
|
||||||
|
img,
|
||||||
|
((0, 0), (0, out_height - height), (0, out_width - width)),
|
||||||
|
mode="symmetric",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_img_and_mask(image, mask, device, pad_out_to_modulo=8, scale_factor=None):
|
||||||
|
out_image = get_image(image)
|
||||||
|
out_mask = get_image(mask)
|
||||||
|
|
||||||
|
if scale_factor is not None:
|
||||||
|
out_image = scale_image(out_image, 1)
|
||||||
|
out_mask = scale_image(out_mask, scale_factor, interpolation=cv2.INTER_NEAREST)
|
||||||
|
|
||||||
|
if pad_out_to_modulo is not None and pad_out_to_modulo > 1:
|
||||||
|
out_image = pad_img_to_modulo(out_image, pad_out_to_modulo)
|
||||||
|
out_mask = pad_img_to_modulo(out_mask, pad_out_to_modulo)
|
||||||
|
|
||||||
|
out_image = torch.from_numpy(out_image).unsqueeze(0).to(device)
|
||||||
|
out_mask = torch.from_numpy(out_mask).unsqueeze(0).to(device)
|
||||||
|
|
||||||
|
out_mask = (out_mask > 0) * 1
|
||||||
|
|
||||||
|
return out_image, out_mask
|
||||||
|
|
||||||
|
|
||||||
|
class LamaInpaint:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
device,
|
||||||
|
model_path=None,
|
||||||
|
) -> None:
|
||||||
|
if model_path is None:
|
||||||
|
model_path = os.path.join(cript_directory, "model\\big-lama.pt")
|
||||||
|
|
||||||
|
self.model = torch.jit.load(model_path, map_location=device)
|
||||||
|
self.model.eval()
|
||||||
|
self.model.to(device)
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
image: Union[Image.Image, np.ndarray],
|
||||||
|
mask: Union[Image.Image, np.ndarray],
|
||||||
|
):
|
||||||
|
if isinstance(image, np.ndarray):
|
||||||
|
orig_height, orig_width = image.shape[:2]
|
||||||
|
else:
|
||||||
|
orig_height, orig_width = np.array(image).shape[:2]
|
||||||
|
|
||||||
|
# image_width = image.shape[1]
|
||||||
|
# mask_width = mask.shape[1]
|
||||||
|
scale = image.width / mask.width
|
||||||
|
image, mask = prepare_img_and_mask(image, mask, self.device, 8, scale)
|
||||||
|
with torch.inference_mode():
|
||||||
|
inpainted = self.model(image, mask)
|
||||||
|
cur_res = inpainted[0].permute(1, 2, 0).detach().cpu().numpy()
|
||||||
|
cur_res = np.clip(cur_res * 255, 0, 255).astype("uint8")
|
||||||
|
cur_res = cur_res[:orig_height, :orig_width]
|
||||||
|
return cur_res
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
de = "cpu"
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
de = "cuda"
|
||||||
|
|
||||||
|
lama = LamaInpaint(de, mode_pa)
|
||||||
|
|
||||||
|
image_path = sys.argv[2]
|
||||||
|
mask_path = sys.argv[3]
|
||||||
|
output_path = sys.argv[4]
|
||||||
|
|
||||||
|
# 若是没有传递mask_path,需要自己计算mask区域
|
||||||
|
# 使用Image.open打开图片
|
||||||
|
image = Image.open(image_path).convert("RGB")
|
||||||
|
mask = Image.open(mask_path).convert("L")
|
||||||
|
|
||||||
|
res = lama.run(image, mask)
|
||||||
|
# 将修复后的图片保存到本地
|
||||||
|
img = Image.fromarray(res)
|
||||||
|
# 使用 save 方法将图像保存到文件
|
||||||
|
img.save(output_path)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
sys.exit(str(e))
|
||||||
43
resources/scripts/lama/lama_inpaint.spec
Normal file
43
resources/scripts/lama/lama_inpaint.spec
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['lama_inpaint.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='lama_inpaint',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
|
coll = COLLECT(
|
||||||
|
exe,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
name='lama_inpaint',
|
||||||
|
)
|
||||||
37
resources/scripts/lama_inpaint.spec
Normal file
37
resources/scripts/lama_inpaint.spec
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['lama_inpaint.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='lama_inpaint',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@ -51,27 +51,45 @@ let basicApi = {
|
|||||||
/**
|
/**
|
||||||
* 使用electron的net模块实现的post方法
|
* 使用electron的net模块实现的post方法
|
||||||
* @param {*} url 请求的url
|
* @param {*} url 请求的url
|
||||||
* @param {*} data 传输的数据(json格式)
|
* @param {*} data 传输的数据,,所有格式
|
||||||
* @param {*} headers 请求头
|
* @param {*} headers 请求头
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
post: (url, data = {}, headers = {}) => {
|
post: (url, data = {}, headers = {}, others = {}) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = net.request({
|
let req_obj = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: url,
|
url: url,
|
||||||
headers: Object.assign({
|
headers: Object.assign({
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}, headers)
|
}, headers)
|
||||||
});
|
}
|
||||||
|
req_obj = Object.assign(req_obj, others);
|
||||||
|
const request = net.request(req_obj);
|
||||||
|
|
||||||
request.write(JSON.stringify(data));
|
request.write(JSON.stringify(data));
|
||||||
|
|
||||||
request.on('response', (response) => {
|
request.on('response', (response) => {
|
||||||
let responseData = '';
|
let responseData;
|
||||||
|
if (response.headers["content-type"] === "application/json") {
|
||||||
|
|
||||||
|
responseData = '';
|
||||||
|
} else if (response.headers["content-type"].startsWith("image/")) {
|
||||||
|
// 处理图片数据
|
||||||
|
responseData = []
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("未知的请求返回数据类型");
|
||||||
|
}
|
||||||
|
|
||||||
response.on('data', (chunk) => {
|
response.on('data', (chunk) => {
|
||||||
responseData += chunk;
|
if (response.headers['content-type'] === 'application/json') {
|
||||||
|
// 处理 JSON 数据
|
||||||
|
responseData += chunk;
|
||||||
|
} else if (response.headers['content-type'].startsWith('image/')) {
|
||||||
|
// 处理图片数据
|
||||||
|
responseData.push(chunk);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
response.on('end', () => {
|
response.on('end', () => {
|
||||||
@ -83,8 +101,11 @@ let basicApi = {
|
|||||||
let parsedData;
|
let parsedData;
|
||||||
if (response.headers['content-type'].includes('application/json')) {
|
if (response.headers['content-type'].includes('application/json')) {
|
||||||
parsedData = JSON.parse(responseData);
|
parsedData = JSON.parse(responseData);
|
||||||
|
} else if (response.headers['content-type'].startsWith('image/')) {
|
||||||
|
// parsedData = responseData;
|
||||||
|
parsedData = Buffer.concat(responseData);
|
||||||
} else {
|
} else {
|
||||||
parsedData = responseData;
|
throw new Error("未知的请求返回数据类型");
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
|
|||||||
4
src/define/Tools/common.js
Normal file
4
src/define/Tools/common.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
const fspromises = fs.promises;
|
||||||
|
|
||||||
105
src/define/Tools/file.js
Normal file
105
src/define/Tools/file.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
|
||||||
|
import fs from "fs"
|
||||||
|
import path from "path";
|
||||||
|
const fspromises = fs.promises;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断文件或目录是否存在
|
||||||
|
* @param {*} path 文件或目录的路径
|
||||||
|
* @returns true表示存在,false表示不存在
|
||||||
|
*/
|
||||||
|
export async function CheckFileOrDirExist(path) {
|
||||||
|
try {
|
||||||
|
await fspromises.access(path);
|
||||||
|
return true; // 文件或目录存在
|
||||||
|
} catch (error) {
|
||||||
|
return false; // 文件或目录不存在
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** * 判断一个文件地址是不是文件夹
|
||||||
|
* @param {*} path 输入的文件地址
|
||||||
|
* @returns true 是 false 不是
|
||||||
|
*/
|
||||||
|
export async function IsDirectory(path) {
|
||||||
|
try {
|
||||||
|
const stat = await fspromises.stat(path);
|
||||||
|
return stat.isDirectory();
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`获取文件夹信息失败: ${path}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 将文件或者是文件夹备份到指定的文职
|
||||||
|
* @param {*} source_path 源文件/文件夹地址
|
||||||
|
* @param {*} target_path 目标文件/文件夹地址
|
||||||
|
*/
|
||||||
|
export async function BackupFileOrFolder(source_path, target_path) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// 判断父文件夹是否存在,不存在创建
|
||||||
|
const parent_path = path.dirname(target_path);
|
||||||
|
if (!(await CheckFileOrDirExist(parent_path))) {
|
||||||
|
await fspromises.mkdir(parent_path, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是不是文件夹
|
||||||
|
const isDirectory = await IsDirectory(source_path);
|
||||||
|
|
||||||
|
if (isDirectory) {
|
||||||
|
// 复制文件夹
|
||||||
|
await fspromises.rename(source_path, target_path);
|
||||||
|
} else {
|
||||||
|
// 复制文件
|
||||||
|
await fspromises.copyFile(source, target);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定的文件夹下面的所有的指定的拓展名的文件
|
||||||
|
* @param {*} folderPath 文件夹地址
|
||||||
|
* @param {*} extensions 拓展地址
|
||||||
|
* @returns 返回文件中指定的后缀文件地址(绝对地址)
|
||||||
|
*/
|
||||||
|
export async function GetFilesWithExtensions(folderPath, extensions) {
|
||||||
|
try {
|
||||||
|
// 判断当前是不是文件夹
|
||||||
|
if (!(await IsDirectory(folderPath))) {
|
||||||
|
throw new Error("输入的不是有效的文件夹地址")
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/define/Tools/image.js
Normal file
72
src/define/Tools/image.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/define/Tools/index.js
Normal file
9
src/define/Tools/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import * as image from './image';
|
||||||
|
import * as common from './common';
|
||||||
|
import * as file from './file'
|
||||||
|
|
||||||
|
export {
|
||||||
|
image,
|
||||||
|
common,
|
||||||
|
file
|
||||||
|
};
|
||||||
@ -13,6 +13,7 @@ if (!app.isPackaged) {
|
|||||||
img_base: path.join(__dirname, "../../resources/config/img_base.json"),
|
img_base: path.join(__dirname, "../../resources/config/img_base.json"),
|
||||||
video_config: path.join(__dirname, "../../resources/config/video_config.json"),
|
video_config: path.join(__dirname, "../../resources/config/video_config.json"),
|
||||||
scripts_path: path.join(__dirname, "../../resources/scripts"),
|
scripts_path: path.join(__dirname, "../../resources/scripts"),
|
||||||
|
logger_path: path.join(__dirname, "../../resources/logger"),
|
||||||
package_path: path.join(__dirname, "../../resources/package"),
|
package_path: path.join(__dirname, "../../resources/package"),
|
||||||
image_path: path.join(__dirname, "../../resources/image"),
|
image_path: path.join(__dirname, "../../resources/image"),
|
||||||
temp_sd_image: path.join(__dirname, "../../resources/image/TempSDImage"),
|
temp_sd_image: path.join(__dirname, "../../resources/image/TempSDImage"),
|
||||||
@ -43,6 +44,7 @@ if (!app.isPackaged) {
|
|||||||
video_config: path.join(__dirname, "../../../resources/config/video_config.json"),
|
video_config: path.join(__dirname, "../../../resources/config/video_config.json"),
|
||||||
img_base: path.join(__dirname, "../../../resources/config/img_base.json"),
|
img_base: path.join(__dirname, "../../../resources/config/img_base.json"),
|
||||||
scripts_path: path.join(__dirname, "../../../resources/scripts"),
|
scripts_path: path.join(__dirname, "../../../resources/scripts"),
|
||||||
|
logger_path: path.join(__dirname, "../../../resources/logger"),
|
||||||
package_path: path.join(__dirname, "../../../resources/package"),
|
package_path: path.join(__dirname, "../../../resources/package"),
|
||||||
discordScript: path.join(__dirname, "../../../resources/scripts/discordScript.js"),
|
discordScript: path.join(__dirname, "../../../resources/scripts/discordScript.js"),
|
||||||
image_path: path.join(__dirname, "../../../resources/image"),
|
image_path: path.join(__dirname, "../../../resources/image"),
|
||||||
|
|||||||
@ -180,6 +180,13 @@ export const DEFINE_STRING = {
|
|||||||
OPEN_DISCORD_WINDOW: "OPEN_DISCORD_WINDOW"
|
OPEN_DISCORD_WINDOW: "OPEN_DISCORD_WINDOW"
|
||||||
},
|
},
|
||||||
IMG: {
|
IMG: {
|
||||||
ONE_SPLIT_FOUR: "ONE_SPLIT_FOUR"
|
ONE_SPLIT_FOUR: "ONE_SPLIT_FOUR",
|
||||||
|
BASE64_TO_FILE: "BASE64_TO_FILE",
|
||||||
|
PROCESS_IMAGE: "PROCESS_IMAGE",
|
||||||
|
BATCH_PROCESS_IMAGE: "BATCH_PROCESS_IMAGE",
|
||||||
|
BATCH_PROCESS_IMAGE_RESULT: "BATCH_PROCESS_IMAGE_RESULT"
|
||||||
|
},
|
||||||
|
SYSTEM: {
|
||||||
|
OPEN_FILE: "OPEN_FILE",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3
src/define/logger_define.js
Normal file
3
src/define/logger_define.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const LOGGER_DEFINE = {
|
||||||
|
REMOVE_WATERMARK: "去除水印",
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import { DEFINE_STRING } from '../../define/define_string'
|
import { DEFINE_STRING } from '../../define/define_string'
|
||||||
import { Image } from "../Public/Image";
|
import { Image } from "../Public/Image";
|
||||||
|
import { LOGGER_DEFINE } from "../../define/logger_define";
|
||||||
|
import { errorMessage } from "../generalTools";
|
||||||
let image = new Image(global);
|
let image = new Image(global);
|
||||||
|
|
||||||
|
|
||||||
@ -8,6 +10,21 @@ function ImageIpc() {
|
|||||||
|
|
||||||
// 一拆四
|
// 一拆四
|
||||||
ipcMain.handle(DEFINE_STRING.IMG.ONE_SPLIT_FOUR, async (event, value) => await image.OneSplitFour(value));
|
ipcMain.handle(DEFINE_STRING.IMG.ONE_SPLIT_FOUR, async (event, value) => await image.OneSplitFour(value));
|
||||||
|
|
||||||
|
// 将base64的图片转换为文件
|
||||||
|
ipcMain.handle(DEFINE_STRING.IMG.BASE64_TO_FILE, async (event, value) => await image.Base64ToFile(value));
|
||||||
|
|
||||||
|
// t图片处理,去除水印
|
||||||
|
ipcMain.handle(DEFINE_STRING.IMG.PROCESS_IMAGE, async (event, value) => {
|
||||||
|
try {
|
||||||
|
return await image.ProcessImage(value)
|
||||||
|
} catch (error) {
|
||||||
|
return errorMessage(error, LOGGER_DEFINE.REMOVE_WATERMARK)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量处理,去除所有水印
|
||||||
|
ipcMain.handle(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE, async (event, value) => await image.BatchProcessImage(value));
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
ImageIpc
|
ImageIpc
|
||||||
|
|||||||
17
src/main/IPCEvent/system.js
Normal file
17
src/main/IPCEvent/system.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { ipcMain } from "electron";
|
||||||
|
import { DEFINE_STRING } from '../../define/define_string'
|
||||||
|
const { shell } = require('electron')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function SystemIpc() {
|
||||||
|
|
||||||
|
// 打开指定的文件
|
||||||
|
ipcMain.on(DEFINE_STRING.SYSTEM.OPEN_FILE, async (event, value) => {
|
||||||
|
await shell.openPath(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
SystemIpc
|
||||||
|
}
|
||||||
@ -1,6 +1,16 @@
|
|||||||
import { errorMessage, successMessage } from "../generalTools";
|
import { errorMessage, successMessage } from "../generalTools";
|
||||||
import path from "path";
|
import path, { resolve } from "path";
|
||||||
import { Tools } from "../tools";
|
import { Tools } from "../tools";
|
||||||
|
import fs from "fs";
|
||||||
|
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||||
|
import { isEmpty, reject } from "lodash";
|
||||||
|
import { basicApi } from "../../api/apiBasic";
|
||||||
|
import sharp from 'sharp';
|
||||||
|
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";
|
||||||
|
|
||||||
export class Image {
|
export class Image {
|
||||||
constructor(global) {
|
constructor(global) {
|
||||||
@ -45,4 +55,281 @@ export class Image {
|
|||||||
return errorMessage(error.message);
|
return errorMessage(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片处理,去除水印
|
||||||
|
* @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" : "";
|
||||||
|
|
||||||
|
if (isRemote && isEmpty(urls)) {
|
||||||
|
throw new Error("使用iopaint图片处理,但是没有配置图片处理地址");
|
||||||
|
}
|
||||||
|
if (!isRemote && isEmpty(value.out_file)) {
|
||||||
|
throw new Error("水印处理,使用软件直接处理类型为file,需要指定输出的文件地址");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量处理所有的图片,去除水印
|
||||||
|
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
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 默认所有的的宽高都是一样的,获取第一张图片的宽高
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -263,7 +263,7 @@ export class ImageGenerate {
|
|||||||
let task_list = task_list_json.task_list.filter(item => item.id == element)[0];
|
let task_list = task_list_json.task_list.filter(item => item.id == element)[0];
|
||||||
let seed = -1;
|
let seed = -1;
|
||||||
await fspromises.mkdir(path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder), { recursive: true });
|
await fspromises.mkdir(path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder), { recursive: true });
|
||||||
this.global.requestQuene.enqueue(async () => {
|
await this.global.requestQuene.enqueue(async () => {
|
||||||
let res = await this.sd.OneImageGeneration(images[0], task_list, seed);
|
let res = await this.sd.OneImageGeneration(images[0], task_list, seed);
|
||||||
let tmp_seed = -1;
|
let tmp_seed = -1;
|
||||||
if (seed == -1) {
|
if (seed == -1) {
|
||||||
@ -293,7 +293,7 @@ export class ImageGenerate {
|
|||||||
let copy_path = path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder, base_name);
|
let copy_path = path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder, base_name);
|
||||||
await this.tools.copyFileOrDirectory(randomData, copy_path);
|
await this.tools.copyFileOrDirectory(randomData, copy_path);
|
||||||
} else {
|
} else {
|
||||||
this.global.requestQuene.enqueue(async () => {
|
await this.global.requestQuene.enqueue(async () => {
|
||||||
await this.sd.OneImageGeneration(item, task_list, tmp_seed);
|
await this.sd.OneImageGeneration(item, task_list, tmp_seed);
|
||||||
}, `${task_list.out_folder}_${images[j]}`, batch, task_list.out_folder)
|
}, `${task_list.out_folder}_${images[j]}`, batch, task_list.out_folder)
|
||||||
}
|
}
|
||||||
@ -339,6 +339,7 @@ export class ImageGenerate {
|
|||||||
|
|
||||||
// 监听总批次完成
|
// 监听总批次完成
|
||||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||||
|
debugger
|
||||||
if (failedTasks.length > 0) {
|
if (failedTasks.length > 0) {
|
||||||
let message = `
|
let message = `
|
||||||
批次生成任务都已完成。
|
批次生成任务都已完成。
|
||||||
@ -353,6 +354,12 @@ export class ImageGenerate {
|
|||||||
message: message
|
message: message
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
//判断当前batch是不是还有任务没有跑完
|
||||||
|
let current_batch = this.global.requestQuene.getBatch(batch);
|
||||||
|
if (current_batch && current_batch.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||||
code: 1,
|
code: 1,
|
||||||
message: "所有生成任务完成"
|
message: "所有生成任务完成"
|
||||||
|
|||||||
@ -640,7 +640,7 @@ async function SaveSDConfig(value) {
|
|||||||
sd_config.webui.width = value.width ? value.width : sd_config.webui.width;
|
sd_config.webui.width = value.width ? value.width : sd_config.webui.width;
|
||||||
sd_config.webui.height = value.height ? value.height : sd_config.webui.height;
|
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.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.webui.adetailer = value.hasOwnProperty("adetailer") ? value.adetailer : sd_config.webui.adetailer;
|
||||||
|
|
||||||
await fspromises.writeFile(define.sd_setting, JSON.stringify(sd_config));
|
await fspromises.writeFile(define.sd_setting, JSON.stringify(sd_config));
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -66,11 +66,15 @@ function checkStringValueDeletePrefix(value, prefix) {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 返回成功的消息,包含code,data,message
|
* 返回成功的消息,包含code,data,message
|
||||||
* @param {*} data
|
* @param {*} data 返回的数据
|
||||||
* @param {*} message
|
* @param {*} message 成功消息
|
||||||
|
* @param {*} service 消息日志类型
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function successMessage(data, message = null) {
|
function successMessage(data, message = null, service = null) {
|
||||||
|
if (service && message) {
|
||||||
|
global.logger.info(service, `成功返回数据`);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
code: 1,
|
code: 1,
|
||||||
data: data,
|
data: data,
|
||||||
@ -79,11 +83,15 @@ function successMessage(data, message = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回失败的消息,包含code,message
|
* 返回失败的消息,
|
||||||
* @param {*} message
|
* @param {*} message 错误信息
|
||||||
|
* @param {*} service 错误日志类型
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function errorMessage(message) {
|
function errorMessage(message, service = null) {
|
||||||
|
if (service && message) {
|
||||||
|
global.logger.error(service, message);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
message: message
|
message: message
|
||||||
|
|||||||
@ -31,7 +31,9 @@ import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js'
|
|||||||
import { MainIpc } from './IPCEvent/mainIpc.js'
|
import { MainIpc } from './IPCEvent/mainIpc.js'
|
||||||
import { GlobalIpc } from "./IPCEvent/globalIpc.js";
|
import { GlobalIpc } from "./IPCEvent/globalIpc.js";
|
||||||
import { ImageIpc } from "./IPCEvent/imageIpc.js";
|
import { ImageIpc } from "./IPCEvent/imageIpc.js";
|
||||||
|
import { SystemIpc } from "./IPCEvent/system.js";
|
||||||
import { system } from "systeminformation";
|
import { system } from "systeminformation";
|
||||||
|
import { Logger } from "./logger.js";
|
||||||
|
|
||||||
let tools = new Tools();
|
let tools = new Tools();
|
||||||
let imageGenerate = new ImageGenerate(global);
|
let imageGenerate = new ImageGenerate(global);
|
||||||
@ -44,6 +46,7 @@ async function InitData(gl) {
|
|||||||
gl.config = res;
|
gl.config = res;
|
||||||
gl.requestQuene = new AsyncQueue(gl, res.task_number);
|
gl.requestQuene = new AsyncQueue(gl, res.task_number);
|
||||||
gl.fileQueue = new AsyncQueue(gl, 1);
|
gl.fileQueue = new AsyncQueue(gl, 1);
|
||||||
|
gl.logger = new Logger(define.logger_path);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +197,13 @@ app.whenReady().then(async () => {
|
|||||||
// 判断动态文件是不是存在
|
// 判断动态文件是不是存在
|
||||||
tools.checkJsonFileExistsOrCreate(path.normalize(define.dynamic_setting));
|
tools.checkJsonFileExistsOrCreate(path.normalize(define.dynamic_setting));
|
||||||
// 判断标签文件是不是存在
|
// 判断标签文件是不是存在
|
||||||
tools.checkJsonFileExistsOrCreate(path.normalize(define.tag_setting));
|
tools.checkJsonFileExistsOrCreate(path.normalize(define.tag_setting), JSON.stringify({
|
||||||
|
"character_tags": [],
|
||||||
|
"style_tags": [],
|
||||||
|
"scene_tags": [],
|
||||||
|
"prefix_tags": [],
|
||||||
|
"suffix_tags": []
|
||||||
|
}));
|
||||||
// 判断SD图片缓存文件是不是存在(不存在创建)
|
// 判断SD图片缓存文件是不是存在(不存在创建)
|
||||||
tools.checkFolderExistsOrCreate(path.normalize(define.temp_sd_image));
|
tools.checkFolderExistsOrCreate(path.normalize(define.temp_sd_image));
|
||||||
tools.checkFolderExistsOrCreate(path.normalize(path.join(define.image_path, "c_s")));
|
tools.checkFolderExistsOrCreate(path.normalize(path.join(define.image_path, "c_s")));
|
||||||
@ -230,6 +239,7 @@ MainIpc(createWindow);
|
|||||||
OriginalImageGenerateIpc();
|
OriginalImageGenerateIpc();
|
||||||
GlobalIpc();
|
GlobalIpc();
|
||||||
ImageIpc();
|
ImageIpc();
|
||||||
|
SystemIpc();
|
||||||
|
|
||||||
|
|
||||||
ipcMain.handle('dark-mode:toggle', (event, value) => {
|
ipcMain.handle('dark-mode:toggle', (event, value) => {
|
||||||
|
|||||||
59
src/main/logger.js
Normal file
59
src/main/logger.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// const winston = require('winston');
|
||||||
|
import winston from 'winston';
|
||||||
|
import path from 'path';
|
||||||
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
constructor(log_folder) {
|
||||||
|
this.log_folder = log_folder;
|
||||||
|
this.logger = winston.createLogger({
|
||||||
|
level: 'info',
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp(),
|
||||||
|
winston.format.printf(info => `${info.timestamp} [${info.level.toUpperCase()}] [${info.service}] ${info.message}`)
|
||||||
|
),
|
||||||
|
transports: [
|
||||||
|
new DailyRotateFile({
|
||||||
|
filename: path.resolve(this.log_folder, `LAITool-%DATE%.log`),
|
||||||
|
datePattern: 'YYYY-MM-DD',
|
||||||
|
zippedArchive: true,
|
||||||
|
maxSize: '10m',
|
||||||
|
maxFiles: '14d'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
this.logger.add(new winston.transports.Console({
|
||||||
|
format: winston.format.simple(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存info级别的日志
|
||||||
|
* @param {*} service service 名称
|
||||||
|
* @param {*} message 消息
|
||||||
|
*/
|
||||||
|
info(service, message) {
|
||||||
|
this.logger.info(message, { service });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存error级别的日志
|
||||||
|
* @param {*} service service 名称
|
||||||
|
* @param {*} message 消息
|
||||||
|
*/
|
||||||
|
error(service, message) {
|
||||||
|
this.logger.error(message, { service });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存warn级别的日志
|
||||||
|
* @param {*} service service 名称
|
||||||
|
* @param {*} message 消息
|
||||||
|
*/
|
||||||
|
warn(service, message) {
|
||||||
|
this.logger.warn(message, { service });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,7 +38,7 @@ export class AsyncQueue {
|
|||||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining++;
|
this.batchCompletion[batchId].subBatches[subBatchId].remaining++;
|
||||||
this.tasks.push({ task, taskId, batchId, subBatchId });
|
this.tasks.push({ task, taskId, batchId, subBatchId });
|
||||||
if (!this.manualMode) {
|
if (!this.manualMode) {
|
||||||
this.process();
|
await this.process();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ export class AsyncQueue {
|
|||||||
while (this.tasks.length > 0 && (this.manualMode ? this.taskProgress.length < task_count : this.currentConcurrency < this.concurrencyLimit)) {
|
while (this.tasks.length > 0 && (this.manualMode ? this.taskProgress.length < task_count : this.currentConcurrency < this.concurrencyLimit)) {
|
||||||
const { task, taskId, batchId, subBatchId } = this.tasks.shift();
|
const { task, taskId, batchId, subBatchId } = this.tasks.shift();
|
||||||
this.currentConcurrency++;
|
this.currentConcurrency++;
|
||||||
task().then(() => {
|
Promise.resolve(task()).then(() => {
|
||||||
this.currentConcurrency--;
|
this.currentConcurrency--;
|
||||||
this.handleTaskCompletion(batchId, subBatchId, null);
|
this.handleTaskCompletion(batchId, subBatchId, null);
|
||||||
if (!this.manualMode) {
|
if (!this.manualMode) {
|
||||||
@ -243,6 +243,12 @@ export class AsyncQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取指定batch的任务列表
|
||||||
|
getTasks(batchId) {
|
||||||
|
return this.tasks.filter(item => item.batchId === batchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取失败的任务
|
// 获取失败的任务
|
||||||
getFailedTasks(batchId, subBatchId = 'default') {
|
getFailedTasks(batchId, subBatchId = 'default') {
|
||||||
if (this.batchCompletion[batchId] && this.batchCompletion[batchId].subBatches[subBatchId]) {
|
if (this.batchCompletion[batchId] && this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export class Tools {
|
|||||||
* 判断json文件是不是存在,不存在的话,协议一个空的json文件
|
* 判断json文件是不是存在,不存在的话,协议一个空的json文件
|
||||||
* @param {*} filePath 文件地址
|
* @param {*} filePath 文件地址
|
||||||
*/
|
*/
|
||||||
async checkJsonFileExistsOrCreate(filePath) {
|
async checkJsonFileExistsOrCreate(filePath, defaultValue = "{}") {
|
||||||
try {
|
try {
|
||||||
if (!(await this.checkExists(filePath))) {
|
if (!(await this.checkExists(filePath))) {
|
||||||
// 判断传入的json文件的父文件夹是不是存在,不存在的话,创建
|
// 判断传入的json文件的父文件夹是不是存在,不存在的话,创建
|
||||||
@ -54,7 +54,7 @@ export class Tools {
|
|||||||
// 创建文件夹
|
// 创建文件夹
|
||||||
await fspromises.mkdir(parentFolder, { recursive: true });
|
await fspromises.mkdir(parentFolder, { recursive: true });
|
||||||
}
|
}
|
||||||
await fspromises.writeFile(filePath, "{}");
|
await fspromises.writeFile(filePath, defaultValue);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
@ -101,7 +101,7 @@ export class Tools {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 延时多少秒
|
* 延时多少秒
|
||||||
* @param {等待时间} time
|
* @param {*} time
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async delay(time) {
|
async delay(time) {
|
||||||
@ -123,8 +123,8 @@ export class Tools {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定路径下面的指定的拓展的文件
|
* 获取指定路径下面的指定的拓展的文件
|
||||||
* @param {指定的文件夹路径} folderPath
|
* @param {*} folderPath
|
||||||
* @param {后缀名} extensions
|
* @param {*} extensions
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getFilesWithExtensions(folderPath, extensions) {
|
async getFilesWithExtensions(folderPath, extensions) {
|
||||||
|
|||||||
@ -5,6 +5,15 @@ import { DEFINE_STRING } from "../define/define_string"
|
|||||||
const img = {
|
const img = {
|
||||||
// 加载当前链接的SD服务数据
|
// 加载当前链接的SD服务数据
|
||||||
OneSplitFour: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.ONE_SPLIT_FOUR, value)),
|
OneSplitFour: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.ONE_SPLIT_FOUR, value)),
|
||||||
|
|
||||||
|
// 将base64的图片转换为文件
|
||||||
|
Base64ToFile: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.BASE64_TO_FILE, value)),
|
||||||
|
|
||||||
|
// 请求图片处理,去除水印
|
||||||
|
ProcessImage: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.PROCESS_IMAGE, value)),
|
||||||
|
|
||||||
|
//批量处理图片,去除水印
|
||||||
|
BatchProcessImage: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE, value)),
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
img
|
img
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { discord } from './discord.js';
|
|||||||
import { mj } from './mj.js';
|
import { mj } from './mj.js';
|
||||||
import { sd } from './sd.js';
|
import { sd } from './sd.js';
|
||||||
import { img } from './img.js';
|
import { img } from './img.js';
|
||||||
|
import { system } from './system.js';
|
||||||
// Custom APIs for renderer
|
// Custom APIs for renderer
|
||||||
|
|
||||||
let events = [];
|
let events = [];
|
||||||
@ -413,6 +414,7 @@ if (process.contextIsolated) {
|
|||||||
contextBridge.exposeInMainWorld('discord', discord)
|
contextBridge.exposeInMainWorld('discord', discord)
|
||||||
contextBridge.exposeInMainWorld("sd", sd)
|
contextBridge.exposeInMainWorld("sd", sd)
|
||||||
contextBridge.exposeInMainWorld("img", img)
|
contextBridge.exposeInMainWorld("img", img)
|
||||||
|
contextBridge.exposeInMainWorld("system", system)
|
||||||
contextBridge.exposeInMainWorld('darkMode', {
|
contextBridge.exposeInMainWorld('darkMode', {
|
||||||
toggle: (value) => ipcRenderer.invoke('dark-mode:toggle', value),
|
toggle: (value) => ipcRenderer.invoke('dark-mode:toggle', value),
|
||||||
})
|
})
|
||||||
@ -426,5 +428,6 @@ if (process.contextIsolated) {
|
|||||||
window.discord = discord;
|
window.discord = discord;
|
||||||
window.sd = sd;
|
window.sd = sd;
|
||||||
window.img = img;
|
window.img = img;
|
||||||
|
window.system = system;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/preload/system.js
Normal file
11
src/preload/system.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ipcRenderer } from "electron"
|
||||||
|
import { DEFINE_STRING } from "../define/define_string"
|
||||||
|
|
||||||
|
|
||||||
|
const system = {
|
||||||
|
// 打开指定的文件
|
||||||
|
OpenFile: async (value, callback) => callback(ipcRenderer.send(DEFINE_STRING.SYSTEM.OPEN_FILE, value)),
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
system
|
||||||
|
}
|
||||||
@ -1,190 +1,250 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-tabs animated default-value="inGetFrame">
|
<n-tabs animated default-value="inGetFrame">
|
||||||
<n-tab-pane size="large" name="draftGetFrame" tab="剪映草稿抽帧">
|
<n-tab-pane size="large" name="draftGetFrame" tab="剪映草稿抽帧">
|
||||||
<n-spin :show="show">
|
<n-spin :show="show">
|
||||||
<template #description>
|
<template #description> 正在抽帧 </template>
|
||||||
正在抽帧
|
<n-space vertical>
|
||||||
</template>
|
<div style="color: red">注意:该项是使用剪映内的草稿进行分镜</div>
|
||||||
<n-space vertical>
|
<n-select
|
||||||
<div style="color: red;">
|
v-model:value="selectedValue"
|
||||||
注意:该项是使用剪映内的草稿进行分镜
|
filterable
|
||||||
</div>
|
placeholder="选择草稿"
|
||||||
<n-select v-model:value="selectedValue" filterable placeholder="选择草稿" :options="options" />
|
:options="options"
|
||||||
<n-button round type="primary" @click="getFrameFunc">开始抽帧</n-button>
|
/>
|
||||||
</n-space>
|
<div>
|
||||||
</n-spin>
|
<n-button round type="primary" @click="getFrameFunc">开始抽帧</n-button>
|
||||||
<n-divider />
|
<n-button style="margin-left: 10px" round type="primary" @click="openExportFolder"
|
||||||
</n-tab-pane>
|
>打开抽帧文件夹</n-button
|
||||||
<n-tab-pane name="inGetFrame" tab="软件内抽帧">
|
>
|
||||||
<div style="color: red; font-size: large;">
|
</div>
|
||||||
注意:该功能需要对环境要求较高。(环境需要自行安装,第一次执行需要下载模型需要魔法)<span class="url_class" @click="OpenTeachDoc">环境安装教程</span>
|
</n-space>
|
||||||
<br>
|
</n-spin>
|
||||||
分割敏感度:值越小分割越精细,值越大分割越粗糙
|
<n-divider />
|
||||||
</div>
|
</n-tab-pane>
|
||||||
<n-form style="margin-top: 50px;" ref="formRef" label-placement="left" inline :model="frameValue"
|
<n-tab-pane name="inGetFrame" tab="软件内抽帧">
|
||||||
:rules="rules" size="medium">
|
<div style="color: red; font-size: large">
|
||||||
<n-form-item label="分割敏感度" path="sensitivity" style="width: 300px;">
|
注意:该功能需要对环境要求较高。(环境需要自行安装,第一次执行需要下载模型需要魔法)<span
|
||||||
<n-slider v-model:value=frameValue.sensitivity show-tooltip />
|
class="url_class"
|
||||||
</n-form-item>
|
@click="OpenTeachDoc"
|
||||||
<n-form-item label="分镜视频地址" path="video_path">
|
>环境安装教程</span
|
||||||
<n-input v-model:value="frameValue.video_path" placeholder="选择或输入要分镜的视频地址" style="width: 300px;" />
|
>
|
||||||
<n-button type="primary" @click="GetVideoFile">
|
<br />
|
||||||
选择文件(MP4)
|
分割敏感度:值越小分割越精细,值越大分割越粗糙
|
||||||
</n-button>
|
</div>
|
||||||
</n-form-item>
|
<n-form
|
||||||
<n-form-item>
|
style="margin-top: 50px"
|
||||||
<n-button type="info" @click="StartStoryboarding" :loading="storyLoading">
|
ref="formRef"
|
||||||
开始分镜
|
label-placement="left"
|
||||||
</n-button>
|
inline
|
||||||
</n-form-item>
|
:model="frameValue"
|
||||||
</n-form>
|
:rules="rules"
|
||||||
<n-divider />
|
size="medium"
|
||||||
</n-tab-pane>
|
>
|
||||||
</n-tabs>
|
<n-form-item label="分割敏感度" path="sensitivity" style="width: 300px">
|
||||||
<n-code style="padding: 0; font-size: small;" :code="code" language="js" word-wrap />
|
<n-slider v-model:value="frameValue.sensitivity" show-tooltip />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="分镜视频地址" path="video_path">
|
||||||
|
<n-input
|
||||||
|
v-model:value="frameValue.video_path"
|
||||||
|
placeholder="选择或输入要分镜的视频地址"
|
||||||
|
style="width: 300px"
|
||||||
|
/>
|
||||||
|
<n-button type="primary" @click="GetVideoFile"> 选择文件(MP4) </n-button>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item>
|
||||||
|
<n-button type="info" @click="StartStoryboarding" :loading="storyLoading">
|
||||||
|
开始分镜
|
||||||
|
</n-button>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item>
|
||||||
|
<n-button type="info" @click="openExportFolder" :loading="storyLoading">
|
||||||
|
打开抽帧文件夹
|
||||||
|
</n-button>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<n-divider />
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
<n-code style="padding: 0; font-size: small" :code="code" language="js" word-wrap />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, ref, onMounted, toRaw } from "vue";
|
import { defineComponent, ref, onMounted, toRaw } from 'vue'
|
||||||
import { NSelect, NButton, useMessage, NSpin, NDivider, NCode, NTabs, NTabPane, NSpace, NForm, NFormItem, NSlider, NInput } from "naive-ui"
|
import {
|
||||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
NSelect,
|
||||||
|
NButton,
|
||||||
|
useMessage,
|
||||||
|
NSpin,
|
||||||
|
NDivider,
|
||||||
|
NCode,
|
||||||
|
NTabs,
|
||||||
|
NTabPane,
|
||||||
|
NSpace,
|
||||||
|
NForm,
|
||||||
|
NFormItem,
|
||||||
|
NSlider,
|
||||||
|
NInput
|
||||||
|
} from 'naive-ui'
|
||||||
|
import { DEFINE_STRING } from '../../../../define/define_string.js'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
NSelect, NButton, NSpin, NDivider, NCode, NTabs, NTabPane, NSpace, NForm, NFormItem, NSlider, NInput
|
NSelect,
|
||||||
},
|
NButton,
|
||||||
setup() {
|
NSpin,
|
||||||
let options = [];
|
NDivider,
|
||||||
let out_dir = ref(null);
|
NCode,
|
||||||
let selectedValue = ref(null);
|
NTabs,
|
||||||
let show = ref(false);
|
NTabPane,
|
||||||
let code = ref("");
|
NSpace,
|
||||||
const message = useMessage();
|
NForm,
|
||||||
let storyLoading = ref(false);
|
NFormItem,
|
||||||
|
NSlider,
|
||||||
|
NInput
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
let options = []
|
||||||
|
let out_dir = ref(null)
|
||||||
|
let selectedValue = ref(null)
|
||||||
|
let show = ref(false)
|
||||||
|
let code = ref('')
|
||||||
|
const message = useMessage()
|
||||||
|
let storyLoading = ref(false)
|
||||||
|
|
||||||
let frameValue = ref({
|
let frameValue = ref({
|
||||||
sensitivity: 30,
|
sensitivity: 30,
|
||||||
video_path: null
|
video_path: null
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.api.getDraftFileList((value) => {
|
||||||
|
value.forEach((element) => {
|
||||||
|
let obj = {
|
||||||
|
label: element,
|
||||||
|
value: element
|
||||||
|
}
|
||||||
|
options.push(obj)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
onMounted(() => {
|
window.api.setEventListen([DEFINE_STRING.GET_FRAME_RETUN], (value) => {
|
||||||
window.api.getDraftFileList((value) => {
|
if (value.code == 0) {
|
||||||
value.forEach(element => {
|
message.error(value.message)
|
||||||
let obj = {
|
code.value = code.value + '\n' + value.data
|
||||||
label: element,
|
return
|
||||||
value: element
|
|
||||||
}
|
|
||||||
options.push(obj)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
window.api.setEventListen([DEFINE_STRING.GET_FRAME_RETUN], (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
code.value = code.value + "\n" + value.data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 完成
|
|
||||||
if (value.type == 0) {
|
|
||||||
storyLoading.value = false;
|
|
||||||
message.success("分镜抽帧完成");
|
|
||||||
}
|
|
||||||
code.value = code.value + "\n" + value.data;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
async function getFrameFunc(e) {
|
|
||||||
out_dir = window.config.project_path
|
|
||||||
if (selectedValue.value == null || selectedValue.value == undefined) {
|
|
||||||
message.error("请选择剪映草稿和输出草稿")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
show.value = true
|
|
||||||
// 抽帧
|
|
||||||
await window.api.getFrame([selectedValue.value, out_dir], (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error("抽帧失败");
|
|
||||||
code.value = value.message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success("抽帧成功");
|
|
||||||
code.value = value.data;
|
|
||||||
show.value = false;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
// 完成
|
||||||
function selectExportFolder(e) {
|
if (value.type == 0) {
|
||||||
window.api.selectFolder(null, (value) => {
|
storyLoading.value = false
|
||||||
if (value.length <= 0) {
|
message.success('分镜抽帧完成')
|
||||||
message.error("必须选择输出文件夹位置");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
out_dir.value = value[0]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
code.value = code.value + '\n' + value.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
async function getFrameFunc(e) {
|
||||||
* 选择指定的视频文件
|
out_dir = window.config.project_path
|
||||||
*/
|
if (selectedValue.value == null || selectedValue.value == undefined) {
|
||||||
async function GetVideoFile() {
|
message.error('请选择剪映草稿和输出草稿')
|
||||||
await window.api.SelectFile(['mp4'], (value) => {
|
return
|
||||||
debugger
|
}
|
||||||
if (value.code == 0) {
|
show.value = true
|
||||||
message.error(value.message);
|
// 抽帧
|
||||||
return;
|
await window.api.getFrame([selectedValue.value, out_dir], (value) => {
|
||||||
}
|
if (value.code == 0) {
|
||||||
frameValue.value.video_path = value.value;
|
message.error('抽帧失败')
|
||||||
});
|
code.value = value.message
|
||||||
}
|
return
|
||||||
|
|
||||||
/**
|
|
||||||
* 开始分镜执行分镜任务
|
|
||||||
*/
|
|
||||||
async function StartStoryboarding() {
|
|
||||||
storyLoading.value = true;
|
|
||||||
if (frameValue.value.video_path == null) {
|
|
||||||
message.error("选择分镜的视频地址");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugger
|
|
||||||
if (toRaw(frameValue.value).video_path.split('.')[toRaw(frameValue.value).video_path.split('.').length - 1].toUpperCase() != "MP4") {
|
|
||||||
message.error("目前只支持MP4格式");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await window.api.StartStoryboarding(toRaw(frameValue.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开环境安装网站
|
|
||||||
*/
|
|
||||||
function OpenTeachDoc() {
|
|
||||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/docx/VrBVd2KUDosmNfxat3OceWuInjd?from=from_copylink");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
selectedValue,
|
|
||||||
options,
|
|
||||||
out_dir,
|
|
||||||
getFrameFunc,
|
|
||||||
selectExportFolder,
|
|
||||||
show,
|
|
||||||
code,
|
|
||||||
frameValue,
|
|
||||||
GetVideoFile,
|
|
||||||
StartStoryboarding,
|
|
||||||
OpenTeachDoc,
|
|
||||||
storyLoading
|
|
||||||
}
|
}
|
||||||
|
message.success('抽帧成功')
|
||||||
|
code.value = value.data
|
||||||
|
show.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
function selectExportFolder(e) {
|
||||||
|
window.api.selectFolder(null, (value) => {
|
||||||
|
if (value.length <= 0) {
|
||||||
|
message.error('必须选择输出文件夹位置')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out_dir.value = value[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择指定的视频文件
|
||||||
|
*/
|
||||||
|
async function GetVideoFile() {
|
||||||
|
await window.api.SelectFile(['mp4'], (value) => {
|
||||||
|
debugger
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
frameValue.value.video_path = value.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始分镜执行分镜任务
|
||||||
|
*/
|
||||||
|
async function StartStoryboarding() {
|
||||||
|
storyLoading.value = true
|
||||||
|
if (frameValue.value.video_path == null) {
|
||||||
|
message.error('选择分镜的视频地址')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
debugger
|
||||||
|
if (
|
||||||
|
toRaw(frameValue.value)
|
||||||
|
.video_path.split('.')
|
||||||
|
[toRaw(frameValue.value).video_path.split('.').length - 1].toUpperCase() != 'MP4'
|
||||||
|
) {
|
||||||
|
message.error('目前只支持MP4格式')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await window.api.StartStoryboarding(toRaw(frameValue.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开环境安装网站
|
||||||
|
*/
|
||||||
|
function OpenTeachDoc() {
|
||||||
|
window.api.OpenUrl(
|
||||||
|
'https://pvwu1oahp5m.feishu.cn/docx/VrBVd2KUDosmNfxat3OceWuInjd?from=from_copylink'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openExportFolder() {
|
||||||
|
window.api.OpenFolder("input_crop")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedValue,
|
||||||
|
options,
|
||||||
|
out_dir,
|
||||||
|
getFrameFunc,
|
||||||
|
selectExportFolder,
|
||||||
|
show,
|
||||||
|
code,
|
||||||
|
frameValue,
|
||||||
|
GetVideoFile,
|
||||||
|
StartStoryboarding,
|
||||||
|
OpenTeachDoc,
|
||||||
|
storyLoading,
|
||||||
|
openExportFolder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.url_class {
|
.url_class {
|
||||||
color: blue;
|
color: blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.url_class:hover {
|
.url_class:hover {
|
||||||
color: brown;
|
color: brown;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,99 +1,123 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-spin :show="show">
|
<n-spin :show="show">
|
||||||
<template #description>
|
<template #description> 正在反推 </template>
|
||||||
正在反推
|
<n-button type="info" @click="RemoveWather"> 去水印 </n-button>
|
||||||
</template>
|
<n-button type="info" style="margin-left: 20px" @click="PushBackPrompt"> 开始反推 </n-button>
|
||||||
<n-button type="info" @click="PushBackPrompt"> 开始反推 </n-button>
|
<n-button type="error" style="margin-left: 20px" @click="DeleteBadPrompt">
|
||||||
<n-button type="error" style="margin-left: 50px;" @click="DeleteBadPrompt"> 一键去除所有不想要的值 </n-button>
|
一键去除所有不想要的值
|
||||||
<n-button type="warning" style="margin-left: 50px;" @click="ManagePrompt"> 管理不想要的值 </n-button>
|
</n-button>
|
||||||
<n-divider />
|
<n-button type="warning" style="margin-left: 20px" @click="ManagePrompt">
|
||||||
</n-spin>
|
管理不想要的值
|
||||||
<n-code style="padding: 0;" :code="code" language="js" word-wrap />
|
</n-button>
|
||||||
|
<n-divider />
|
||||||
|
</n-spin>
|
||||||
|
<n-code style="padding: 0" :code="code" language="js" word-wrap />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, ref, onMounted, h, toRaw } from "vue";
|
import { defineComponent, ref, onMounted, h, toRaw } from 'vue'
|
||||||
import { NButton, useMessage, useDialog, NDivider, NCode, NSpin } from "naive-ui"
|
import { NButton, useMessage, useDialog, NDivider, NCode, NSpin } from 'naive-ui'
|
||||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||||
import ManageBadPrompt from "../Components/ManageBadPrompt.vue"
|
import ManageBadPrompt from '../Components/ManageBadPrompt.vue'
|
||||||
|
import GetWaterMask from '../Watermark/GetWaterMask.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
NButton, NDivider, NCode, NSpin
|
NButton,
|
||||||
},
|
NDivider,
|
||||||
setup() {
|
NCode,
|
||||||
let dialog = useDialog();
|
NSpin
|
||||||
let code = ref("");
|
},
|
||||||
let show = ref(false);
|
setup() {
|
||||||
let promprRef = ref();
|
let dialog = useDialog()
|
||||||
let message = useMessage();
|
let code = ref('')
|
||||||
|
let show = ref(false)
|
||||||
|
let promprRef = ref()
|
||||||
|
let message = useMessage()
|
||||||
|
|
||||||
// 开始反推
|
// 开始反推
|
||||||
async function PushBackPrompt() {
|
async function PushBackPrompt() {
|
||||||
show.value = true;
|
show.value = true
|
||||||
await window.api.PushBackPrompt((value) => {
|
await window.api.PushBackPrompt((value) => {
|
||||||
if (value.code == 0) {
|
if (value.code == 0) {
|
||||||
message.error("反推报错");
|
message.error('反推报错')
|
||||||
code.value = value.message;
|
code.value = value.message
|
||||||
return;
|
return
|
||||||
}
|
|
||||||
message.success("反推成功");
|
|
||||||
code.value = value.data;
|
|
||||||
show.value = false;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理不想要的值
|
|
||||||
*/
|
|
||||||
async function ManagePrompt() {
|
|
||||||
// 弹窗,管理不想要的值
|
|
||||||
dialog.create({
|
|
||||||
showIcon: false,
|
|
||||||
title: "管理不想要的值",
|
|
||||||
content: () => h(ManageBadPrompt, { ref: promprRef }),
|
|
||||||
style: `width : 650px;`,
|
|
||||||
maskClosable: false,
|
|
||||||
positiveText: "确定",
|
|
||||||
negativeText: "取消",
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
console.log(toRaw(promprRef.value.tags));
|
|
||||||
// 将数据写入
|
|
||||||
await window.api.SaveBadPrompt(toRaw(promprRef.value.tags), (value) => {
|
|
||||||
console.log(value)
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
message.success("添加成功");
|
|
||||||
})
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一键删除不想要的值
|
|
||||||
*/
|
|
||||||
async function DeleteBadPrompt() {
|
|
||||||
await window.api.DeleteBadPrompt((value) => {
|
|
||||||
debugger
|
|
||||||
console.log(value);
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success("去除成功");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
code,
|
|
||||||
show,
|
|
||||||
PushBackPrompt,
|
|
||||||
ManagePrompt,
|
|
||||||
DeleteBadPrompt
|
|
||||||
}
|
}
|
||||||
|
message.success('反推成功')
|
||||||
|
code.value = value.data
|
||||||
|
show.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理不想要的值
|
||||||
|
*/
|
||||||
|
async function ManagePrompt() {
|
||||||
|
// 弹窗,管理不想要的值
|
||||||
|
dialog.create({
|
||||||
|
showIcon: false,
|
||||||
|
title: '管理不想要的值',
|
||||||
|
content: () => h(ManageBadPrompt, { ref: promprRef }),
|
||||||
|
style: `width : 650px;`,
|
||||||
|
maskClosable: false,
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
console.log(toRaw(promprRef.value.tags))
|
||||||
|
// 将数据写入
|
||||||
|
await window.api.SaveBadPrompt(toRaw(promprRef.value.tags), (value) => {
|
||||||
|
console.log(value)
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
message.success('添加成功')
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一键删除不想要的值
|
||||||
|
*/
|
||||||
|
async function DeleteBadPrompt() {
|
||||||
|
await window.api.DeleteBadPrompt((value) => {
|
||||||
|
debugger
|
||||||
|
console.log(value)
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success('去除成功')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function RemoveWather() {
|
||||||
|
// 判断当前数据是不是存在
|
||||||
|
// 处理数据。获取当前的所有的数据
|
||||||
|
let dialogWidth = window.innerWidth * 0.7
|
||||||
|
let dialogHeight = window.innerHeight * 0.9
|
||||||
|
// ImportWordAndSrt
|
||||||
|
dialog.create({
|
||||||
|
showIcon: false,
|
||||||
|
closeOnEsc: false,
|
||||||
|
content: () => h(GetWaterMask, { width: dialogWidth, height: dialogHeight }),
|
||||||
|
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||||
|
maskClosable: false,
|
||||||
|
onClose: () => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code,
|
||||||
|
show,
|
||||||
|
PushBackPrompt,
|
||||||
|
ManagePrompt,
|
||||||
|
DeleteBadPrompt,
|
||||||
|
RemoveWather
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
50
src/renderer/src/components/Components/ProgressDialog.vue
Normal file
50
src/renderer/src/components/Components/ProgressDialog.vue
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<n-progress type="circle" :percentage="percentage">
|
||||||
|
<span style="text-align: center; font-size: 20px">{{ text }}</span>
|
||||||
|
</n-progress>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
|
||||||
|
import { NProgress, useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
NProgress
|
||||||
|
},
|
||||||
|
props: ['tatol', 'current'],
|
||||||
|
setup(props) {
|
||||||
|
let text = ref('等待中')
|
||||||
|
let percentage = ref(0)
|
||||||
|
let tatol = ref(props.tatol ? props.tatol : 1)
|
||||||
|
let current = ref(props.current ? props.current : 0)
|
||||||
|
|
||||||
|
// 更新数据
|
||||||
|
function modifyTotal(value) {
|
||||||
|
tatol.value = value
|
||||||
|
if (!value || value == 0) {
|
||||||
|
text.value = '等待中'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function modifyCurrent(value) {
|
||||||
|
current.value = value ? value : 0
|
||||||
|
percentage.value = Math.round((current.value / (tatol.value ? tatol.value : 1)) * 100)
|
||||||
|
if (tatol.value && tatol.value > 0) {
|
||||||
|
text.value = percentage.value + '%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {})
|
||||||
|
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
percentage,
|
||||||
|
tatol,
|
||||||
|
current,
|
||||||
|
modifyTotal,
|
||||||
|
modifyCurrent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -63,7 +63,7 @@ export default defineComponent({
|
|||||||
let data = ref(props.initData)
|
let data = ref(props.initData)
|
||||||
let AnalyzeCharacter = ref(props.Character)
|
let AnalyzeCharacter = ref(props.Character)
|
||||||
let tagTreeData = ref(props.treeData)
|
let tagTreeData = ref(props.treeData)
|
||||||
let auto_save = ref(window.config.auto_save ? window.config.auto_save : false)
|
let auto_save = ref(window.config?.auto_save ? window.config.auto_save : false)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.initData,
|
() => props.initData,
|
||||||
|
|||||||
@ -1,274 +1,318 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-tabs class="card-tabs" default-value="signin" size="large" animated pane-wrapper-style="margin: 0 -4px"
|
<n-tabs
|
||||||
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;">
|
class="card-tabs"
|
||||||
<n-tab-pane name="signin" tab="GPT服务商">
|
default-value="signin"
|
||||||
<n-form ref="formRef" :model="gpt_select_value" :rules="rules">
|
size="large"
|
||||||
<n-form-item label="选择自定义的GPT服务商">
|
animated
|
||||||
<n-select style="width: 300px;" :options="gpt_options"
|
pane-wrapper-style="margin: 0 -4px"
|
||||||
v-model:value="gpt_select_key"></n-select>
|
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;"
|
||||||
<n-button color="#e18a3b" style="margin-left: 10px;" @click="EditOption">编辑</n-button>
|
>
|
||||||
<n-button color="#984f31" style="margin-left: 10px;" @click="AddOption">添加</n-button>
|
<n-tab-pane name="signin" tab="GPT服务商">
|
||||||
<n-button type="error" style="margin-left: 10px;" @click="DeleteOption">删除</n-button>
|
<n-form ref="formRef" :model="gpt_select_value" :rules="rules">
|
||||||
</n-form-item>
|
<n-form-item label="选择自定义的GPT服务商">
|
||||||
<n-form-item path="label" label="GPT 名称">
|
<n-select
|
||||||
<n-input v-model:value="gpt_select_value.label"></n-input>
|
style="width: 300px"
|
||||||
</n-form-item>
|
:options="gpt_options"
|
||||||
<n-form-item path="value" label="GPT请求网址(写全)">
|
v-model:value="gpt_select_key"
|
||||||
<n-input v-model:value="gpt_select_value.gpt_url"></n-input>
|
></n-select>
|
||||||
</n-form-item>
|
<n-button color="#e18a3b" style="margin-left: 10px" @click="EditOption">编辑</n-button>
|
||||||
<div style="text-align: right;">
|
<n-button color="#984f31" style="margin-left: 10px" @click="AddOption">添加</n-button>
|
||||||
<n-button type="primary" @click="SaveGptOption">保存</n-button>
|
<n-button type="error" style="margin-left: 10px" @click="DeleteOption">删除</n-button>
|
||||||
</div>
|
</n-form-item>
|
||||||
</n-form>
|
<n-form-item path="label" label="GPT 名称">
|
||||||
</n-tab-pane>
|
<n-input v-model:value="gpt_select_value.label"></n-input>
|
||||||
<n-tab-pane name="signup" tab="GPT模型">
|
</n-form-item>
|
||||||
<n-form ref="modelFormRef" :model="gpt_model_select_value" :rules="modelRules">
|
<n-form-item path="value" label="GPT请求网址(写全)">
|
||||||
<n-form-item label="选择自定义的GPT模型">
|
<n-input v-model:value="gpt_select_value.gpt_url"></n-input>
|
||||||
<n-select style="width: 300px;" :options="gpt_model_options"
|
</n-form-item>
|
||||||
v-model:value="gpt_model_select_key"></n-select>
|
<div style="text-align: right">
|
||||||
<n-button color="#e18a3b" style="margin-left: 10px;" @click="EditModelOption">编辑</n-button>
|
<n-button type="primary" @click="SaveGptOption">保存</n-button>
|
||||||
<n-button color="#984f31" style="margin-left: 10px;" @click="AddModelOption">添加</n-button>
|
</div>
|
||||||
<n-button type="error" style="margin-left: 10px;" @click="DeleteModelOption">删除</n-button>
|
</n-form>
|
||||||
</n-form-item>
|
</n-tab-pane>
|
||||||
<n-form-item path="label" label="GPT 模型名称">
|
<n-tab-pane name="signup" tab="GPT模型">
|
||||||
<n-input v-model:value="gpt_model_select_value.label"></n-input>
|
<n-form ref="modelFormRef" :model="gpt_model_select_value" :rules="modelRules">
|
||||||
</n-form-item>
|
<n-form-item label="选择自定义的GPT模型">
|
||||||
<n-form-item path="value" label="GPT 模型值">
|
<n-select
|
||||||
<n-input v-model:value="gpt_model_select_value.value"></n-input>
|
style="width: 300px"
|
||||||
</n-form-item>
|
:options="gpt_model_options"
|
||||||
<div style="text-align: right;">
|
v-model:value="gpt_model_select_key"
|
||||||
<n-button type="primary" @click="SaveGptModelOption">保存</n-button>
|
></n-select>
|
||||||
</div>
|
<n-button color="#e18a3b" style="margin-left: 10px" @click="EditModelOption"
|
||||||
</n-form>
|
>编辑</n-button
|
||||||
</n-tab-pane>
|
>
|
||||||
</n-tabs>
|
<n-button color="#984f31" style="margin-left: 10px" @click="AddModelOption"
|
||||||
</div>
|
>添加</n-button
|
||||||
|
>
|
||||||
|
<n-button type="error" style="margin-left: 10px" @click="DeleteModelOption"
|
||||||
|
>删除</n-button
|
||||||
|
>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item path="label" label="GPT 模型名称">
|
||||||
|
<n-input v-model:value="gpt_model_select_value.label"></n-input>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item path="value" label="GPT 模型值">
|
||||||
|
<n-input v-model:value="gpt_model_select_value.value"></n-input>
|
||||||
|
</n-form-item>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<n-button type="primary" @click="SaveGptModelOption">保存</n-button>
|
||||||
|
</div>
|
||||||
|
</n-form>
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, toRaw, onMounted, defineComponent } from "vue"
|
import { ref, toRaw, onMounted, defineComponent } from 'vue'
|
||||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, useMessage, NSelect, NTabs, NTabPane } from "naive-ui"
|
import {
|
||||||
import { cloneDeep } from "lodash";
|
NForm,
|
||||||
|
NFormItem,
|
||||||
|
NInput,
|
||||||
|
NInputNumber,
|
||||||
|
useDialog,
|
||||||
|
NButton,
|
||||||
|
useMessage,
|
||||||
|
NSelect,
|
||||||
|
NTabs,
|
||||||
|
NTabPane
|
||||||
|
} from 'naive-ui'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSelect, NTabs, NTabPane
|
NForm,
|
||||||
},
|
NFormItem,
|
||||||
setup() {
|
NInput,
|
||||||
let message = useMessage();
|
NInputNumber,
|
||||||
let gpt_model_options = ref([]);
|
NButton,
|
||||||
let gpt_options = ref([]);
|
NSelect,
|
||||||
let gpt_select_value = ref({});
|
NTabs,
|
||||||
let gpt_select_key = ref("");
|
NTabPane
|
||||||
let gpt_model_select_value = ref({});
|
},
|
||||||
let gpt_model_select_key = ref("");
|
setup() {
|
||||||
let formRef = ref(null);
|
let message = useMessage()
|
||||||
let modelFormRef = ref(null);
|
let gpt_model_options = ref([])
|
||||||
|
let gpt_options = ref([])
|
||||||
|
let gpt_select_value = ref({})
|
||||||
|
let gpt_select_key = ref('')
|
||||||
|
let gpt_model_select_value = ref({})
|
||||||
|
let gpt_model_select_key = ref('')
|
||||||
|
let formRef = ref(null)
|
||||||
|
let modelFormRef = ref(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载GPT的配置信息
|
* 加载GPT的配置信息
|
||||||
*/
|
*/
|
||||||
async function InitGptOptions() {
|
async function InitGptOptions() {
|
||||||
await window.api.getGptBusinessOption("dynamic", (value) => {
|
await window.api.getGptBusinessOption('dynamic', (value) => {
|
||||||
if (value.code == 0) {
|
if (value.code == 0) {
|
||||||
message.error(value.message);
|
message.error(value.message)
|
||||||
return;
|
return
|
||||||
}
|
|
||||||
if (value.data.length == 0) {
|
|
||||||
message.error("请先添加自定义GPT服务商");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gpt_options.value = value.data;
|
|
||||||
gpt_select_key.value = value.data[0].value;
|
|
||||||
gpt_select_value.value = {
|
|
||||||
label: null,
|
|
||||||
value: null
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
await window.api.getGptModelOption("dynamic", (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value.data.length == 0) {
|
|
||||||
message.error("请先添加自定义GPT模型");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gpt_model_options.value = value.data;
|
|
||||||
gpt_model_select_key.value = value.data[0].value;
|
|
||||||
gpt_model_select_value.value = {
|
|
||||||
label: null,
|
|
||||||
value: null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
if (value.data.length == 0) {
|
||||||
await InitGptOptions();
|
message.error('请先添加自定义GPT服务商')
|
||||||
})
|
return
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存当前的GPT配置信息
|
|
||||||
*/
|
|
||||||
async function SaveGptOption(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
formRef.value?.validate(async (errors) => {
|
|
||||||
if (errors) {
|
|
||||||
message.error("请检查必填字段");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 保存逻辑
|
|
||||||
await window.api.SaveDynamicGptOption([JSON.stringify(toRaw(gpt_select_value.value)), "gpt_options"], async (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success("添加成功");
|
|
||||||
await InitGptOptions();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
gpt_options.value = value.data
|
||||||
/**
|
gpt_select_key.value = value.data[0].value
|
||||||
* 将当前选中的GPT配置信息,写入下面已编辑
|
gpt_select_value.value = {
|
||||||
*/
|
label: null,
|
||||||
async function EditOption() {
|
value: null,
|
||||||
let temp = gpt_options.value.find(x => x.value == gpt_select_key.value);
|
gpt_url: null
|
||||||
gpt_select_value.value = cloneDeep(temp);
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
await window.api.getGptModelOption('dynamic', (value) => {
|
||||||
* 将当前选中的GPT模型配置信息,写入下面已编辑
|
if (value.code == 0) {
|
||||||
*/
|
message.error(value.message)
|
||||||
async function EditModelOption() {
|
return
|
||||||
let temp = gpt_model_options.value.find(x => x.value == gpt_model_select_key.value);
|
|
||||||
gpt_model_select_value.value = cloneDeep(temp);
|
|
||||||
}
|
}
|
||||||
|
if (value.data.length == 0) {
|
||||||
/**
|
message.error('请先添加自定义GPT模型')
|
||||||
* 删除当前选中的GPT服务商信息
|
return
|
||||||
*/
|
|
||||||
async function DeleteOption() {
|
|
||||||
let temp = gpt_options.value.find(x => x.value == gpt_select_key.value);
|
|
||||||
let id = temp.id;
|
|
||||||
await window.api.DeleteDynamicGptOption([id, "gpt_options"], async (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success("删除成功");
|
|
||||||
await InitGptOptions();
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
gpt_model_options.value = value.data
|
||||||
/**
|
gpt_model_select_key.value = value.data[0].value
|
||||||
* 删除当前选中的GPT服务商信息
|
gpt_model_select_value.value = {
|
||||||
*/
|
label: null,
|
||||||
async function DeleteModelOption() {
|
value: null
|
||||||
let temp = gpt_model_options.value.find(x => x.value == gpt_model_select_key.value);
|
|
||||||
let id = temp.id;
|
|
||||||
await window.api.DeleteDynamicGptOption([id, "gpt_model_options"], async (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success("删除成功");
|
|
||||||
await InitGptOptions();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存当前的GPT配置信息
|
|
||||||
*/
|
|
||||||
async function SaveGptModelOption(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
modelFormRef.value?.validate(async (errors) => {
|
|
||||||
if (errors) {
|
|
||||||
message.error("请检查必填字段");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 保存逻辑
|
|
||||||
await window.api.SaveDynamicGptOption([JSON.stringify(toRaw(gpt_model_select_value.value)), "gpt_model_options"], async (value) => {
|
|
||||||
if (value.code == 0) {
|
|
||||||
message.error(value.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success("添加成功");
|
|
||||||
await InitGptOptions();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加一个GPT服务商,只是清楚当前的数据
|
|
||||||
*/
|
|
||||||
async function AddOption() {
|
|
||||||
gpt_select_value.value = {
|
|
||||||
label: null,
|
|
||||||
value: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加一个GPT服务商,只是清楚当前的数据
|
|
||||||
*/
|
|
||||||
async function AddModelOption() {
|
|
||||||
gpt_model_select_value.value = {
|
|
||||||
label: null,
|
|
||||||
value: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let ruleObj = (errorMessage) => {
|
|
||||||
return [{
|
|
||||||
required: true,
|
|
||||||
validator(rule, value) {
|
|
||||||
if (value == null || value == "")
|
|
||||||
return new Error(errorMessage);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
trigger: ["input", "blur"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
let rules = {
|
|
||||||
label: ruleObj("必填GPT名称"),
|
|
||||||
gpt_url: ruleObj("必填GPT请求网址"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let modelRules = {
|
|
||||||
label: ruleObj("必填GPT名称"),
|
|
||||||
value: ruleObj("必填GPT请求网址"),
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
gpt_model_options,
|
|
||||||
gpt_options,
|
|
||||||
gpt_select_value,
|
|
||||||
gpt_model_select_value,
|
|
||||||
gpt_select_key,
|
|
||||||
gpt_model_select_key,
|
|
||||||
SaveGptOption,
|
|
||||||
EditOption,
|
|
||||||
DeleteOption,
|
|
||||||
AddOption,
|
|
||||||
SaveGptModelOption,
|
|
||||||
rules,
|
|
||||||
modelRules,
|
|
||||||
formRef,
|
|
||||||
modelFormRef,
|
|
||||||
EditModelOption,
|
|
||||||
AddModelOption,
|
|
||||||
DeleteModelOption
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
onMounted(async () => {
|
||||||
|
await InitGptOptions()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存当前的GPT配置信息
|
||||||
|
*/
|
||||||
|
async function SaveGptOption(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
formRef.value?.validate(async (errors) => {
|
||||||
|
if (errors) {
|
||||||
|
message.error('请检查必填字段')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 保存逻辑
|
||||||
|
await window.api.SaveDynamicGptOption(
|
||||||
|
[JSON.stringify(toRaw(gpt_select_value.value)), 'gpt_options'],
|
||||||
|
async (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success('添加成功')
|
||||||
|
await InitGptOptions()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将当前选中的GPT配置信息,写入下面已编辑
|
||||||
|
*/
|
||||||
|
async function EditOption() {
|
||||||
|
let temp = gpt_options.value.find((x) => x.value == gpt_select_key.value)
|
||||||
|
gpt_select_value.value = cloneDeep(temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将当前选中的GPT模型配置信息,写入下面已编辑
|
||||||
|
*/
|
||||||
|
async function EditModelOption() {
|
||||||
|
let temp = gpt_model_options.value.find((x) => x.value == gpt_model_select_key.value)
|
||||||
|
gpt_model_select_value.value = cloneDeep(temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除当前选中的GPT服务商信息
|
||||||
|
*/
|
||||||
|
async function DeleteOption() {
|
||||||
|
let temp = gpt_options.value.find((x) => x.value == gpt_select_key.value)
|
||||||
|
let id = temp.id
|
||||||
|
await window.api.DeleteDynamicGptOption([id, 'gpt_options'], async (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success('删除成功')
|
||||||
|
await InitGptOptions()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除当前选中的GPT服务商信息
|
||||||
|
*/
|
||||||
|
async function DeleteModelOption() {
|
||||||
|
let temp = gpt_model_options.value.find((x) => x.value == gpt_model_select_key.value)
|
||||||
|
let id = temp.id
|
||||||
|
await window.api.DeleteDynamicGptOption([id, 'gpt_model_options'], async (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success('删除成功')
|
||||||
|
await InitGptOptions()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存当前的GPT配置信息
|
||||||
|
*/
|
||||||
|
async function SaveGptModelOption(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
modelFormRef.value?.validate(async (errors) => {
|
||||||
|
if (errors) {
|
||||||
|
message.error('请检查必填字段')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 保存逻辑
|
||||||
|
await window.api.SaveDynamicGptOption(
|
||||||
|
[JSON.stringify(toRaw(gpt_model_select_value.value)), 'gpt_model_options'],
|
||||||
|
async (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success('添加成功')
|
||||||
|
await InitGptOptions()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个GPT服务商,只是清楚当前的数据
|
||||||
|
*/
|
||||||
|
async function AddOption() {
|
||||||
|
gpt_select_value.value = {
|
||||||
|
label: null,
|
||||||
|
value: null,
|
||||||
|
gpt_url: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个GPT服务商,只是清楚当前的数据
|
||||||
|
*/
|
||||||
|
async function AddModelOption() {
|
||||||
|
gpt_model_select_value.value = {
|
||||||
|
label: null,
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ruleObj = (errorMessage) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator(rule, value) {
|
||||||
|
if (value == null || value == '') return new Error(errorMessage)
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
trigger: ['input', 'blur']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let rules = {
|
||||||
|
label: ruleObj('必填GPT名称'),
|
||||||
|
gpt_url: ruleObj('必填GPT请求网址')
|
||||||
|
}
|
||||||
|
|
||||||
|
let modelRules = {
|
||||||
|
label: ruleObj('必填GPT名称'),
|
||||||
|
value: ruleObj('必填GPT请求网址')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
gpt_model_options,
|
||||||
|
gpt_options,
|
||||||
|
gpt_select_value,
|
||||||
|
gpt_model_select_value,
|
||||||
|
gpt_select_key,
|
||||||
|
gpt_model_select_key,
|
||||||
|
SaveGptOption,
|
||||||
|
EditOption,
|
||||||
|
DeleteOption,
|
||||||
|
AddOption,
|
||||||
|
SaveGptModelOption,
|
||||||
|
rules,
|
||||||
|
modelRules,
|
||||||
|
formRef,
|
||||||
|
modelFormRef,
|
||||||
|
EditModelOption,
|
||||||
|
AddModelOption,
|
||||||
|
DeleteModelOption
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.card-tabs .n-tabs-nav--bar-type {
|
.card-tabs .n-tabs-nav--bar-type {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="出图方式(文生图、图生图)" path="type">
|
<n-form-item label="出图方式(文生图、图生图)" path="type">
|
||||||
<n-select
|
<n-select
|
||||||
style="width: 250px"
|
style="width: 250px"
|
||||||
v-model:value="formValue.type"
|
v-model:value="formValue.type"
|
||||||
:options="modelOption"
|
:options="modelOption"
|
||||||
placeholder="选择出图方式"
|
placeholder="选择出图方式"
|
||||||
|
|||||||
555
src/renderer/src/components/Watermark/GetWaterMask.vue
Normal file
555
src/renderer/src/components/Watermark/GetWaterMask.vue
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
<template>
|
||||||
|
<div style="margin-top: 30px">
|
||||||
|
<div style="display: flex; margin-right: 10px">
|
||||||
|
<ImageFileSelect :modifyImage="modifyImage"></ImageFileSelect>
|
||||||
|
<div style="width: 250px; margin-left: 10px">
|
||||||
|
<n-checkbox size="small" v-model:checked="mask_setting.isRemote"> 使用iopaint </n-checkbox>
|
||||||
|
<!-- <div>
|
||||||
|
<n-checkbox v-model:checked="mask_setting.isRemote"> 软件内去除 </n-checkbox>
|
||||||
|
</div> -->
|
||||||
|
<div style="display: flex; justify-content: flex-end; align-items: flex-end">
|
||||||
|
<n-input
|
||||||
|
size="tiny"
|
||||||
|
style="margin-top: 5px"
|
||||||
|
v-model:value="mask_setting.localUrl"
|
||||||
|
placeholder="请输入本地iopaint地址"
|
||||||
|
></n-input>
|
||||||
|
<n-button type="success" @click="SaveMaskConfig" size="tiny">保存</n-button>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px">
|
||||||
|
<span class="url_class" @click="OpenTeach('lama')">lama安装教程</span>
|
||||||
|
<span class="url_class" @click="OpenTeach('iopaint')" style="margin-left: 5px"
|
||||||
|
>iopaint安装教程</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style="margin-top: 10px; display: flex; justify-content: center; align-items: center"
|
||||||
|
id="cnvas_ss"
|
||||||
|
>
|
||||||
|
<!-- <canvas ref="imageCanvas" /> -->
|
||||||
|
<canvas
|
||||||
|
ref="imageCanvas"
|
||||||
|
:style="{
|
||||||
|
cursor: 'grab',
|
||||||
|
clipPath: `inset(0 ${sliderPos}% 0 0)`,
|
||||||
|
transition: `clip-path 300ms`
|
||||||
|
}"
|
||||||
|
@mousemove="draw"
|
||||||
|
@mouseup="stopDrawing"
|
||||||
|
@mousedown="startDrawing"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; display: flex; justify-content: center; align-items: center">
|
||||||
|
<n-slider v-model:value="radius" :step="1" style="width: 120px" />
|
||||||
|
<n-button style="margin-left: 10px" @click="undo" type="success">回撤</n-button>
|
||||||
|
<n-button style="margin-left: 10px" @click="Check" type="success">查看效果</n-button>
|
||||||
|
<n-button style="margin-left: 10px" @click="SaveMask" type="success">保存蒙板</n-button>
|
||||||
|
<n-button style="margin-left: 10px" @click="ViewMaskImage" type="success">查看蒙板</n-button>
|
||||||
|
<n-popover trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<n-button style="margin-left: 10px" @click="BatchMask" type="success"
|
||||||
|
>开始批量处理</n-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<span>批量处理的图片的尺寸要和上面的蒙板一致</span>
|
||||||
|
</n-popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref, onMounted, h, watch, toRaw } from 'vue'
|
||||||
|
import {
|
||||||
|
NButton,
|
||||||
|
useMessage,
|
||||||
|
NInput,
|
||||||
|
NTag,
|
||||||
|
NImage,
|
||||||
|
NCheckbox,
|
||||||
|
NSlider,
|
||||||
|
NPopover,
|
||||||
|
NProgress,
|
||||||
|
useDialog,
|
||||||
|
useDialogReactiveList
|
||||||
|
} from 'naive-ui'
|
||||||
|
import ImageFileSelect from './ImageFileSelect.vue'
|
||||||
|
import ProgressDialog from '../Components/ProgressDialog.vue'
|
||||||
|
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||||
|
import { isEmpty } from 'lodash'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
NButton,
|
||||||
|
NInput,
|
||||||
|
NTag,
|
||||||
|
ImageFileSelect,
|
||||||
|
NImage,
|
||||||
|
NCheckbox,
|
||||||
|
NSlider,
|
||||||
|
NPopover,
|
||||||
|
NProgress
|
||||||
|
},
|
||||||
|
props: ['width', 'height'],
|
||||||
|
setup(props) {
|
||||||
|
let message = useMessage()
|
||||||
|
let dialog = useDialog()
|
||||||
|
const imageCanvas = ref(null)
|
||||||
|
let imageContext = ref(null)
|
||||||
|
|
||||||
|
let height = ref(props.height)
|
||||||
|
let width = ref(props.width)
|
||||||
|
|
||||||
|
let isDrawing = false
|
||||||
|
let radius = ref(20) // 圆的半径
|
||||||
|
let points = [] // 用于保存鼠标的移动轨迹
|
||||||
|
let canvasHistory = [] // 用于保存画布的历史记录
|
||||||
|
let step = 0
|
||||||
|
let mask_setting = ref({
|
||||||
|
isRemote: true,
|
||||||
|
localUrl: null,
|
||||||
|
mask_path: null
|
||||||
|
})
|
||||||
|
let progressDialogRef = ref(null)
|
||||||
|
let total = ref(0)
|
||||||
|
let current = ref(0)
|
||||||
|
let dp
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (imageCanvas.value) {
|
||||||
|
const ctx = imageCanvas.value.getContext('2d', { willReadFrequently: true })
|
||||||
|
if (ctx) {
|
||||||
|
imageContext.value = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let div = document.getElementById('cnvas_ss')
|
||||||
|
div.style.height = height.value - 220 + 'px'
|
||||||
|
|
||||||
|
await window.api.GetDefineConfigJsonByProperty(
|
||||||
|
JSON.stringify(['img_base', 'mask_setting', false, null]),
|
||||||
|
(value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 判断是不是有数据
|
||||||
|
if (value.data) {
|
||||||
|
mask_setting.value = Object.assign(mask_setting.value, value.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
window.api.setEventListen([DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT], (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total.value = value.data.total
|
||||||
|
current.value = value.data.current
|
||||||
|
if (progressDialogRef.value == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
progressDialogRef.value?.modifyTotal(total.value)
|
||||||
|
progressDialogRef.value?.modifyCurrent(current.value)
|
||||||
|
|
||||||
|
if (value.data.total == value.data.current) {
|
||||||
|
// 销毁进度组件
|
||||||
|
setTimeout(() => {
|
||||||
|
if (dp) {
|
||||||
|
dp.destroy()
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let hasImage = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图片的高度并且计算适应的宽高
|
||||||
|
* @param image
|
||||||
|
*/
|
||||||
|
function getCurrentWidthHeight(image) {
|
||||||
|
debugger
|
||||||
|
let image_width = 512
|
||||||
|
let image_height = 512
|
||||||
|
// 设置图片的大小
|
||||||
|
if (image) {
|
||||||
|
image_width = image.width
|
||||||
|
image_height = image.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断图片大小,获取当前的传入的window,判断当前的宽度和高度是不是超出了
|
||||||
|
// let min_size = Math.min(width.value,height.value);
|
||||||
|
let image_height_scale = (height.value - 220) / image_height
|
||||||
|
let image_width_scale = (width.value - 220) / image_width
|
||||||
|
|
||||||
|
let scale = Math.min(image_height_scale, image_width_scale)
|
||||||
|
return [image_width * scale, image_height * scale]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择图片初始化
|
||||||
|
* @param image_d
|
||||||
|
*/
|
||||||
|
function modifyImage(image_d) {
|
||||||
|
let [image_width, image_height] = getCurrentWidthHeight(image_d)
|
||||||
|
|
||||||
|
imageContext.value.canvas.width = image_width
|
||||||
|
imageContext.value.canvas.height = image_height
|
||||||
|
|
||||||
|
imageContext.value.drawImage(image_d, 0, 0, image_width, image_height)
|
||||||
|
canvasHistory = []
|
||||||
|
canvasHistory.push(imageCanvas.value.toDataURL('image/png'))
|
||||||
|
hasImage = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止画
|
||||||
|
*/
|
||||||
|
function stopDrawing() {
|
||||||
|
debugger
|
||||||
|
isDrawing = false
|
||||||
|
canvasHistory.push(imageCanvas.value.toDataURL('image/png'))
|
||||||
|
console.log(canvasHistory)
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算点和点直接的所有像素点
|
||||||
|
* @param x0
|
||||||
|
* @param y0
|
||||||
|
* @param x1
|
||||||
|
* @param y1
|
||||||
|
*/
|
||||||
|
function bresenhamLine(x0, y0, x1, y1) {
|
||||||
|
let dx = Math.abs(x1 - x0)
|
||||||
|
let dy = Math.abs(y1 - y0)
|
||||||
|
let sx = x0 < x1 ? 1 : -1
|
||||||
|
let sy = y0 < y1 ? 1 : -1
|
||||||
|
let err = dx - dy
|
||||||
|
|
||||||
|
let points = []
|
||||||
|
while (true) {
|
||||||
|
points.push({ x: x0, y: y0 })
|
||||||
|
|
||||||
|
if (x0 === x1 && y0 === y1) break
|
||||||
|
let e2 = 2 * err
|
||||||
|
if (e2 > -dy) {
|
||||||
|
err -= dy
|
||||||
|
x0 += sx
|
||||||
|
}
|
||||||
|
if (e2 < dx) {
|
||||||
|
err += dx
|
||||||
|
y0 += sy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充颜色
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
function draw(event) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const rect = imageContext.value.canvas.getBoundingClientRect()
|
||||||
|
const x = event.clientX - rect.left
|
||||||
|
const y = event.clientY - rect.top
|
||||||
|
|
||||||
|
if (isDrawing && hasImage) {
|
||||||
|
// 获取上一个鼠标位置
|
||||||
|
const lastPoint = points[points.length - 1]
|
||||||
|
// 使用 Bresenham's line algorithm 计算两个点之间的所有像素点
|
||||||
|
const linePoints = bresenhamLine(lastPoint.x, lastPoint.y, x, y)
|
||||||
|
for (const point of linePoints) {
|
||||||
|
// 在每个像素点的周围绘制一个圆
|
||||||
|
imageContext.value.beginPath()
|
||||||
|
imageContext.value.arc(point.x, point.y, radius.value, 0, Math.PI * 2, false)
|
||||||
|
imageContext.value.fillStyle = 'rgba(255, 255, 0)'
|
||||||
|
imageContext.value.fill()
|
||||||
|
}
|
||||||
|
// 将当前鼠标位置添加到 points 数组中
|
||||||
|
points.push({ x, y })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始话
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
function startDrawing(event) {
|
||||||
|
isDrawing = true
|
||||||
|
const rect = imageContext.value.canvas.getBoundingClientRect()
|
||||||
|
points.push({ x: event.clientX - rect.left, y: event.clientY - rect.top })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回撤
|
||||||
|
*/
|
||||||
|
async function undo() {
|
||||||
|
debugger
|
||||||
|
if (step <= 0) {
|
||||||
|
// 如果没有可以撤销的步骤,就清空画布
|
||||||
|
imageContext.value.clearRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
imageContext.value.canvas.width,
|
||||||
|
imageContext.value.canvas.height
|
||||||
|
)
|
||||||
|
canvasHistory = []
|
||||||
|
} else {
|
||||||
|
step--
|
||||||
|
let canvasPic = new Image()
|
||||||
|
canvasHistory.pop()
|
||||||
|
canvasPic.src = canvasHistory[step]
|
||||||
|
canvasPic.onload = function () {
|
||||||
|
imageContext.value.clearRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
imageContext.value.canvas.width,
|
||||||
|
imageContext.value.canvas.height
|
||||||
|
)
|
||||||
|
imageContext.value.drawImage(canvasPic, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的蒙板
|
||||||
|
*/
|
||||||
|
function getCanvasMaskBase64() {
|
||||||
|
// 处理最后的图片
|
||||||
|
const imageData = imageContext.value.getImageData(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
imageContext.value.canvas.width,
|
||||||
|
imageContext.value.canvas.height
|
||||||
|
)
|
||||||
|
const newImageData = imageContext.value.createImageData(imageData.width, imageData.height)
|
||||||
|
|
||||||
|
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||||
|
if (
|
||||||
|
imageData.data[i] === 255 &&
|
||||||
|
imageData.data[i + 1] === 255 &&
|
||||||
|
imageData.data[i + 2] === 0
|
||||||
|
) {
|
||||||
|
// 如果这个像素点的颜色是 rgba(255, 255, 0, 255),就将它设置为白色
|
||||||
|
newImageData.data[i] = newImageData.data[i + 1] = newImageData.data[i + 2] = 255
|
||||||
|
newImageData.data[i + 3] = 255
|
||||||
|
} else {
|
||||||
|
// 否则,就将它设置为黑色
|
||||||
|
newImageData.data[i] = newImageData.data[i + 1] = newImageData.data[i + 2] = 0
|
||||||
|
newImageData.data[i + 3] = 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 创建一个新的 Canvas 对象
|
||||||
|
const newCanvas = document.createElement('canvas')
|
||||||
|
newCanvas.width = imageData.width
|
||||||
|
newCanvas.height = imageData.height
|
||||||
|
const newContext = newCanvas.getContext('2d')
|
||||||
|
|
||||||
|
// 将新的 ImageData 对象绘制到新的 Canvas 对象上
|
||||||
|
newContext.putImageData(newImageData, 0, 0)
|
||||||
|
|
||||||
|
// 将新的 Canvas 对象转换为一个 base64 编码的图片 URL
|
||||||
|
const base64 = newCanvas.toDataURL('image/png')
|
||||||
|
return base64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看效果
|
||||||
|
*/
|
||||||
|
async function Check() {
|
||||||
|
// 判断当前是不是有蒙板
|
||||||
|
if (canvasHistory.length <= 1) {
|
||||||
|
message.error('请先绘制蒙板')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let base64 = getCanvasMaskBase64()
|
||||||
|
let baseUrl = mask_setting.value.localUrl
|
||||||
|
if (mask_setting.value.isRemote) {
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
image: canvasHistory[0],
|
||||||
|
mask: base64,
|
||||||
|
type: 'arrayBuffer'
|
||||||
|
}
|
||||||
|
await window.img.ProcessImage(JSON.stringify(data), (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
debugger
|
||||||
|
const arrayBuffer = value.data
|
||||||
|
const blob = new Blob([arrayBuffer], { type: 'image/png' })
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onloadend = function () {
|
||||||
|
let img = new Image()
|
||||||
|
img.src = reader.result
|
||||||
|
img.onload = function () {
|
||||||
|
imageContext.value.drawImage(img, 0, 0)
|
||||||
|
canvasHistory.push(imageCanvas.value.toDataURL())
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(blob)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function SaveMask() {
|
||||||
|
debugger
|
||||||
|
// 判断当前是不是又蒙板
|
||||||
|
if (canvasHistory.length <= 1) {
|
||||||
|
message.error('请先绘制蒙板')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let base64 = getCanvasMaskBase64()
|
||||||
|
// 将base64 保存到本地
|
||||||
|
|
||||||
|
await window.img.Base64ToFile(
|
||||||
|
JSON.stringify([base64, `data/mask/mask_${new Date().getTime()}.png`]),
|
||||||
|
async (value) => {
|
||||||
|
debugger
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mask_setting.value.mask_path = value.data
|
||||||
|
await SaveMaskConfig()
|
||||||
|
message.success('蒙板保存成功')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存蒙板配置信息
|
||||||
|
*/
|
||||||
|
async function SaveMaskConfig() {
|
||||||
|
// 判断是不是iopaint,是的话没有localUrl就报错
|
||||||
|
if (mask_setting.value.isRemote && isEmpty(mask_setting.value.localUrl)) {
|
||||||
|
message.error('请输入iopaint地址')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await window.api.SaveDefineConfigJsonByProperty(
|
||||||
|
JSON.stringify(['img_base', 'mask_setting', toRaw(mask_setting.value), false]),
|
||||||
|
(value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success(value.message)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*查看蒙板
|
||||||
|
*/
|
||||||
|
async function ViewMaskImage() {
|
||||||
|
window.system.OpenFile(mask_setting.value.mask_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始批量处理
|
||||||
|
*/
|
||||||
|
async function BatchMask() {
|
||||||
|
// 判断当前是不是有配置模板
|
||||||
|
if (mask_setting.value.mask_path == null) {
|
||||||
|
message.error('请先绘制蒙板')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始处理
|
||||||
|
// 弹窗,管理不想要的值
|
||||||
|
dp = dialog.create({
|
||||||
|
name: 'progressDialog',
|
||||||
|
showIcon: false,
|
||||||
|
content: () =>
|
||||||
|
h(ProgressDialog, {
|
||||||
|
ref: progressDialogRef,
|
||||||
|
total: total,
|
||||||
|
current: current
|
||||||
|
}),
|
||||||
|
style: `width : 180px; opacity: 0.8;;`,
|
||||||
|
maskClosable: false,
|
||||||
|
closable: false,
|
||||||
|
onPositiveClick: async () => {}
|
||||||
|
})
|
||||||
|
// 提交处理任务,默认处理文件夹下面的所有的PNG图片
|
||||||
|
await window.img.BatchProcessImage('tmp/input_crop', (value) => {
|
||||||
|
debugger
|
||||||
|
// 不管返回什么,这边都关掉
|
||||||
|
setTimeout(() => {
|
||||||
|
if (dp) {
|
||||||
|
dp.destroy()
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.api.showGlobalMessageDialog(value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenTeach(type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'lama':
|
||||||
|
window.api.OpenUrl(
|
||||||
|
'https://rvgyir5wk1c.feishu.cn/docx/RZYCdG7ZpoKsIzxBEzccNEIFn8f#QOIRdJQvEouJB7xv0Xqcx3zInyb'
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case 'iopaint':
|
||||||
|
window.api.OpenUrl(
|
||||||
|
'https://rvgyir5wk1c.feishu.cn/docx/RZYCdG7ZpoKsIzxBEzccNEIFn8f#MLoUdn5cfo6NJTxL0xTcUGyLn43'
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifyImage,
|
||||||
|
imageCanvas,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
stopDrawing,
|
||||||
|
startDrawing,
|
||||||
|
draw,
|
||||||
|
undo,
|
||||||
|
Check,
|
||||||
|
SaveMask,
|
||||||
|
mask_setting,
|
||||||
|
SaveMaskConfig,
|
||||||
|
ViewMaskImage,
|
||||||
|
radius,
|
||||||
|
BatchMask,
|
||||||
|
progressDialogRef,
|
||||||
|
total,
|
||||||
|
current,
|
||||||
|
OpenTeach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.url_class {
|
||||||
|
color: #e18a3b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url_class:hover {
|
||||||
|
color: brown;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
src/renderer/src/components/Watermark/ImageFileSelect.vue
Normal file
55
src/renderer/src/components/Watermark/ImageFileSelect.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<n-upload :show-file-list="false" directory-dnd :default-upload="false" @change="selectImage">
|
||||||
|
<n-upload-dragger>
|
||||||
|
<div style="margin-bottom: 12px">
|
||||||
|
<n-icon size="48" :depth="3">
|
||||||
|
<archive-icon />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<n-text style="font-size: 16px"> 点击或者拖动文件到该区域来上传 </n-text>
|
||||||
|
</n-upload-dragger>
|
||||||
|
</n-upload>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref, onMounted, h, toRaw, watch } from 'vue'
|
||||||
|
import { NButton, useMessage, NInput, NTag, NUpload, NUploadDragger } from 'naive-ui'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
NButton,
|
||||||
|
NInput,
|
||||||
|
NTag,
|
||||||
|
NUpload,
|
||||||
|
NUploadDragger
|
||||||
|
},
|
||||||
|
props: ['modifyImage'],
|
||||||
|
setup(props) {
|
||||||
|
let message = useMessage()
|
||||||
|
let image = ref(props.image)
|
||||||
|
|
||||||
|
function getBase64(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = () => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => resolve(img)
|
||||||
|
img.onerror = reject
|
||||||
|
img.src = reader.result
|
||||||
|
}
|
||||||
|
reader.onerror = (error) => reject(error)
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectImage(value) {
|
||||||
|
let img = await getBase64(value.file.file)
|
||||||
|
props.modifyImage(img)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
selectImage,
|
||||||
|
image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Loading…
x
Reference in New Issue
Block a user