mirror of
https://iceshrimp.dev/crimekillz/trashposs
synced 2024-11-24 17:59:05 +01:00
[backend] Remove nsfw detection / tensorflow
This commit is contained in:
parent
6b45b7019c
commit
5c7a663320
BIN
.yarn/cache/@mapbox-node-pre-gyp-npm-1.0.9-7ef8e73557-1b9c4c87a6.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@mapbox-node-pre-gyp-npm-1.0.9-7ef8e73557-1b9c4c87a6.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@nsfw-filter-gif-frames-npm-1.0.2-38d9beaa21-1da5be8751.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@nsfw-filter-gif-frames-npm-1.0.2-38d9beaa21-1da5be8751.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@nsfw-filter-save-pixels-npm-2.3.4-bc58c7088f-3d8f8ba370.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@nsfw-filter-save-pixels-npm-2.3.4-bc58c7088f-3d8f8ba370.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-backend-cpu-npm-3.21.0-3b1ad6ef49-0c2d7ae1ba.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-backend-cpu-npm-3.21.0-3b1ad6ef49-0c2d7ae1ba.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-backend-cpu-npm-4.9.0-b2c383ebb4-a85f21c132.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-backend-cpu-npm-4.9.0-b2c383ebb4-a85f21c132.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-backend-webgl-npm-3.21.0-1869be8b43-e857fcdb52.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-backend-webgl-npm-3.21.0-1869be8b43-e857fcdb52.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-backend-webgl-npm-4.9.0-20855a82df-a210885ffa.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-backend-webgl-npm-4.9.0-20855a82df-a210885ffa.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-converter-npm-3.21.0-d1a52c5cb8-b31e010f53.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-converter-npm-3.21.0-d1a52c5cb8-b31e010f53.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-converter-npm-4.9.0-53ae2c5e42-9685823165.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-converter-npm-4.9.0-53ae2c5e42-9685823165.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-core-npm-3.21.0-55e687f48d-58a6686662.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-core-npm-3.21.0-55e687f48d-58a6686662.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-core-npm-4.9.0-0230dde1f0-973d4f2193.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-core-npm-4.9.0-0230dde1f0-973d4f2193.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-data-npm-3.21.0-3dcd30b900-ac54f4e6f7.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-data-npm-3.21.0-3dcd30b900-ac54f4e6f7.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-data-npm-4.9.0-edf807b6e3-49420730c9.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-data-npm-4.9.0-edf807b6e3-49420730c9.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-layers-npm-3.21.0-2bd44d0d44-73d1e9dcb5.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-layers-npm-3.21.0-2bd44d0d44-73d1e9dcb5.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-layers-npm-4.9.0-caa8082ef7-186983b32b.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-layers-npm-4.9.0-caa8082ef7-186983b32b.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-node-npm-3.21.1-eddf4f4a47-39289653e0.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-node-npm-3.21.1-eddf4f4a47-39289653e0.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-npm-3.21.0-fc5d555cf7-712d0acb8a.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-npm-3.21.0-fc5d555cf7-712d0acb8a.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@tensorflow-tfjs-npm-4.9.0-99af600b5f-3f786ba433.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@tensorflow-tfjs-npm-4.9.0-99af600b5f-3f786ba433.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-long-npm-4.0.2-e7bdc00dd4-d16cde7240.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-long-npm-4.0.2-e7bdc00dd4-d16cde7240.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-node-fetch-npm-2.6.4-3e5a54c325-f3e1d881bb.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-node-fetch-npm-2.6.4-3e5a54c325-f3e1d881bb.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-offscreencanvas-npm-2019.3.0-5d5329705c-006361d170.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-offscreencanvas-npm-2019.3.0-5d5329705c-006361d170.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-offscreencanvas-npm-2019.7.0-c691495e16-018cfcd19e.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-offscreencanvas-npm-2019.7.0-c691495e16-018cfcd19e.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-seedrandom-npm-2.4.30-2dd47adb4c-1bcf634bb0.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-seedrandom-npm-2.4.30-2dd47adb4c-1bcf634bb0.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-webgl-ext-npm-0.0.30-198c76ab05-c98aa8af2d.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-webgl-ext-npm-0.0.30-198c76ab05-c98aa8af2d.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@types-webgl2-npm-0.0.6-1a6b507373-32a6ff4088.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@types-webgl2-npm-0.0.6-1a6b507373-32a6ff4088.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@webgpu-types-npm-0.1.16-58c22d069b-550732f427.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@webgpu-types-npm-0.1.16-58c22d069b-550732f427.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/@webgpu-types-npm-0.1.30-bbd4b8862a-c07516879c.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/@webgpu-types-npm-0.1.30-bbd4b8862a-c07516879c.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/agent-base-npm-4.3.0-48c7e81d60-0c10891060.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/agent-base-npm-4.3.0-48c7e81d60-0c10891060.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/core-js-npm-3.29.1-baa30db121-b38446dbfc.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/core-js-npm-3.29.1-baa30db121-b38446dbfc.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/cwise-compiler-npm-1.1.3-26ff483afd-6c8ed75ad5.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/cwise-compiler-npm-1.1.3-26ff483afd-6c8ed75ad5.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/data-uri-to-buffer-npm-0.0.3-b26974551a-c85bc1e278.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/data-uri-to-buffer-npm-0.0.3-b26974551a-c85bc1e278.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/es6-promise-npm-4.2.8-c9f5b11f66-95614a8887.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/es6-promise-npm-4.2.8-c9f5b11f66-95614a8887.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/es6-promisify-npm-5.0.0-3726550934-fbed9d7915.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/es6-promisify-npm-5.0.0-3726550934-fbed9d7915.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/fs-minipass-npm-1.2.7-0e18342ce1-40fd46a2b5.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/fs-minipass-npm-1.2.7-0e18342ce1-40fd46a2b5.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/get-pixels-frame-info-update-npm-3.3.2-9a479a0deb-d32e0772d1.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/get-pixels-frame-info-update-npm-3.3.2-9a479a0deb-d32e0772d1.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/gif-encoder-npm-0.4.1-e3fef33f8c-36dabe29f8.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/gif-encoder-npm-0.4.1-e3fef33f8c-36dabe29f8.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/google-protobuf-npm-3.21.2-7c82de39ab-3caa2e1e26.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/google-protobuf-npm-3.21.2-7c82de39ab-3caa2e1e26.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/har-schema-npm-2.0.0-3a318c0ca5-d8946348f3.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/har-schema-npm-2.0.0-3a318c0ca5-d8946348f3.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-b998a7269c.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-b998a7269c.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/http-signature-npm-1.2.0-ee92426f34-3324598712.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/http-signature-npm-1.2.0-ee92426f34-3324598712.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/https-proxy-agent-npm-2.2.4-8bb97bed48-5fa8eab256.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/https-proxy-agent-npm-2.2.4-8bb97bed48-5fa8eab256.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/iota-array-npm-1.0.0-d8061f3372-e537c236d9.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/iota-array-npm-1.0.0-d8061f3372-e537c236d9.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/jpeg-js-npm-0.3.7-fe3ece5658-85a1ab09fe.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/jpeg-js-npm-0.3.7-fe3ece5658-85a1ab09fe.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/long-npm-4.0.0-ecd96a31ed-16afbe8f74.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/long-npm-4.0.0-ecd96a31ed-16afbe8f74.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/minipass-npm-2.9.0-6335fbe4af-077b66f31b.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/minipass-npm-2.9.0-6335fbe4af-077b66f31b.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/minizlib-npm-1.3.3-b590e5bfb8-b0425c04d2.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/minizlib-npm-1.3.3-b590e5bfb8-b0425c04d2.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/multi-integer-range-npm-3.0.0-b72c939d45-ce4b57c060.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/multi-integer-range-npm-3.0.0-b72c939d45-ce4b57c060.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/ndarray-npm-1.0.18-0841bb9e2d-e4d442c9e8.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/ndarray-npm-1.0.18-0841bb9e2d-e4d442c9e8.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/ndarray-npm-1.0.19-d03b8fa8ef-2f8ac89f0b.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/ndarray-npm-1.0.19-d03b8fa8ef-2f8ac89f0b.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/ndarray-ops-npm-1.2.2-cd8205bd47-6d4f81bd37.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/ndarray-ops-npm-1.2.2-cd8205bd47-6d4f81bd37.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/ndarray-pack-npm-1.2.1-9765c7e13c-455030bebc.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/ndarray-pack-npm-1.2.1-9765c7e13c-455030bebc.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/node-bitmap-npm-0.0.1-df1ff3aae0-4e30bbd22e.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/node-bitmap-npm-0.0.1-df1ff3aae0-4e30bbd22e.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/nsfwjs-npm-2.4.2-5f987f8b7e-a66a78caaa.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/nsfwjs-npm-2.4.2-5f987f8b7e-a66a78caaa.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-8f5497a127.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-8f5497a127.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/omggif-npm-1.0.10-6ffa5bf343-15102e46b6.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/omggif-npm-1.0.10-6ffa5bf343-15102e46b6.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/parse-data-uri-npm-0.2.0-d70e3f0b43-6e8d76eb4b.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/parse-data-uri-npm-0.2.0-d70e3f0b43-6e8d76eb4b.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/pngjs-nozlib-npm-1.0.0-1c99b27261-63dd1425fe.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/pngjs-nozlib-npm-1.0.0-1c99b27261-63dd1425fe.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/qs-npm-6.5.3-90b2635484-6f20bf08ca.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/qs-npm-6.5.3-90b2635484-6f20bf08ca.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/readable-stream-npm-1.1.14-41e61d1768-17dfeae3e9.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/readable-stream-npm-1.1.14-41e61d1768-17dfeae3e9.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/request-npm-2.88.2-f4a57c72c4-4e112c087f.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/request-npm-2.88.2-f4a57c72c4-4e112c087f.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/rimraf-npm-2.7.1-9a71f3cc37-cdc7f6eacb.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/rimraf-npm-2.7.1-9a71f3cc37-cdc7f6eacb.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/tar-npm-4.4.19-5f8e81b87e-423c8259b1.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/tar-npm-4.4.19-5f8e81b87e-423c8259b1.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/through-npm-2.3.4-eb93babff1-bab392560a.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/through-npm-2.3.4-eb93babff1-bab392560a.zip
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
.yarn/cache/uuid-npm-3.4.0-4fd8ef88ad-58de2feed6.zip
(Stored with Git LFS)
vendored
BIN
.yarn/cache/uuid-npm-3.4.0-4fd8ef88ad-58de2feed6.zip
(Stored with Git LFS)
vendored
Binary file not shown.
@ -44,7 +44,6 @@
|
||||
"dependencies": {
|
||||
"@bull-board/api": "5.6.0",
|
||||
"@bull-board/ui": "5.6.0",
|
||||
"@tensorflow/tfjs": "^3.21.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"seedrandom": "^3.0.5"
|
||||
},
|
||||
|
BIN
packages/backend/nsfw-model/group1-shard1of6
(Stored with Git LFS)
BIN
packages/backend/nsfw-model/group1-shard1of6
(Stored with Git LFS)
Binary file not shown.
BIN
packages/backend/nsfw-model/group1-shard2of6
(Stored with Git LFS)
BIN
packages/backend/nsfw-model/group1-shard2of6
(Stored with Git LFS)
Binary file not shown.
BIN
packages/backend/nsfw-model/group1-shard3of6
(Stored with Git LFS)
BIN
packages/backend/nsfw-model/group1-shard3of6
(Stored with Git LFS)
Binary file not shown.
BIN
packages/backend/nsfw-model/group1-shard4of6
(Stored with Git LFS)
BIN
packages/backend/nsfw-model/group1-shard4of6
(Stored with Git LFS)
Binary file not shown.
BIN
packages/backend/nsfw-model/group1-shard5of6
(Stored with Git LFS)
BIN
packages/backend/nsfw-model/group1-shard5of6
(Stored with Git LFS)
Binary file not shown.
BIN
packages/backend/nsfw-model/group1-shard6of6
(Stored with Git LFS)
BIN
packages/backend/nsfw-model/group1-shard6of6
(Stored with Git LFS)
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -19,7 +19,6 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-android-arm64": "1.3.11",
|
||||
"@tensorflow/tfjs-node": "3.21.1",
|
||||
"@types/formidable": "^2.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -36,9 +35,6 @@
|
||||
"@redocly/openapi-core": "1.0.0-beta.131",
|
||||
"@sinonjs/fake-timers": "9.1.2",
|
||||
"@syuilo/aiscript": "0.11.1",
|
||||
"@tensorflow/tfjs": "^4.2.0",
|
||||
"@tensorflow/tfjs-backend-webgl": "^4.2.0",
|
||||
"@tensorflow/tfjs-core": "^4.2.0",
|
||||
"adm-zip": "^0.5.10",
|
||||
"ajv": "8.12.0",
|
||||
"archiver": "5.3.1",
|
||||
@ -98,7 +94,6 @@
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.1",
|
||||
"nodemailer": "6.9.3",
|
||||
"nsfwjs": "2.4.2",
|
||||
"oauth": "^0.10.0",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "^9.1.3",
|
||||
|
@ -0,0 +1,33 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class RemoteNsfwDetection1697663824168 implements MigrationInterface {
|
||||
name = 'RemoteNsfwDetection1697663824168'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_3b33dff77bb64b23c88151d23e"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_8bdcd3dd2bddb78014999a16ce"`);
|
||||
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "maybeSensitive"`);
|
||||
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "maybePorn"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveMediaDetection"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."meta_sensitivemediadetection_enum"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveMediaDetectionSensitivity"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."meta_sensitivemediadetectionsensitivity_enum"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "setSensitiveFlagAutomatically"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableSensitiveMediaDetectionForVideos"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "autoSensitive"`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "autoSensitive" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "enableSensitiveMediaDetectionForVideos" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "setSensitiveFlagAutomatically" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."meta_sensitivemediadetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveMediaDetectionSensitivity" "public"."meta_sensitivemediadetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."meta_sensitivemediadetection_enum" AS ENUM('none', 'all', 'local', 'remote')`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveMediaDetection" "public"."meta_sensitivemediadetection_enum" NOT NULL DEFAULT 'none'`);
|
||||
await queryRunner.query(`ALTER TABLE "drive_file" ADD "maybePorn" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "drive_file" ADD "maybeSensitive" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_8bdcd3dd2bddb78014999a16ce" ON "drive_file" ("maybePorn") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_3b33dff77bb64b23c88151d23e" ON "drive_file" ("maybeSensitive") `);
|
||||
}
|
||||
}
|
@ -1,18 +1,12 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as crypto from "node:crypto";
|
||||
import { join } from "node:path";
|
||||
import * as stream from "node:stream";
|
||||
import * as util from "node:util";
|
||||
import { FSWatcher } from "chokidar";
|
||||
import { fileTypeFromFile } from "file-type";
|
||||
import probeImageSize from "probe-image-size";
|
||||
import FFmpeg from "fluent-ffmpeg";
|
||||
import isSvg from "is-svg";
|
||||
import { type predictionType } from "nsfwjs";
|
||||
import sharp from "sharp";
|
||||
import { encode } from "blurhash";
|
||||
import { detectSensitive } from "@/services/detect-sensitive.js";
|
||||
import { createTempDir } from "./create-temp.js";
|
||||
|
||||
const pipeline = util.promisify(stream.pipeline);
|
||||
|
||||
@ -27,8 +21,6 @@ export type FileInfo = {
|
||||
height?: number;
|
||||
orientation?: number;
|
||||
blurhash?: string;
|
||||
sensitive: boolean;
|
||||
porn: boolean;
|
||||
warnings: string[];
|
||||
};
|
||||
|
||||
@ -47,12 +39,6 @@ const TYPE_SVG = {
|
||||
*/
|
||||
export async function getFileInfo(
|
||||
path: string,
|
||||
opts: {
|
||||
skipSensitiveDetection: boolean;
|
||||
sensitiveThreshold?: number;
|
||||
sensitiveThresholdForPorn?: number;
|
||||
enableSensitiveMediaDetectionForVideos?: boolean;
|
||||
},
|
||||
): Promise<FileInfo> {
|
||||
const warnings = [] as string[];
|
||||
|
||||
@ -123,26 +109,6 @@ export async function getFileInfo(
|
||||
});
|
||||
}
|
||||
|
||||
let sensitive = false;
|
||||
let porn = false;
|
||||
|
||||
if (!opts.skipSensitiveDetection) {
|
||||
await detectSensitivity(
|
||||
path,
|
||||
type.mime,
|
||||
opts.sensitiveThreshold ?? 0.5,
|
||||
opts.sensitiveThresholdForPorn ?? 0.75,
|
||||
opts.enableSensitiveMediaDetectionForVideos ?? false,
|
||||
).then(
|
||||
(value) => {
|
||||
[sensitive, porn] = value;
|
||||
},
|
||||
(error) => {
|
||||
warnings.push(`detectSensitivity failed: ${error}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
size,
|
||||
md5,
|
||||
@ -151,183 +117,10 @@ export async function getFileInfo(
|
||||
height,
|
||||
orientation,
|
||||
blurhash,
|
||||
sensitive,
|
||||
porn,
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
|
||||
async function detectSensitivity(
|
||||
source: string,
|
||||
mime: string,
|
||||
sensitiveThreshold: number,
|
||||
sensitiveThresholdForPorn: number,
|
||||
analyzeVideo: boolean,
|
||||
): Promise<[sensitive: boolean, porn: boolean]> {
|
||||
let sensitive = false;
|
||||
let porn = false;
|
||||
|
||||
function judgePrediction(
|
||||
result: readonly predictionType[],
|
||||
): [sensitive: boolean, porn: boolean] {
|
||||
let sensitive = false;
|
||||
let porn = false;
|
||||
|
||||
if (
|
||||
(result.find((x) => x.className === "Sexy")?.probability ?? 0) >
|
||||
sensitiveThreshold
|
||||
)
|
||||
sensitive = true;
|
||||
if (
|
||||
(result.find((x) => x.className === "Hentai")?.probability ?? 0) >
|
||||
sensitiveThreshold
|
||||
)
|
||||
sensitive = true;
|
||||
if (
|
||||
(result.find((x) => x.className === "Porn")?.probability ?? 0) >
|
||||
sensitiveThreshold
|
||||
)
|
||||
sensitive = true;
|
||||
|
||||
if (
|
||||
(result.find((x) => x.className === "Porn")?.probability ?? 0) >
|
||||
sensitiveThresholdForPorn
|
||||
)
|
||||
porn = true;
|
||||
|
||||
return [sensitive, porn];
|
||||
}
|
||||
|
||||
if (["image/jpeg", "image/png", "image/webp"].includes(mime)) {
|
||||
const result = await detectSensitive(source);
|
||||
if (result) {
|
||||
[sensitive, porn] = judgePrediction(result);
|
||||
}
|
||||
} else if (
|
||||
analyzeVideo &&
|
||||
(mime === "image/apng" || mime.startsWith("video/"))
|
||||
) {
|
||||
const [outDir, disposeOutDir] = await createTempDir();
|
||||
try {
|
||||
const command = FFmpeg()
|
||||
.input(source)
|
||||
.inputOptions([
|
||||
"-skip_frame",
|
||||
"nokey", // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない)
|
||||
"-lowres",
|
||||
"3", // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない)
|
||||
])
|
||||
.noAudio()
|
||||
.videoFilters([
|
||||
{
|
||||
filter: "select", // フレームのフィルタリング
|
||||
options: {
|
||||
e: "eq(pict_type,PICT_TYPE_I)", // I-Frame のみをフィルタする(VP9 とかはデコードしてみないとわからないっぽい)
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: "blackframe", // 暗いフレームの検出
|
||||
options: {
|
||||
amount: "0", // 暗さに関わらず全てのフレームで測定値を取る
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: "metadata",
|
||||
options: {
|
||||
mode: "select", // フレーム選択モード
|
||||
key: "lavfi.blackframe.pblack", // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する)
|
||||
value: "50",
|
||||
function: "less", // 50% 未満のフレームを選択する(50% 以上暗部があるフレームだと誤検知を招くかもしれないので)
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: "scale",
|
||||
options: {
|
||||
w: 299,
|
||||
h: 299,
|
||||
},
|
||||
},
|
||||
])
|
||||
.format("image2")
|
||||
.output(join(outDir, "%d.png"))
|
||||
.outputOptions(["-vsync", "0"]); // 可変フレームレートにすることで穴埋めをさせない
|
||||
const results: ReturnType<typeof judgePrediction>[] = [];
|
||||
let frameIndex = 0;
|
||||
let targetIndex = 0;
|
||||
let nextIndex = 1;
|
||||
for await (const path of asyncIterateFrames(outDir, command)) {
|
||||
try {
|
||||
const index = frameIndex++;
|
||||
if (index !== targetIndex) {
|
||||
continue;
|
||||
}
|
||||
targetIndex = nextIndex;
|
||||
nextIndex += index; // fibonacci sequence によってフレーム数制限を掛ける
|
||||
const result = await detectSensitive(path);
|
||||
if (result) {
|
||||
results.push(judgePrediction(result));
|
||||
}
|
||||
} finally {
|
||||
fs.promises.unlink(path);
|
||||
}
|
||||
}
|
||||
sensitive =
|
||||
results.filter((x) => x[0]).length >=
|
||||
Math.ceil(results.length * sensitiveThreshold);
|
||||
porn =
|
||||
results.filter((x) => x[1]).length >=
|
||||
Math.ceil(results.length * sensitiveThresholdForPorn);
|
||||
} finally {
|
||||
disposeOutDir();
|
||||
}
|
||||
}
|
||||
|
||||
return [sensitive, porn];
|
||||
}
|
||||
|
||||
async function* asyncIterateFrames(
|
||||
cwd: string,
|
||||
command: FFmpeg.FfmpegCommand,
|
||||
): AsyncGenerator<string, void> {
|
||||
const watcher = new FSWatcher({
|
||||
cwd,
|
||||
disableGlobbing: true,
|
||||
});
|
||||
let finished = false;
|
||||
command.once("end", () => {
|
||||
finished = true;
|
||||
watcher.close();
|
||||
});
|
||||
command.run();
|
||||
for (let i = 1; true; i++) {
|
||||
const current = `${i}.png`;
|
||||
const next = `${i + 1}.png`;
|
||||
const framePath = join(cwd, current);
|
||||
if (await exists(join(cwd, next))) {
|
||||
yield framePath;
|
||||
} else if (!finished) {
|
||||
watcher.add(next);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
watcher.on("add", function onAdd(path) {
|
||||
if (path === next) {
|
||||
// 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている
|
||||
watcher.unwatch(current);
|
||||
watcher.off("add", onAdd);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
command.once("end", resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている
|
||||
command.once("error", reject);
|
||||
});
|
||||
yield framePath;
|
||||
} else if (await exists(framePath)) {
|
||||
yield framePath;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function exists(path: string): Promise<boolean> {
|
||||
return fs.promises.access(path).then(
|
||||
() => true,
|
||||
|
@ -183,19 +183,6 @@ export class DriveFile {
|
||||
})
|
||||
public isSensitive: boolean;
|
||||
|
||||
@Index()
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
comment: "Whether the DriveFile is NSFW. (predict)",
|
||||
})
|
||||
public maybeSensitive: boolean;
|
||||
|
||||
@Index()
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
})
|
||||
public maybePorn: boolean;
|
||||
|
||||
/**
|
||||
* 外部の(信頼されていない)URLへの直リンクか否か
|
||||
*/
|
||||
|
@ -253,33 +253,6 @@ export class Meta {
|
||||
})
|
||||
public recaptchaSecretKey: string | null;
|
||||
|
||||
@Column("enum", {
|
||||
enum: ["none", "all", "local", "remote"],
|
||||
default: "none",
|
||||
})
|
||||
public sensitiveMediaDetection: "none" | "all" | "local" | "remote";
|
||||
|
||||
@Column("enum", {
|
||||
enum: ["medium", "low", "high", "veryLow", "veryHigh"],
|
||||
default: "medium",
|
||||
})
|
||||
public sensitiveMediaDetectionSensitivity:
|
||||
| "medium"
|
||||
| "low"
|
||||
| "high"
|
||||
| "veryLow"
|
||||
| "veryHigh";
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
})
|
||||
public setSensitiveFlagAutomatically: boolean;
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
})
|
||||
public enableSensitiveMediaDetectionForVideos: boolean;
|
||||
|
||||
@Column("integer", {
|
||||
default: 1024,
|
||||
comment: "Drive capacity of a local user (MB)",
|
||||
|
@ -182,11 +182,6 @@ export class UserProfile {
|
||||
})
|
||||
public alwaysMarkNsfw: boolean;
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
})
|
||||
public autoSensitive: boolean;
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
})
|
||||
|
@ -539,7 +539,6 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
injectFeaturedNote: profile!.injectFeaturedNote,
|
||||
receiveAnnouncementEmail: profile!.receiveAnnouncementEmail,
|
||||
alwaysMarkNsfw: profile!.alwaysMarkNsfw,
|
||||
autoSensitive: profile!.autoSensitive,
|
||||
carefulBot: profile!.carefulBot,
|
||||
autoAcceptFollowed: profile!.autoAcceptFollowed,
|
||||
noCrawle: profile!.noCrawle,
|
||||
|
@ -374,11 +374,6 @@ export const packedMeDetailedOnlySchema = {
|
||||
nullable: true,
|
||||
optional: false,
|
||||
},
|
||||
autoSensitive: {
|
||||
type: "boolean",
|
||||
nullable: true,
|
||||
optional: false,
|
||||
},
|
||||
carefulBot: {
|
||||
type: "boolean",
|
||||
nullable: true,
|
||||
|
@ -2,7 +2,6 @@ import config from "@/config/index.js";
|
||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
|
||||
import define from "../../define.js";
|
||||
import { Exp } from "@tensorflow/tfjs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["meta"],
|
||||
@ -266,26 +265,6 @@ export const meta = {
|
||||
optional: true,
|
||||
nullable: true,
|
||||
},
|
||||
sensitiveMediaDetection: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
},
|
||||
sensitiveMediaDetectionSensitivity: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
},
|
||||
setSensitiveFlagAutomatically: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
},
|
||||
enableSensitiveMediaDetectionForVideos: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
},
|
||||
proxyAccountId: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
@ -531,12 +510,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
secureMode: instance.secureMode,
|
||||
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
||||
recaptchaSecretKey: instance.recaptchaSecretKey,
|
||||
sensitiveMediaDetection: instance.sensitiveMediaDetection,
|
||||
sensitiveMediaDetectionSensitivity:
|
||||
instance.sensitiveMediaDetectionSensitivity,
|
||||
setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically,
|
||||
enableSensitiveMediaDetectionForVideos:
|
||||
instance.enableSensitiveMediaDetectionForVideos,
|
||||
proxyAccountId: instance.proxyAccountId,
|
||||
twitterConsumerKey: instance.twitterConsumerKey,
|
||||
twitterConsumerSecret: instance.twitterConsumerSecret,
|
||||
|
@ -62,7 +62,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
noCrawle: profile.noCrawle,
|
||||
preventAiLearning: profile.preventAiLearning,
|
||||
alwaysMarkNsfw: profile.alwaysMarkNsfw,
|
||||
autoSensitive: profile.autoSensitive,
|
||||
carefulBot: profile.carefulBot,
|
||||
injectFeaturedNote: profile.injectFeaturedNote,
|
||||
receiveAnnouncementEmail: profile.receiveAnnouncementEmail,
|
||||
|
@ -101,16 +101,6 @@ export const paramDef = {
|
||||
enableRecaptcha: { type: "boolean" },
|
||||
recaptchaSiteKey: { type: "string", nullable: true },
|
||||
recaptchaSecretKey: { type: "string", nullable: true },
|
||||
sensitiveMediaDetection: {
|
||||
type: "string",
|
||||
enum: ["none", "all", "local", "remote"],
|
||||
},
|
||||
sensitiveMediaDetectionSensitivity: {
|
||||
type: "string",
|
||||
enum: ["medium", "low", "high", "veryLow", "veryHigh"],
|
||||
},
|
||||
setSensitiveFlagAutomatically: { type: "boolean" },
|
||||
enableSensitiveMediaDetectionForVideos: { type: "boolean" },
|
||||
proxyAccountId: { type: "string", format: "misskey:id", nullable: true },
|
||||
maintainerName: { type: "string", nullable: true },
|
||||
maintainerEmail: { type: "string", nullable: true },
|
||||
@ -148,8 +138,6 @@ export const paramDef = {
|
||||
smtpPort: { type: "integer", nullable: true },
|
||||
smtpUser: { type: "string", nullable: true },
|
||||
smtpPass: { type: "string", nullable: true },
|
||||
swPublicKey: { type: "string", nullable: true },
|
||||
swPrivateKey: { type: "string", nullable: true },
|
||||
tosUrl: { type: "string", nullable: true },
|
||||
repositoryUrl: { type: "string" },
|
||||
feedbackUrl: { type: "string" },
|
||||
@ -348,24 +336,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
set.recaptchaSecretKey = ps.recaptchaSecretKey;
|
||||
}
|
||||
|
||||
if (ps.sensitiveMediaDetection !== undefined) {
|
||||
set.sensitiveMediaDetection = ps.sensitiveMediaDetection;
|
||||
}
|
||||
|
||||
if (ps.sensitiveMediaDetectionSensitivity !== undefined) {
|
||||
set.sensitiveMediaDetectionSensitivity =
|
||||
ps.sensitiveMediaDetectionSensitivity;
|
||||
}
|
||||
|
||||
if (ps.setSensitiveFlagAutomatically !== undefined) {
|
||||
set.setSensitiveFlagAutomatically = ps.setSensitiveFlagAutomatically;
|
||||
}
|
||||
|
||||
if (ps.enableSensitiveMediaDetectionForVideos !== undefined) {
|
||||
set.enableSensitiveMediaDetectionForVideos =
|
||||
ps.enableSensitiveMediaDetectionForVideos;
|
||||
}
|
||||
|
||||
if (ps.proxyAccountId !== undefined) {
|
||||
set.proxyAccountId = ps.proxyAccountId;
|
||||
}
|
||||
@ -462,14 +432,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
set.errorImageUrl = ps.errorImageUrl;
|
||||
}
|
||||
|
||||
if (ps.swPublicKey !== undefined) {
|
||||
set.swPublicKey = ps.swPublicKey;
|
||||
}
|
||||
|
||||
if (ps.swPrivateKey !== undefined) {
|
||||
set.swPrivateKey = ps.swPrivateKey;
|
||||
}
|
||||
|
||||
if (ps.tosUrl !== undefined) {
|
||||
set.ToSUrl = ps.tosUrl;
|
||||
}
|
||||
|
@ -123,7 +123,6 @@ export const paramDef = {
|
||||
injectFeaturedNote: { type: "boolean" },
|
||||
receiveAnnouncementEmail: { type: "boolean" },
|
||||
alwaysMarkNsfw: { type: "boolean" },
|
||||
autoSensitive: { type: "boolean" },
|
||||
ffVisibility: { type: "string", enum: ["public", "followers", "private"] },
|
||||
pinnedPageId: { type: "string", format: "misskey:id", nullable: true },
|
||||
mutedWords: { type: "array" },
|
||||
@ -213,8 +212,6 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
||||
profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
|
||||
if (typeof ps.alwaysMarkNsfw === "boolean")
|
||||
profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
|
||||
if (typeof ps.autoSensitive === "boolean")
|
||||
profileUpdates.autoSensitive = ps.autoSensitive;
|
||||
if (ps.emailNotificationTypes !== undefined)
|
||||
profileUpdates.emailNotificationTypes = ps.emailNotificationTypes;
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
import * as fs from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname } from "node:path";
|
||||
import * as nsfw from "nsfwjs";
|
||||
import si from "systeminformation";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const REQUIRED_CPU_FLAGS = ["avx2", "fma"];
|
||||
let isSupportedCpu: undefined | boolean = undefined;
|
||||
|
||||
let model: nsfw.NSFWJS;
|
||||
|
||||
export async function detectSensitive(
|
||||
path: string,
|
||||
): Promise<nsfw.predictionType[] | null> {
|
||||
try {
|
||||
if (isSupportedCpu === undefined) {
|
||||
const cpuFlags = await getCpuFlags();
|
||||
isSupportedCpu = REQUIRED_CPU_FLAGS.every((required) =>
|
||||
cpuFlags.includes(required),
|
||||
);
|
||||
}
|
||||
|
||||
if (!isSupportedCpu) {
|
||||
console.error("This CPU cannot use TensorFlow.");
|
||||
return null;
|
||||
}
|
||||
|
||||
const tf = await import("@tensorflow/tfjs-node");
|
||||
|
||||
if (model == null)
|
||||
model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, {
|
||||
size: 299,
|
||||
});
|
||||
|
||||
const buffer = await fs.promises.readFile(path);
|
||||
const image = (await tf.node.decodeImage(buffer, 3)) as any;
|
||||
try {
|
||||
const predictions = await model.classify(image);
|
||||
return predictions;
|
||||
} finally {
|
||||
image.dispose();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getCpuFlags(): Promise<string[]> {
|
||||
const str = await si.cpuFlags();
|
||||
return str.split(/\s+/);
|
||||
}
|
@ -396,7 +396,7 @@ async function expireOldFile(user: IRemoteUser, driveCapacity: number) {
|
||||
|
||||
for (const fileId of exceedFileIds) {
|
||||
const file = await DriveFiles.findOneBy({ id: fileId });
|
||||
deleteFile(file, true);
|
||||
if (file) deleteFile(file, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,39 +448,7 @@ export async function addFile({
|
||||
requestIp = null,
|
||||
requestHeaders = null,
|
||||
}: AddFileArgs): Promise<DriveFile> {
|
||||
let skipNsfwCheck = false;
|
||||
const instance = await fetchMeta();
|
||||
if (user == null) skipNsfwCheck = true;
|
||||
if (instance.sensitiveMediaDetection === "none") skipNsfwCheck = true;
|
||||
if (
|
||||
user &&
|
||||
instance.sensitiveMediaDetection === "local" &&
|
||||
Users.isRemoteUser(user)
|
||||
)
|
||||
skipNsfwCheck = true;
|
||||
if (
|
||||
user &&
|
||||
instance.sensitiveMediaDetection === "remote" &&
|
||||
Users.isLocalUser(user)
|
||||
)
|
||||
skipNsfwCheck = true;
|
||||
|
||||
const info = await getFileInfo(path, {
|
||||
skipSensitiveDetection: skipNsfwCheck,
|
||||
sensitiveThreshold: // 感度が高いほどしきい値は低くすることになる
|
||||
instance.sensitiveMediaDetectionSensitivity === "veryHigh"
|
||||
? 0.1
|
||||
: instance.sensitiveMediaDetectionSensitivity === "high"
|
||||
? 0.3
|
||||
: instance.sensitiveMediaDetectionSensitivity === "low"
|
||||
? 0.7
|
||||
: instance.sensitiveMediaDetectionSensitivity === "veryLow"
|
||||
? 0.9
|
||||
: 0.5,
|
||||
sensitiveThresholdForPorn: 0.75,
|
||||
enableSensitiveMediaDetectionForVideos:
|
||||
instance.enableSensitiveMediaDetectionForVideos,
|
||||
});
|
||||
const info = await getFileInfo(path);
|
||||
logger.info(`${JSON.stringify(info)}`);
|
||||
|
||||
// 現状 false positive が多すぎて実用に耐えない
|
||||
@ -595,8 +563,6 @@ export async function addFile({
|
||||
file.isLink = isLink;
|
||||
file.requestIp = requestIp;
|
||||
file.requestHeaders = requestHeaders;
|
||||
file.maybeSensitive = info.sensitive;
|
||||
file.maybePorn = info.porn;
|
||||
file.isSensitive = user
|
||||
? Users.isLocalUser(user) && profile!.alwaysMarkNsfw
|
||||
? true
|
||||
@ -605,10 +571,6 @@ export async function addFile({
|
||||
: false
|
||||
: false;
|
||||
|
||||
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
|
||||
if (info.sensitive && instance.setSensitiveFlagAutomatically)
|
||||
file.isSensitive = true;
|
||||
|
||||
if (url !== null) {
|
||||
file.src = url;
|
||||
|
||||
|
@ -29,116 +29,6 @@
|
||||
<XBotProtection />
|
||||
</FormFolder>
|
||||
|
||||
<FormFolder class="_formBlock">
|
||||
<template #icon
|
||||
><i class="ph-eye-slash ph-bold ph-lg"></i
|
||||
></template>
|
||||
<template #label>{{
|
||||
i18n.ts.sensitiveMediaDetection
|
||||
}}</template>
|
||||
<template
|
||||
v-if="sensitiveMediaDetection === 'all'"
|
||||
#suffix
|
||||
>{{ i18n.ts.all }}</template
|
||||
>
|
||||
<template
|
||||
v-else-if="sensitiveMediaDetection === 'local'"
|
||||
#suffix
|
||||
>{{ i18n.ts.localOnly }}</template
|
||||
>
|
||||
<template
|
||||
v-else-if="sensitiveMediaDetection === 'remote'"
|
||||
#suffix
|
||||
>{{ i18n.ts.remoteOnly }}</template
|
||||
>
|
||||
<template v-else #suffix>{{ i18n.ts.none }}</template>
|
||||
|
||||
<div class="_formRoot">
|
||||
<span class="_formBlock">{{
|
||||
i18n.ts._sensitiveMediaDetection.description
|
||||
}}</span>
|
||||
|
||||
<FormRadios
|
||||
v-model="sensitiveMediaDetection"
|
||||
class="_formBlock"
|
||||
>
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="local">
|
||||
{{ i18n.ts.localOnly }}
|
||||
</option>
|
||||
<option value="remote">
|
||||
{{ i18n.ts.remoteOnly }}
|
||||
</option>
|
||||
</FormRadios>
|
||||
|
||||
<FormRange
|
||||
v-model="sensitiveMediaDetectionSensitivity"
|
||||
:min="0"
|
||||
:max="4"
|
||||
:step="1"
|
||||
:text-converter="(v) => `${v + 1}`"
|
||||
class="_formBlock"
|
||||
>
|
||||
<template #label>{{
|
||||
i18n.ts._sensitiveMediaDetection.sensitivity
|
||||
}}</template>
|
||||
<template #caption>{{
|
||||
i18n.ts._sensitiveMediaDetection
|
||||
.sensitivityDescription
|
||||
}}</template>
|
||||
</FormRange>
|
||||
|
||||
<FormSwitch
|
||||
v-model="enableSensitiveMediaDetectionForVideos"
|
||||
class="_formBlock"
|
||||
>
|
||||
<template #label
|
||||
>{{
|
||||
i18n.ts._sensitiveMediaDetection
|
||||
.analyzeVideos
|
||||
}}<span class="_beta">{{
|
||||
i18n.ts.beta
|
||||
}}</span></template
|
||||
>
|
||||
<template #caption>{{
|
||||
i18n.ts._sensitiveMediaDetection
|
||||
.analyzeVideosDescription
|
||||
}}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<FormSwitch
|
||||
v-model="setSensitiveFlagAutomatically"
|
||||
class="_formBlock"
|
||||
>
|
||||
<template #label
|
||||
>{{
|
||||
i18n.ts._sensitiveMediaDetection
|
||||
.setSensitiveFlagAutomatically
|
||||
}}
|
||||
({{ i18n.ts.notRecommended }})</template
|
||||
>
|
||||
<template #caption>{{
|
||||
i18n.ts._sensitiveMediaDetection
|
||||
.setSensitiveFlagAutomaticallyDescription
|
||||
}}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<!-- 現状 false positive が多すぎて実用に耐えない
|
||||
<FormSwitch v-model="disallowUploadWhenPredictedAsPorn" class="_formBlock">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.disallowUploadWhenPredictedAsPorn }}</template>
|
||||
</FormSwitch>
|
||||
-->
|
||||
|
||||
<FormButton primary class="_formBlock" @click="save"
|
||||
><i
|
||||
class="ph-floppy-disk-back ph-bold ph-lg"
|
||||
></i>
|
||||
{{ i18n.ts.save }}</FormButton
|
||||
>
|
||||
</div>
|
||||
</FormFolder>
|
||||
|
||||
<FormFolder class="_formBlock">
|
||||
<template #label>Active Email Validation</template>
|
||||
<template v-if="enableActiveEmailValidation" #suffix
|
||||
@ -274,10 +164,6 @@ import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
let summalyProxy: string = $ref("");
|
||||
let enableHcaptcha: boolean = $ref(false);
|
||||
let enableRecaptcha: boolean = $ref(false);
|
||||
let sensitiveMediaDetection: string = $ref("none");
|
||||
let sensitiveMediaDetectionSensitivity: number = $ref(0);
|
||||
let setSensitiveFlagAutomatically: boolean = $ref(false);
|
||||
let enableSensitiveMediaDetectionForVideos: boolean = $ref(false);
|
||||
let enableIpLogging: boolean = $ref(false);
|
||||
let enableActiveEmailValidation: boolean = $ref(false);
|
||||
|
||||
@ -290,22 +176,6 @@ async function init() {
|
||||
summalyProxy = meta.summalyProxy;
|
||||
enableHcaptcha = meta.enableHcaptcha;
|
||||
enableRecaptcha = meta.enableRecaptcha;
|
||||
sensitiveMediaDetection = meta.sensitiveMediaDetection;
|
||||
sensitiveMediaDetectionSensitivity =
|
||||
meta.sensitiveMediaDetectionSensitivity === "veryLow"
|
||||
? 0
|
||||
: meta.sensitiveMediaDetectionSensitivity === "low"
|
||||
? 1
|
||||
: meta.sensitiveMediaDetectionSensitivity === "medium"
|
||||
? 2
|
||||
: meta.sensitiveMediaDetectionSensitivity === "high"
|
||||
? 3
|
||||
: meta.sensitiveMediaDetectionSensitivity === "veryHigh"
|
||||
? 4
|
||||
: 0;
|
||||
setSensitiveFlagAutomatically = meta.setSensitiveFlagAutomatically;
|
||||
enableSensitiveMediaDetectionForVideos =
|
||||
meta.enableSensitiveMediaDetectionForVideos;
|
||||
enableIpLogging = meta.enableIpLogging;
|
||||
enableActiveEmailValidation = meta.enableActiveEmailValidation;
|
||||
|
||||
@ -317,21 +187,6 @@ async function init() {
|
||||
function save() {
|
||||
os.apiWithDialog("admin/update-meta", {
|
||||
summalyProxy,
|
||||
sensitiveMediaDetection,
|
||||
sensitiveMediaDetectionSensitivity:
|
||||
sensitiveMediaDetectionSensitivity === 0
|
||||
? "veryLow"
|
||||
: sensitiveMediaDetectionSensitivity === 1
|
||||
? "low"
|
||||
: sensitiveMediaDetectionSensitivity === 2
|
||||
? "medium"
|
||||
: sensitiveMediaDetectionSensitivity === 3
|
||||
? "high"
|
||||
: sensitiveMediaDetectionSensitivity === 4
|
||||
? "veryHigh"
|
||||
: 0,
|
||||
setSensitiveFlagAutomatically,
|
||||
enableSensitiveMediaDetectionForVideos,
|
||||
enableIpLogging,
|
||||
enableActiveEmailValidation,
|
||||
}).then(() => {
|
||||
|
@ -54,19 +54,6 @@
|
||||
>
|
||||
<template #label>{{ i18n.ts.alwaysMarkSensitive }}</template>
|
||||
</FormSwitch>
|
||||
<FormSwitch
|
||||
v-model="autoSensitive"
|
||||
class="_formBlock"
|
||||
@update:modelValue="saveProfile()"
|
||||
>
|
||||
<template #label
|
||||
>{{ i18n.ts.enableAutoSensitive
|
||||
}}<span class="_beta">{{ i18n.ts.beta }}</span></template
|
||||
>
|
||||
<template #caption>{{
|
||||
i18n.ts.enableAutoSensitiveDescription
|
||||
}}</template>
|
||||
</FormSwitch>
|
||||
</FormSection>
|
||||
</div>
|
||||
</template>
|
||||
@ -93,7 +80,6 @@ const usage = ref<any>(null);
|
||||
const capacity = ref<any>(null);
|
||||
const uploadFolder = ref<any>(null);
|
||||
let alwaysMarkNsfw = $ref($i.alwaysMarkNsfw);
|
||||
let autoSensitive = $ref($i.autoSensitive);
|
||||
|
||||
const meterStyle = computed(() => {
|
||||
return {
|
||||
@ -140,8 +126,7 @@ function chooseUploadFolder() {
|
||||
|
||||
function saveProfile() {
|
||||
os.api("i/update", {
|
||||
alwaysMarkNsfw: !!alwaysMarkNsfw,
|
||||
autoSensitive: !!autoSensitive,
|
||||
alwaysMarkNsfw: !!alwaysMarkNsfw
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user